矩阵相乘
什么是矩阵?
在数学中,矩阵(Matrix)是指纵横排列的二维数据表格,最早来自于方程组的系数及常数所构成的方阵。这一概念由19世纪英国数学家凯利首先提出。
矩阵是高等代数学中的常见工具,也常见于统计分析等应用数学学科中。并且在ACM竞赛,有很多涉及到矩阵知识的题。许多算法都会结合矩阵来处理,而比较具有代表性的矩阵算法有:矩阵快速幂、高斯消元等等。
例如下面的图片就是一个矩阵:
上述矩阵是一个 4 × 3 矩阵:
某矩阵 A 的第 i 行第 j 列,或 I , j位,通常记为 A[i,j] 或 Aij。在上述例子中 A[3,3]=2。
此外 Aij = (aij),意为 A[i,j] = aij。
矩阵常用知识
① 矩阵相乘的规则:矩阵与矩阵相乘 第一个矩阵的列数必须等于第二个矩阵的行数 假如第一个是m*n的矩阵 第二个是n*p的矩阵 则结果就是m*p的矩阵 且得出来的矩阵中元素具有以下特点:
第一行第一列元素为第一个矩阵的第一行的每个元素和第二个矩阵的第一列的每个元素乘积的和 以此类推 第i行第j列的元素就是第一个矩阵的第i行的每个元素与第二个矩阵第j列的每个元素的乘积的和。
② 单位矩阵: n*n的矩阵 mat ( i , i )=1; 任何一个矩阵乘以单位矩阵就是它本身 n*单位矩阵=n, 可以把单位矩阵等价为整数1。(单位矩阵用在矩阵快速幂中)
例如下图就是一个7*7的单位矩阵:
矩阵相乘算法实现:
① 依据简单的矩阵相乘规则我们很容易写出代码:
1 2 3 4 |
for(int i=0;i<n;i++) for(int j=0;j<n;j++) for(int k=0;k<n;k++) t[i][j]=(t[i][j]+x[i][k]*y[k][j]); |
这个代码就是简单的计算,按照计算规则,依次算出结果矩阵的每一位元素,就是一个一个的计算出所有元素。这种思路比较简单好想,但是这种算法的复杂度是O(N3),而且不能进行优化,所以在平时在进行矩阵乘法时使用起来往往会超时。
② 我们再看一种操作方法:
一行一行的计算出所有元素:
1 2 3 4 |
for(int i=0;i<n;i++) for(int k=0;k<n;k++) for(int j=0;j<n;j++) t[i][j]=(t[i][j]+x[i][k]*y[k][j]); |
试想一下:什么情况下不用再进行相乘的操作?
其实就是一个数为0的时候,这时候即使相乘也不会改变原来位置上的值。
比如,如果此时X[i][k]的值为0,那你即使进行循环操作,t[i][j]值一样不会改变。所以,当x[i][j]值为0时,我们就没必要再进行相乘操作。这样的话就会优化一定的时间。而且这种优化对于一般算法要求的时间复杂度已经足够了。
优化后代码示例:
1 2 3 4 5 |
for(int i=0;i<n;i++) for(int k=0;k<n;k++) if(x[i][k]) //优化步骤 for(int j=0;j<n;j++) t[i][j]=(t[i][j]+x[i][k]*y[k][j]) |
③ 还有一种操作是一列一列的计算出所有元素:
这种情况下一样可以进行优化。
1 2 3 4 5 |
for(int j=0;j<n;j++) for(int k=0;k<n;k++) if(y[k][j]) //优化 for(int i=0;i<n;i++) t[i][j]=(t[i][j]+x[i][k]*y[k][j]); |
算法模板:
1 2 3 4 5 6 7 8 9 10 11 12 |
void Matmul(LLX[MAXN][MAXN],LL Y[MAXN][MAXN]) { LL t[MAXN][MAXN]={0}; for(int i=0;i<N;i++) for(int k=0;k<N;k++) if(X[i][k]) for(int j=0;j<N;j++) t[i][j]=(t[i][j]+X[i][k]*Y[k][j]); for(int i=0;i<N;i++) for(int j=0;j<N;j++) X[i][j]=t[i][j]; } |
例题:
来源:HDOJ 4920 Matrixmultiplication
Problem Description
Given two matrices A and B of size n×n, find the product of them.
bobo hates big integers. So you are only asked to find the result modulo 3.
Input
The input consists of several tests. For each tests:
The first line contains n (1≤n≤800). Each of the following n lines contain n integers — the description of the matrix A. The j-th integer in the i-th line equals Aij. The next n lines describe the matrix B in similar format (0≤Aij,Bij≤109).
Output
For each tests:
Print n lines. Each of them contain n integers — the matrix A×B in similar format.
Sample Input
1 0 1 2 0 1 2 3 4 5 6 7
Sample Output
0 0 1 2 1
题目大意:求两个矩阵相乘mod3的结果。
这道题就是一个赤裸裸的矩阵相乘的题。但是要注意题目的时间复杂度,一定要进行优化。并且,此题还有二个坑点,如果把握不好就会超时。
① 就是Mod 3 时,一定不能在矩阵相乘算法的for循环里,如果放在相乘算法里会超时。
②在进行相乘之前把所有元素先Mod 3 这样做也会优化一定的时间。
因为题目所给数据并不是很大,所以即使把mod 3 放到结束输出语句里面就可以了,不用担心相乘的过程会超出int型。
AC代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
#include <stdio.h> #include <string.h> #include <algorithm> using namespace std; const int MAXN = 810; const int Modn = 3; int N; int X[MAXN][MAXN]; int Y[MAXN][MAXN]; int t[MAXN][MAXN]; int main() { while(scanf("%d",&N)!=EOF){ for(int i=0;i<N;i++){ for(int j=0;j<N;j++){ scanf("%d",&X[i][j]); X[i][j]%=3; } } for(int i=0;i<N;i++){ for(int j=0;j<N;j++){ scanf("%d",&Y[i][j]); Y[i][j]%=3; t[i][j]=0; } } for(int i=0;i<N;i++){ for(int k=0;k<N;k++) if(X[i][k]) for(intj=0;j<N;j++) t[i][j]=(t[i][j]+X[i][k]*Y[k][j]%3)%3; } for(int i=0;i<N;i++){ printf("%d",t[i][0]); for(int j=1;j<N;j++){ printf("%d",t[i][j]); } printf("\n"); } } return 0; } |
单独考察矩阵相乘的题目并不多见,普遍都是结合矩阵快速幂,高斯消元等矩阵算法。进行这些算法之前我们必须要掌握矩阵相乘算法
我们需掌握简单的相乘规则,才能学习后面的一些矩阵算法。同时,为了以后学习算法以及做题的需要,我们也要记得矩阵相乘时的算法复杂度及优化细节。