6
\$\begingroup\$

This is a simple C++ code with a function mult to multiply matrices. This can easily be generalized for any \$n \times n\$ matrix by replacing 4 with any positive number greater than 1.

The multiplication is done by iterating over the rows, and iterating (nested in the rows iteration) over the columns. While inside the columns iteration, the multiplication is performed, which is a dot product (again using a new iteration).

The simple demo shows the multiplication of \$A \times A\$.

I wonder if there are better ways in writing this.

Code:

#include <iostream>
#include <string>

using namespace std;

int mult(int A[4][4], int B[4][4]){

int C[4][4], num;

for (int i=0; i<4; i++){
     for(int j=0; j<4; j++){
        num = 0;
        for(int k=0; k<4; k++){
          num += A[i][k]*B[k][j];
}
      C[i][j]=num; 
      cout << num << " ";
}
     cout << endl;
}

return 0;

}

int main(){

   int A[4][4], ind=0;
cout << "Default Matrix A: \n \n";

   for (int i=0; i<4; i++){
     for(int j=0; j<4; j++){
      A[i][j]=ind; ind++;
      cout<< A[i][j]<< " ";
}
     cout << endl;


}
    cout << "\nMultiplication of A^2: \n \n";
mult(A,A);
return 0;

}

Output:

Default Matrix A:

    0 1 2 3
    4 5 6 7
    8 9 10 11
    12 13 14 15

Multiplication of A^2:

56 62 68 74
152 174 196 218
248 286 324 362
344 398 452 506
\$\endgroup\$
  • 2
    \$\begingroup\$ I think that implementing good mathematical libraries takes a lot of effort, unless your goal is to learn about that, I think the best way to go is to use a external library such as Eigen. \$\endgroup\$ – WooWapDaBug Feb 5 '18 at 7:39
  • \$\begingroup\$ @WooWapDaBug thanks for the ref.. yes i prefer using creative algorithm. \$\endgroup\$ – Arief Anbiya Feb 5 '18 at 15:52
9
\$\begingroup\$

Avoid using namespace std

This can cause name collisions because it adds every name in the std namespace to the global namespace. For a small program like this one it's unlikely that you'll run into any problems (then again, maybe not) but it's best to get into the habit of using the std:: prefix on names in the std namespace.

Alternatively, you can introduce using declarations like using std::cout; to add specific names to the global namespace.

Avoid std::endl in favor of \n

std::endl flushes the stream, which can cause a loss in performance.

Declare variables in the most local scope possible

You declare num at the beginning of mult() but you don't actually use it (and initialize it to 0) until you're inside the j loop. It's better to simply declare and initialize it in the same place and in the place where you start to use it.

I would also rename it to be more descriptive (e.g. sum or dot_product).

Avoid hard-coded numbers

Your code works with \$4\times 4\$ matrices but you've got 4 hard-coded all over the place. You say

This can easily be generalized for any nxn matrix by replacing 4 with any positive number greater than 1.

But there are a lot of instances where 4 needs to be replaced. At the very minimum you should define 4 as a constant and use that constant in the code:

const std::size_t N = 4; // or constexpr instead of const if your compiler supports it

int mult(int A[N][N], int B[N][N]) {
    int C[N][N];

    for (int i = 0; i < N; i++) {
        for (int j = 0; j < N; j++) {
            int num = 0;
            for (int k = 0; k < N; k++) {
                num += A[i][k] * B[k][j];
            }
            C[i][j] = num;
            std::cout << num << " ";
        }
        std::cout << std::endl;
    }

    return 0;
}

Now I just need to change the definition of N once to use a number other than 4.

Use static_assert to enforce the condition that \$N > 1\$

If your compiler supports static_assert you can ensure that \$N > 1\$ at compile time and cause a compilation failure with a simple message to explain the problem.

Function template

The multiplication algorithm is basically the same for any \$N > 1\$, so this function is a good candidate for a function template based on the dimension \$N\$. For example, you have A with N = 4, but in the same program you could have a matrix (2D array) D with N = 3 and the same code is used to multiply with both matrices. Here's a demo using a function template:

#include <iostream>
#include <string>

template<std::size_t N>
int mult(int A[N][N], int B[N][N]) {
    static_assert(N > 1, "N must be greater than 1");

    int C[N][N];

    for (int i = 0; i < N; i++) {
        for (int j = 0; j < N; j++) {
            int num = 0;
            for (int k = 0; k < N; k++) {
                num += A[i][k] * B[k][j];
            }
            C[i][j] = num;
            std::cout << num << " ";
        }
        std::cout << std::endl;
    }

    return 0;
}

int main() {
    const std::size_t N = 4;
    int A[N][N];
    int ind = 0;
    std::cout << "Default Matrix A: \n \n";

    for (int i = 0; i < N; i++) {
        for (int j = 0; j < N; j++) {
            A[i][j] = ind; ind++;
            std::cout << A[i][j] << " ";
        }
        std::cout << std::endl;
    }
    std::cout << "\nMultiplication of A^2: \n \n";
    mult<N>(A, A);

    const std::size_t N2 = 3;
    int D[N2][N2];
    ind = 0;
    std::cout << "\nDefault Matrix D: \n \n";

    for (int i = 0; i < N2; i++) {
        for (int j = 0; j < N2; j++) {
            D[i][j] = ind; ind++;
            std::cout << D[i][j] << " ";
        }
        std::cout << std::endl;
    }
    std::cout << "\nMultiplication of D^2: \n \n";
    mult<N2>(D, D);
    return 0;
}

Consider a Matrix class instead of 2D arrays

Mathematical libraries implement matrices as a class. Internally, the Matrix class may use 2D arrays to store the data, but client code shouldn't depend on how that data is stored. A Matrix class can hide the implementation details from client code.

Extend to non-square matrices

In general, an \$N \times M\$ matrix \$A\$ can be multiplied with a matrix \$B\$ if \$B\$ is \$M \times P\$ (\$A\$ has \$M\$ columns and \$B\$ has \$M\$ rows, but otherwise the two matrices can have different dimensions). With a function template you can easily extend the multiplication function to support non-square matrices. Simply add the necessary template arguments and tweak the algorithm to use dimensions other than N. Here's a demo:

#include <iostream>
#include <string>

template<std::size_t N, std::size_t M, std::size_t P>
int mult(int A[N][M], int B[M][P]) {
    static_assert(N > 1, "N must be greater than 1");
    static_assert(M > 1, "M must be greater than 1");
    static_assert(P > 1, "P must be greater than 1");

    int C[N][P];

    for (int n = 0; n < N; n++) {
        for (int p = 0; p < P; p++) {
            int num = 0;
            for (int m = 0; m < M; m++) {
                num += A[n][m] * B[m][p];
            }
            C[n][p] = num;
            std::cout << num << " ";
        }
        std::cout << std::endl;
    }

    return 0;
}

int main() {
    int A[4][3]{
        {1, 2, 3},
        {4, 5, 6},
        {7, 8, 9},
        {10, 11, 12}
    };

    int B[3][2]{
        {1, 2},
        {3, 4},
        {5, 6}
    };

    mult<4, 3, 2>(A, B);
    return 0;
}

The demo program outputs the \$N \times P\$ (\$4 \times 2\$) matrix product:

22 28
49 64
76 100
103 136
\$\endgroup\$
  • \$\begingroup\$ Thanks. But currently, in CppDroid, none of your code succeed in compilation.. \$\endgroup\$ – Arief Anbiya Feb 5 '18 at 17:48
  • \$\begingroup\$ @Arief mult() should compile if you remove the static_assert lines. The rest is just demonstration. \$\endgroup\$ – Null Feb 5 '18 at 17:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.