[資料結構]稀疏矩陣乘法演算法實現
阿新 • • 發佈:2019-01-03
作者 | zhonglihao |
演算法名 | 稀疏矩陣乘法 Sparse Matrix Multiplication |
分類 | 資料結構 |
複雜度 | O(n^2) |
形式與資料結構 | C++程式碼 一維結構體儲存 |
特性 | 極簡封裝 不使用連結串列 不需要轉置 計算過程容易理解 |
具體參考出處 | 《演算法導論》(寫的不想看) |
備註 |
// ConsoleApplication1.cpp : 定義控制檯應用程式的入口點。 // #include "stdafx.h" #include "stdio.h" #include "stdlib.h" //稀疏矩陣儲存結構體 第一個元素為矩陣頭,包含行列長度,元素總個數 typedef struct { int row; int col; int element; }sparse_mat; void SparseMatrixRectPrint(sparse_mat* s_mat); void SparseMatrixTriPrint(sparse_mat* s_mat); sparse_mat* SparseMatrixMul(sparse_mat* s_mat_A, sparse_mat* s_mat_B); int _tmain(int argc, _TCHAR* argv[]) { int i, j, k; const int mat_A_row = 4; const int mat_A_col = 4; const int mat_B_row = 4; const int mat_B_col = 4; //原矩陣 int mat_A[mat_A_row][mat_A_col] = { 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0 }; int mat_B[mat_B_row][mat_B_col] = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 }; //計算有效元素數量 int mat_A_ele_count = 0; int mat_B_ele_count = 0; for (i = 0; i < mat_A_row; i++) { for (j = 0; j < mat_A_col; j++) { if (mat_A[i][j] != 0) mat_A_ele_count++; } } for (i = 0; i < mat_B_row; i++) { for (j = 0; j < mat_B_col; j++) { if (mat_B[i][j] != 0) mat_B_ele_count++; } } //動態分配 sparse_mat* sparse_m_A = (sparse_mat*)malloc((mat_A_ele_count + 1)*sizeof(sparse_mat)); sparse_mat* sparse_m_B = (sparse_mat*)malloc((mat_B_ele_count + 1)*sizeof(sparse_mat)); //存入稀疏矩陣資訊 sparse_m_A[0].row = mat_A_row; sparse_m_A[0].col = mat_A_col; sparse_m_A[0].element = mat_A_ele_count; sparse_m_B[0].row = mat_B_row; sparse_m_B[0].col = mat_B_col; sparse_m_B[0].element = mat_B_ele_count; for (i = 0, mat_A_ele_count = 0; i < mat_A_row; i++) { for (j = 0; j < mat_A_col; j++) { if (mat_A[i][j] != 0) { mat_A_ele_count++; sparse_m_A[mat_A_ele_count].element = mat_A[i][j]; sparse_m_A[mat_A_ele_count].row = i; sparse_m_A[mat_A_ele_count].col = j; } } } for (i = 0, mat_B_ele_count = 0; i < mat_B_row; i++) { for (j = 0; j < mat_B_col; j++) { if (mat_B[i][j] != 0) { mat_B_ele_count++; sparse_m_B[mat_B_ele_count].element = mat_B[i][j]; sparse_m_B[mat_B_ele_count].row = i; sparse_m_B[mat_B_ele_count].col = j; } } } //列印原陣列 SparseMatrixRectPrint(sparse_m_A); SparseMatrixRectPrint(sparse_m_B); //SparseMatrixTriPrint(sparse_m_A); //SparseMatrixTriPrint(sparse_m_B); //計算稀疏矩陣乘法 sparse_mat* sparse_m_C = (sparse_mat*)SparseMatrixMul(sparse_m_A, sparse_m_B); SparseMatrixRectPrint(sparse_m_C); system("Pause"); return 0; } //三元組稀疏矩陣乘法函式 極簡封裝 需要花費一點時間計算申請的記憶體 但是肯定比連結串列省空間啦 //Method Written By Zhonglihao sparse_mat* SparseMatrixMul(sparse_mat* s_mat_A, sparse_mat* s_mat_B) { int i, j, k; int s_mat_C_row = s_mat_A[0].row; int s_mat_C_col = s_mat_B[0].col; int s_mat_A_ele_count = s_mat_A[0].element; int s_mat_B_ele_count = s_mat_B[0].element; //判斷是否能夠相乘 或 有一個全為0 那就不用乘啦 if (s_mat_A[0].col != s_mat_B[0].row) return NULL; if (s_mat_A_ele_count == 0 || s_mat_B_ele_count == 0) { sparse_mat* s_mat_C = (sparse_mat*)malloc((1)*sizeof(sparse_mat)); s_mat_C[0].row = s_mat_C_row; s_mat_C[0].col = s_mat_C_col; s_mat_C[0].element = 0; return s_mat_C; } //申請一個長度為B列寬的快取 兩個用途 計算輸出大小時做列封禁,計算相乘時做和快取 int* col_buffer = (int*)malloc(s_mat_C_col*sizeof(int)); //清空快取區 for (k = 0; k < s_mat_C_col; k++) col_buffer[k] = 0; //判斷需要輸出的三元大小申請記憶體 int malloc_element_count = 0; for (i = 1; i <= s_mat_A_ele_count; i++) { if (i >= 2 && s_mat_A[i].row != s_mat_A[i - 1].row) //換行解禁 { for (k = 0; k < s_mat_C_col; k++) col_buffer[k] = 0; } for (j = 1; j <= s_mat_B_ele_count; j++) { if ((s_mat_A[i].col == s_mat_B[j].row) && col_buffer[s_mat_B[j].col] != 1)//沒有列封禁 { col_buffer[s_mat_B[j].col] = 1;//列封禁 malloc_element_count++; } } } sparse_mat* s_mat_C = (sparse_mat*)malloc((malloc_element_count + 1)*sizeof(sparse_mat)); s_mat_C[0].row = s_mat_C_row; s_mat_C[0].col = s_mat_C_col; s_mat_C[0].element = malloc_element_count; int s_mat_C_ele_count = 0;//用於存入元素時做指標 //開始進行乘法相乘 for (k = 0; k < s_mat_C_col; k++) col_buffer[k] = 0;//清理列快取 for (i = 1; i <= s_mat_A_ele_count; i++) { for (j = 1; j <= s_mat_B_ele_count; j++) { if (s_mat_A[i].col == s_mat_B[j].row)//有效用 壓入快取區 col_buffer[s_mat_B[j].col] += s_mat_A[i].element * s_mat_B[j].element; } //如果要換行或者是最後一行 if (((i != s_mat_A_ele_count) && (s_mat_A[i].row != s_mat_A[i + 1].row)) || i == s_mat_A_ele_count) { //掃描快取組 for (k = 0; k < s_mat_C_col; k++) { //如果該點不是0 壓入三元組 清零快取 if (col_buffer[k] != 0) { s_mat_C_ele_count++; s_mat_C[s_mat_C_ele_count].row = s_mat_A[i].row; s_mat_C[s_mat_C_ele_count].col = k; s_mat_C[s_mat_C_ele_count].element = col_buffer[k]; col_buffer[k] = 0; } } } } //釋放快取 返回結果 free(col_buffer); return s_mat_C; } //稀疏矩陣列印 按矩形列印 需要確定三元組按Z排列有序 void SparseMatrixRectPrint(sparse_mat* s_mat) { //獲取行列資訊 int i, j; int row = s_mat[0].row; int col = s_mat[0].col; //列印元素遞增 前提是三元組按照行列順序排好,就只需要遞增下標 int ele_count = 1; //按矩陣掃描列印 for (i = 0; i < row; i++) { for (j = 0; j < col; j++) { if (i == s_mat[ele_count].row && j == s_mat[ele_count].col) { printf("%d\t", s_mat[ele_count].element); ele_count++; } else { printf("0\t"); } }//for printf("\n"); }//for //跳空換行 返回 printf("\n"); return; } //稀疏矩陣列印 按三元組結構列印 void SparseMatrixTriPrint(sparse_mat* s_mat) { int i, j; int ele_count = s_mat[0].element; //按順序列印 for (i = 1; i <= ele_count; i++) { printf("%d\t%d\t%d\n", s_mat[i].row, s_mat[i].col, s_mat[i].element); } //跳空換行 返回 printf("\n"); return; }