矩陣相乘
什麼是矩陣?
在數學中,矩陣(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; } |
單獨考察矩陣相乘的題目並不多見,普遍都是結合矩陣快速冪,高斯消元等矩陣演算法。進行這些演算法之前我們必須要掌握矩陣相乘演算法
我們需掌握簡單的相乘規則,才能學習後面的一些矩陣演算法。同時,為了以後學習演算法以及做題的需要,我們也要記得矩陣相乘時的演算法複雜度及優化細節。