矩陣乘法(行邏輯連結的順序表)及程式碼實現
阿新 • • 發佈:2018-12-23
矩陣相乘的前提條件是:乘號前的矩陣的列數要和乘號後的矩陣的行數相等。且矩陣的乘法運算沒有交換律,即 A*B 和 B*A 是不一樣的。
例如,矩陣A:
矩陣B:
由於矩陣 A 的列數和矩陣 B 的行數相等,可以進行 A*B 運算(不能進行 B*A 運算)。計算方法是:用矩陣A的第 i 行和矩陣B中的每一列 j 對應的數值做乘法運算,乘積一一相加,所得結果即為矩陣 C 中第 i 行第 j 列的值。
得到的乘積矩陣C為:
例如:C12 = 6 是因為:A11*B12 + A12*B22 + A13*B32 + A14*B42,即 3*2 + 0*0 + 0*4 + 5*0 = 6 ,因為這是 A 的第 1 行和 B 的第 2 列的乘積和,所以結果放在 C 的第 1 行第 2 列的位置。
例如,A是 m1*n1 矩陣,B是 m2*n2 矩陣(前提必須是 n1 == m2 ):
普通演算法的時間複雜度為
在稀疏矩陣做乘法運算時,由於本身矩陣中含有的非 0 元素少,普通演算法會出現很多 0*0 或者 k*0 或者 0*k ( k 代表非 0 元素值)的情況。下面介紹使用行邏輯連結的順序表計算矩陣乘積的方法。
解決方案:由於使用的是行邏輯連結的順序表,所以,已經知道了每一個矩陣中的每一行有多少個非0元素,而且第一行的第一個非0元素的位置一定是1。
所以,第 n 行的非0元素的位置範圍是:大於或等於第 n 行第一個元素的位置, 小於第 n+1 行第一個元素的位置(如果是矩陣的最後一行, 小於矩陣中非 0 元素的個數 + 1)。
輸出結果: (1,2,6)
(2,1,-1)
(3,2,4)
例如,矩陣A:
矩陣B:
由於矩陣 A 的列數和矩陣 B 的行數相等,可以進行 A*B 運算(不能進行 B*A 運算)。計算方法是:用矩陣A的第 i 行和矩陣B中的每一列 j 對應的數值做乘法運算,乘積一一相加,所得結果即為矩陣 C 中第 i 行第 j 列的值。
得到的乘積矩陣C為:
例如:C12 = 6 是因為:A11*B12 + A12*B22 + A13*B32 + A14*B42,即 3*2 + 0*0 + 0*4 + 5*0 = 6 ,因為這是 A 的第 1 行和 B 的第 2 列的乘積和,所以結果放在 C 的第 1 行第 2 列的位置。
例如,A是 m1*n1 矩陣,B是 m2*n2 矩陣(前提必須是 n1 == m2 ):
int C[MAX][MAX]; for (int i=0; i<m1;i++) { for (int j=0; j<n2; j++) { C[i][j]=0; for (int k=0; k<n1; k++) { C[i][j]+=A[i][k]*B[k][j]; } } }
O(m1*n2*n1)
。在稀疏矩陣做乘法運算時,由於本身矩陣中含有的非 0 元素少,普通演算法會出現很多 0*0 或者 k*0 或者 0*k ( k 代表非 0 元素值)的情況。下面介紹使用行邏輯連結的順序表計算矩陣乘積的方法。
行邏輯連結的順序表解決矩陣乘積演算法
對矩陣的乘積進行深度剖析,矩陣 A 和矩陣 B 相乘的運算過程是這樣的:- 首先,找到矩陣 A 中第一行的非 0 元素,分別是 A11 = 3和 A14 = 5;(由於行邏輯連結的順序表中儲存的都是非 0 元素,查詢的過程就需要使用記錄每行第一個非 0 元素的首地址的陣列來完成)
-
用 3 去和 B 中對應的第一行中的非 0 元素相乘,矩陣 B 中第一行非 0 元素是 B12
- 以此類推。
攻克問題難點
現在,解決問題的關鍵在於,如何知道順序表中存放的非0元素是哪一行的呢?解決方案:由於使用的是行邏輯連結的順序表,所以,已經知道了每一個矩陣中的每一行有多少個非0元素,而且第一行的第一個非0元素的位置一定是1。
所以,第 n 行的非0元素的位置範圍是:大於或等於第 n 行第一個元素的位置, 小於第 n+1 行第一個元素的位置(如果是矩陣的最後一行, 小於矩陣中非 0 元素的個數 + 1)。
具體實現程式碼
#include <stdio.h> #define MAXSIZE 12500 #define MAXRC 100 #define ElemType int typedef struct { int i,j;//行,列 ElemType e;//元素值 }Triple; typedef struct { Triple data[MAXSIZE+1]; int rpos[MAXRC+1];//每行第一個非零元素在data陣列中的位置 int mu,nu,tu;//行數,列數,元素個數 }RLSMatrix; RLSMatrix MultSMatrix(RLSMatrix A, RLSMatrix B, RLSMatrix C) { //如果矩陣A的列數與矩陣B的行數不等,則不能做矩陣乘運算 if(A.nu != B.mu) return C; C.mu = A.mu; C.nu = B.nu; C.tu = 0; //如果其中任意矩陣的元素個數為零,做乘法元素沒有意義,全是0 if(A.tu * B.tu == 0) return C; else { int arow; int ccol; //遍歷矩陣A的每一行 for(arow=1; arow<=A.mu; arow++) { //建立一個臨時儲存乘積結果的陣列,且初始化為0,遍歷每次都需要清空 int ctemp[MAXRC+1] ={}; C.rpos[arow] = C.tu + 1; //根據行數,在三元組表中找到該行所有的非0元素的位置 int tp; if(arow < A.mu) tp = A.rpos[arow+1];//獲取矩陣A的下一行第一個非零元素在data陣列中位置 else tp = A.tu+1;//若當前行是最後一行,則取最後一個元素+1 int p; int brow; //遍歷當前行的所有的非0元素 for(p=A.rpos[arow]; p<tp; p++) { brow = A.data[p].j;//取該非0元素的列數,便於去B中找對應的做乘積的非0元素 int t; // 判斷如果對於A中非0元素,找到矩陣B中做乘法的那一行中的所有的非0元素 if(brow < B.mu) t = B.rpos[brow+1]; else t = B.tu+1; int q; //遍歷找到的對應的非0元素,開始做乘積運算 for(q=B.rpos[brow]; q<t; q++) { //得到的乘積結果,每次和ctemp陣列中相應位置的數值做加和運算 ccol = B.data[q].j; ctemp[ccol] += A.data[p].e * B.data[q].e; } } //矩陣C的行數等於矩陣A的行數,列數等於矩陣B的列數,所以,得到的ctemp儲存的結果,也會在C的列數的範圍內 for(ccol=1; ccol<=C.nu; ccol++) { //由於結果可以是0,而0不需要儲存,所以在這裡需要判斷 if(ctemp[ccol]) { //不為0,則記錄矩陣中非0元素的個數的變數tu要+1;且該值不能超過存放三元素陣列的空間大小 if(++C.tu > MAXSIZE) return C; else{ C.data[C.tu].e = ctemp[ccol]; C.data[C.tu].i = arow; C.data[C.tu].j = ccol; } } } } return C; } } int main(int argc, char* argv[]) { RLSMatrix M,N,T; M.tu = 4; M.mu = 3; M.nu = 4; M.rpos[1] = 1; M.rpos[2] = 3; M.rpos[3] = 4; M.data[1].e = 3; M.data[1].i = 1; M.data[1].j = 1; M.data[2].e = 5; M.data[2].i = 1; M.data[2].j = 4; M.data[3].e = -1; M.data[3].i = 2; M.data[3].j = 2; M.data[4].e = 2; M.data[4].i = 3; M.data[4].j = 1; N.tu = 4; N.mu = 4; N.nu = 2; N.rpos[1] = 1; N.rpos[2] = 2; N.rpos[3] = 3; N.rpos[4] = 5; N.data[1].e = 2; N.data[1].i = 1; N.data[1].j = 2; N.data[2].e = 1; N.data[2].i = 2; N.data[2].j = 1; N.data[3].e = -2; N.data[3].i = 3; N.data[3].j = 1; N.data[4].e = 4; N.data[4].i = 3; N.data[4].j = 2; T= MultSMatrix(M,N,T); for (int i=1; i<=T.tu; i++) { printf("(%d,%d,%d)\n",T.data[i].i,T.data[i].j,T.data[i].e); } return 0; }
輸出結果: (1,2,6)
(2,1,-1)
(3,2,4)
總結
當稀疏矩陣 Amn 和稀疏矩陣 Bnp 採用行邏輯連結的順序表做乘法運算時,在矩陣 A 的列數(矩陣 B 的行數) n 不是很大的情況下,演算法的時間複雜度相當於O(m*p)
,比普通演算法要快很多。