实矩阵相乘

实矩阵相乘

矩阵相乘

让我们先来了解矩阵是怎么相乘的:

矩阵 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=0n1aitbtj,i=0,1,...,m1;j=0,1,...,k1
如此归纳之后在运用计算机程序将矩阵 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=12033183254207144251
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=427095283816156
运行程序如下:

#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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值