实矩阵相乘
矩阵相乘
让我们先来了解矩阵是怎么相乘的:
矩阵
B
B
B与矩阵
A
A
A 相乘得矩阵
C
C
C:
C
=
A
×
B
C = A \times B
C=A×B
(
c
0
c
1
c
2
c
3
)
=
(
a
0
a
1
a
2
a
3
)
×
(
b
0
b
1
b
2
b
3
)
\begin {pmatrix} c_{0} & c_{1}\\ c_{2} & c_{3} \end {pmatrix} = \begin {pmatrix} a_{0} & a_{1}\\ a_{2} & a_{3} \end {pmatrix} \times \begin {pmatrix} b_{0} & b_{1}\\ b_{2} & b_{3} \end {pmatrix}
(c0c2c1c3)=(a0a2a1a3)×(b0b2b1b3)
其中有:
c
0
=
a
0
×
b
0
+
a
1
×
b
2
c
1
=
a
0
×
b
1
+
a
1
×
b
3
c
2
=
a
2
×
b
0
+
a
3
×
b
2
c
3
=
a
2
×
b
1
+
a
3
×
b
3
c_0 = a_0 \times b_0 + a_1 \times b_2\\ c_1 = a_0 \times b_1 + a_1 \times b_3\\\\ c_2 = a_2 \times b_0 + a_3 \times b_2\\ c_3 = a_2 \times b_1 + a_3 \times b_3
c0=a0×b0+a1×b2c1=a0×b1+a1×b3c2=a2×b0+a3×b2c3=a2×b1+a3×b3
其次,两个矩阵若要想相乘,需要满足:第一个矩阵的列数等于第二个矩阵的行数,如:
A
m
×
n
×
B
n
×
k
=
C
m
×
k
A_{m \times n} \times B_{n \times k} = C_{m \times k}
Am×n×Bn×k=Cm×k
好,复习完以上内容,我们就来看看如何编写程序来计算两个实矩阵相乘吧:
若要求 m × n m \times n m×n阶矩阵 A A A与 n × k n \times k n×k阶矩阵 B B B乘积矩阵 C = A B C=AB C=AB
我们就有矩阵
C
C
C各个元素的式子:
c
i
j
=
∑
t
=
0
n
−
1
a
i
t
b
t
j
,
i
=
0
,
1
,
.
.
.
,
m
−
1
;
j
=
0
,
1
,
.
.
.
,
k
−
1
c_{ij} = \sum_{t = 0}^{n-1} a_{it}b_{tj}, i = 0, 1, ..., m-1;j = 0, 1, ..., k-1
cij=t=0∑n−1aitbtj,i=0,1,...,m−1;j=0,1,...,k−1
如此归纳之后在运用计算机程序将矩阵
C
C
C计算出来便显得简单多了
我们将接口设为:void trmul(double* a, double* b, int m, int n, int k, double* c)
接口与参数说明:
形参与函数类型 | 参数意义 |
---|---|
double a[m][n] | 存放矩阵 A A A的元素 |
double b[n][k] | 存放矩阵 B B B的元素 |
int m | 矩阵 A A A 与乘积矩阵 C C C的行数 |
int n | 矩阵 A A A 的列数与矩阵 B B B的行数 |
k | 矩阵 B B B 与乘积矩阵 C C C的列数 |
double c[m][k] | 返回乘积矩阵 C = A B C=AB C=AB的元素 |
根据以上归纳的公式与接口参数说明,我们很容易就能写出计算两个实矩阵相乘的程序:
void turml(double* a, double* b, int m, int n, int k, double* c) {
for(int i = 0; i < m; ++i) {
for(int j = 0; j < k; ++j) {
int u = i*k+j;
c[u] = 0.0; // 等价于 c[i][j] = 0.0;
for(int t = 0; t < n; ++t) {
c[u] += a[i*n+t] * b[t*k+j]; // 等价于c[i][j] = a[i][t]*b[t][j];
}
}
}
}
我们来测试一下这个程序:
求下列矩阵 A A A与 B B B的乘积矩阵 C = A B C=AB C=AB:
其中
A
A
A 和
B
B
B为以下值:
A
=
(
1
3
−
2
0
4
−
2
−
1
5
−
7
2
0
8
4
1
−
5
3
−
3
2
−
4
1
)
A = \begin {pmatrix} 1 & 3 & -2 & 0 & 4\\ -2 & -1 & 5 & -7 & 2\\ 0 & 8 & 4 & 1 & -5\\ 3 & -3 & 2 & -4 & 1 \end {pmatrix}
A=⎝⎜⎜⎛1−2033−18−3−25420−71−442−51⎠⎟⎟⎞
B
=
(
4
5
−
1
2
−
2
6
7
8
1
0
3
−
5
9
8
−
6
)
B = \begin {pmatrix} 4 & 5 & -1\\ 2 & -2 & 6\\ 7 & 8 & 1\\ 0 & 3 & -5\\ 9 & 8 & -6 \end {pmatrix}\\
B=⎝⎜⎜⎜⎜⎛427095−2838−161−5−6⎠⎟⎟⎟⎟⎞
运行程序如下:
#include "turml.hpp"
int main() {
double a[4][5] = {{1.0, 3.0, -2.0, 0.0, 4.0},
{-2.0, -1.0, 5.0, -7.0, 2.0},
{0.0, 8.0, 4.0, 1.0, -5.0},
{3.0, -3.0, 2.0, -4.0, 1.0}};
double b[5][3] = {{4.0, 5.0, -1.0},
{2.0, -2.0, 6.0},
{7.0, 8.0, 1.0},
{0.0, 3.0, -5.0},
{9.0, 8.0, -6.0}};
double c[4][3];
turml((double*)a, (double*)b, 4, 5, 3, (double*)c);
for(int i = 0; i != 4; ++i) {
for(int j = 0; j != 3; ++j) {
std::cout << c[i][j] << " ";
}
std::cout << std::endl;
}
return 0;
}
输出结果如下所示:
32 15 -9
43 27 24
-1 -21 77
29 33 -5
当然,因为C++
对于二维数组的传参有很严格的限定,因此可以将普通传参转化为模板参数与普通参数混合的方法,以简化传参过程:
template <int M, int N, int K>
void turml_tem(double a[M][N], double b[N][K], double c[M][K]) {
for(int i = 0; i < M; ++i) {
for(int j = 0; j < K; ++j) {
c[i][j] = 0.0;
for(int t = 0; t < N; ++t) {
c[i][j] += a[i][t] * b[t][j];
}
}
}
}
调用过程如下所示:
#include "turml.hpp"
int main() {
double a[4][5] = {{1.0, 3.0, -2.0, 0.0, 4.0},
{-2.0, -1.0, 5.0, -7.0, 2.0},
{0.0, 8.0, 4.0, 1.0, -5.0},
{3.0, -3.0, 2.0, -4.0, 1.0}};
double b[5][3] = {{4.0, 5.0, -1.0},
{2.0, -2.0, 6.0},
{7.0, 8.0, 1.0},
{0.0, 3.0, -5.0},
{9.0, 8.0, -6.0}};
double c[4][3];
turml_tem<4, 5, 3>(a, b, c);
for(int i = 0; i != 4; ++i) {
for(int j = 0; j != 3; ++j) {
std::cout << c[i][j] << " ";
}
std::cout << std::endl;
}
return 0;
}