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