Multiplying two matrix in a different way - can't figure it out how to do it

Viewed 163

As a homework, I have a problem which sounds like this:

We have a n*n square matrix. It is called 'subdiagonal'
if all the elements above the main diagonal are null.

a) Copy the useful elements (the ones which are not null, so basically all the elements
 from the main diagonal and below) to an array. (I've done that)

b) Write an algorithm which takes two subdiagonal matrix A, B as an input.
 Those are transformed into arrays V_a and V_b with the algorithm from a),
 then they calculate C = A*B only using only V_a and V_b

e.g.

Let's say A =
1 0 0 0 0
2 3 0 0 0
4 1 3 0 0
1 9 0 2 0
1 0 1 2 2
B =
2 0 0 0 0
1 1 0 0 0
0 1 2 0 0
1 1 2 3 0
2 0 0 1 2

after this input, V_a = 1,2,3,4,1,3,1,9,0,2,1,0,1,2,2; V_b = 2,1,1,0,1,2,1,1,2,3,2,0,0,1,2

and the product V_c will be 2,7,3,9,4,6,13,11,4,6,8,3,6,8,4

so the matrix will look like 
2   0  0  0  0
7   3  0  0  0
9   4  6  0  0
13  11  4  6  0
8   3  6  8  4

Here's the code that I've been working on for a while:

#include <iostream>
#include <algorithm>

void read(int& a, int**& matrix)
{
    std::cin >> a;
    matrix = new int*[a];
    for (int i = 0; i < a; i++)
    {
        for (int j = 0; j < a; j++)
        {
            matrix[i] = new int[a];
        }
    }
    for (int i = 0; i < a; i++)
    {
        for (int j = 0; j < a; j++)
        {
            std::cin >> matrix[i][j];
        }
    }
}

void showMatrix(int a, int** matrix)
{
    for (int i = 0; i < a; i++)
    {
        for (int j = 0; j < a; j++)
        {
            std::cout << matrix[i][j] << " ";
        }
        std::cout << std::endl;
    }
}

void showArray(int a, int* array)
{
    for (int i = 0; i < a; i++)
    {
        std::cout << array[i] << " ";
    }
}

void createArray(int a, int& arrayLength, int** matrix, int*& array)
{
    int nrDeElemente = a*a - (a * (a - 1)) / 2;
    array = new int[nrDeElemente+1];
    arrayLength = 0;
    for (int i = 0; i < a; i++)
    {
        for (int j = 0; j < i+1; j++)
        {
            array[arrayLength++] = matrix[i][j];
        }
    }
}

int* multiplyArrays(int a, int arrayLength, int* array1, int* array2)
{
    int* array3 = new int[arrayLength + 1];
    for (int i = 0; i < a; i++)
    {
        array3[i] = 0;
    }
    int t = 1;
    for (int i = 0; i < arrayLength; ++i)
    {
        for (int j = 0; j < t; ++j)
        {
            for (int p = j; p < a; p++)
            {
                array3[i] += array1[j] * array2[p];
            }
        }
        ++t;
    }
    return array3;
}

int main()
{
    int **matrix1, **matrix2;
    int *array1, *array2, *multiplyResult;
    int a, arrayLength;
    read(a, matrix1);
    read(a, matrix2);
    createArray(a, arrayLength, matrix1, array1);
    createArray(a, arrayLength, matrix2, array2);
    multiplyResult = multiplyArrays(a, arrayLength, array1, array2);
    showArray(arrayLength, multiplyResult);
}

I've done a), but I don't know how to do b)

I think I understood it (after many hours of trials) conceptually, but I don't really know how to implement it.

I need 3 for loops, as such:

->the most outer one has to be responsible for the position we calculate on the new array

->the next one has to choose which elements from the second array will be multiplied. (choose the multiplier) That's one of the loops I don't know how to implement. It somehow has to stop when the line (from the matrix) ended and start where it stopped + 1 element.

->the most inner one has to choose the second term of the multiplication (the multiplicand). I also don't know how I should implement this one. It should choose as many elements as there multipliers are and also, the looping is quite strange (because I need to select all the elements from the same row every time).

Can anybody help me solve point b and also explain their thinking?

I struggled a lot and I really feel like I need help.

BTW the 3 for loops from multiplyArrays make no sense, you can just write something else instead of them. Those 3 for loops are basically the only things that my program needs (I think).

Thanks :)

2 Answers

Matrix multiplication C = A*B is defined by C(i,j) = sum_k A(i,k)*B(k,j). The matrix A has structural nonzeros where i >= k, and B, where k >= j. Thus it suffices to iterate

  • (outer loop) i from 0 to n-1
  • (middle) j from 0 to i
  • (inner) k from j to i.

The other piece is turning coordinates (i,j) into an offset with respect to the 1D storage format. The number of structural nonzeros in the first i rows is given by the ith triangular number, (i+1)*i/2. Hence the jth element in this row is at (zero-based) index (i+1)*i/2 + j. You or your compiler can strength-reduce the multiplication.

To multiply matrices requires to find where a row start in array, for example row[2] starts at index 3 in array as highlighted below,

1 0 0 0 0
2 3 0 0 0
4 1 3 0 0 => row[2]
1 9 0 2 0
1 0 1 2 2

[1, 2, 3, 4, 1, 3, 1, 9, 0, 2, 1, 0, 1, 2, 2]

Any row can be found if we know how may elements are present before it, like in above example if we know that three elements are present before row[2] then we can locate row[2] easily.

To find number of elements presents before each row requires to calculated an auxiliary array of size equals to number of rows, but to do that let's first see matrix again,
As you can see each row contains element equal to the index + 1 of row,

1          element count = index + 1 = 0 + 1 = 1
2 3                                  = 1 + 1 = 2
4 1 3                                = 2 + 1 = 3
1 9 0 2                              ..
1 0 1 2 2                            ..

It means our auxiliary array would be,
auxiliary array = [0, 1, 2, 3, 4] but how ?

As we know there are no element before row[0] that's why auxiliaryArray[0] = 0 then elements before row[1] is only one element which can be found by index of previous row that is previous row index + 1 => 0 + 1 as showed above auxiliaryArray[1] = 1 and similar for all rows,
But it is not done! current state of auxiliary array is only having information about number of elements present in immediate previous row but not in all previous rows, and to do so we have to calculate sum of all previous rows and that is called partial sum and it will be done as follows,

row[0] = row[0]
row[1] = row[0] + row[1]
row[2] = row[1] + row[2]
..
..

and final result,
auxiliary array = [0, 1, 3, 6, 10]

So as you can see number of elements before row[2] = auxiliaryArray[2] = 3
By using above auxiliary array you can locate any row and if you get first element of row you can find all col elements.
Next point to understand is how many elements you have to multiply in each row and that is again number of elements to multiply = index + 1 as you see above in matrix row[0] only have one element to multiple index + 1 => 0 + 1 and same rule apply for each row.
Last point to consider is, when row is multiplied with col of other matrix it doesn't start always with row[0] of other matrix as you can see below otherMatrix[0][1] is outside of left diagonal of other matrix,

2 0 0 0 0
1 1 0 0 0
0 1 2 0 0
1 1 2 3 0
2 0 0 1 2

Finally we are done!

#include <iostream>

#include <vector>

#include <numeric>
#include <iterator>

using std::cout;

void printMatrixArray(std::size_t rowSize, const std::vector<int>& matArray){

    std::size_t elementCount = 1;
    std::vector<int>::const_iterator it = matArray.cbegin();

    for(std::size_t row = 0; row < rowSize; ++row){

        std::copy(it, it + elementCount, std::ostream_iterator<int>(cout, "\t"));
        cout<< '\n';

        it += elementCount;
        ++elementCount;
    }
}

std::vector<int> leftDiagonalBottomMatrix(const std::vector<std::vector<int>>& mat){

    std::vector<int> res;
    res.reserve(((1 + mat.size()) * mat.size()) / 2);

    std::vector<int>::size_type elementCount = 1;

    for(const std::vector<int>& row : mat){

        for(std::vector<int>::const_iterator it = row.cbegin(), endIt = row.cbegin() + elementCount; endIt != it; ++it){

            res.push_back(*it);
        }

        ++elementCount;
    }

    return res;
}

std::vector<int> multiplyMatrixArrays(const std::vector<int>& mat1Arr, const std::vector<int>& mat2Arr,
                                     std::vector<int>::size_type rowSize){

    std::vector<int> auxiliaryArray(rowSize);
    auxiliaryArray.front() = 0;

    std::iota(auxiliaryArray.begin() + 1, auxiliaryArray.end(), 1);

    std::partial_sum(auxiliaryArray.cbegin(), auxiliaryArray.cend(), auxiliaryArray.begin());

    std::vector<int> res;
    res.reserve(mat1Arr.size());

    for(std::vector<int>::size_type row = 0; row < rowSize; ++row){

        for(std::vector<int>::size_type col = 0; col <= row; ++col){

            int val = 0;

            for(std::vector<int>::size_type ele = col, elementCount = row + 1; ele < elementCount; ++ele){

                val += mat1Arr[auxiliaryArray[row] + ele] * mat2Arr[auxiliaryArray[ele] + col];
            }

            res.push_back(val);
        }
    }

    return  res;
}

std::vector<int> matrixMultiply(const std::vector<std::vector<int>>& mat1, const std::vector<std::vector<int>>& mat2){

    return multiplyMatrixArrays(leftDiagonalBottomMatrix(mat1), leftDiagonalBottomMatrix(mat2), mat1.size());
}


int main(){

    std::vector<std::vector<int>> mat1{{1, 0, 0, 0, 0}, {2, 3, 0, 0, 0}, {4, 1, 3, 0, 0}, {1, 9, 0, 2, 0},
                                       {1, 0, 1, 2, 2}};

    std::vector<std::vector<int>> mat2{{2, 0, 0, 0, 0}, {1, 1, 0, 0, 0}, {0, 1, 2, 0, 0}, {1, 1, 2, 3, 0},
                                       {2, 0, 0, 1, 2}};

    printMatrixArray(mat1.size(), matrixMultiply(mat1, mat2));
}

Output:

2   
7   3   
9   4   6   
13  11  4   6   
8   3   6   8   4

Output does not print elements above the left diagonal of matrix!

Related