1. 程式人生 > >稀疏矩陣(三元組表示)基本操作 實驗報告 (轉置,加法,乘法)

稀疏矩陣(三元組表示)基本操作 實驗報告 (轉置,加法,乘法)

一、        實驗內容

稀疏矩陣的壓縮儲存結構,以及稀疏矩陣的三元組表表示方法下的轉置、相加、相乘等演算法

二、        實驗目的

1.      熟悉陣列、矩陣的定義和基本操作

2.      熟悉稀疏矩陣的儲存方式和基本運算

3.      理解稀疏矩陣的三元組表型別定義,掌握稀疏矩陣的輸入、輸出和轉置演算法

三、        實驗原理

1.      使用三元組儲存矩陣中的非零元素(三元組分別儲存非零元素的行下標,列下標和元素值)。除了三元組表本身,儲存一個稀疏矩陣還需要額外的三個變數,分別儲存矩陣的非零元個數,矩陣的行數和矩陣的列數。

 

2.      稀疏矩陣的建立演算法:

第一步:根據矩陣建立一個二維陣列,表示原始矩陣

第二步:取出二維陣列中的元素(從第一個元素開始取),判斷取出元素是否為非零元素,如果為非零元素,把該非零元素的數值以及行下標和列下表儲存到三元陣列表裡,否則取出下一個元素,重複該步驟。

第三步:重複第二步,知道二維陣列中所有的元素已經取出。

 

3.      稀疏矩陣倒置演算法:

第一步:判斷進行倒置的矩陣是否為空矩陣,如果是,則直接返回錯誤資訊。

第二步:計算要倒置的矩陣每列非零元素的數量,存入到num陣列(其中num[i] 代表矩陣中第i列非零元素的個數)。以及倒置後矩陣每行首非零元的位置,存入cpot陣列中(其中cpot表示倒置後矩陣每行非零元的位置,對應表示原矩陣每列中第一個非零元的位置)。

第三步:確定倒置後矩陣的行數和列數。

第四步:取出表示要導致矩陣中三元組表元素 {e, I, j}(第一次取出第一個,依次取出下一個元素),從第二步

cpot陣列中確定該元素倒置後存放的位置(cpot[j]),把該元素的行下標和列下標倒置以後放入新表的指定位置中。cpot[j] 變數加一。

第五步:重複第四步,直到三元組表中所有的元素都完成倒置。

第六步:把完成倒置運算的三元組表輸出。

 

4.      稀疏矩陣加法演算法:

第一步:檢查相加兩個矩陣的行數和列數是否相同,如果相同,則進入第二步,否則輸出錯誤資訊。

第二步:定義變數ij,用於控制三元組表的遍歷。

第三步:比較變數矩陣M中第i個元素和矩陣N中第j個元素,如果兩個元素是同一行元素,如果不是則進入第四步,如果是,再繼續比較兩個元素是否為同一列元素,如果是,把兩個元素值相加,放到三元組表中;否則把列下表小的元素依次放到三元組表中。進入第五步

第四步:如果矩陣M中第i個元素的行下標大於矩陣N中第j個元素的行下標,則把矩陣N中第j個元素所在行的所有非零元素新增到三元組表中;如果矩陣M中第i個元素的行下標小於矩陣N中第j個元素的下標,則把M中第i個元素所在行的所有非零元素依次新增到三元組表中。

第五步:重複第三步,直到矩陣M和矩陣N中所有元素都非零元素新增到三元組表中。

第六步:輸出運算結果

 

5.      稀疏矩陣乘法演算法:

第一步:檢查矩陣M和矩陣N能否參與乘法運算(即矩陣M的列數等於矩陣N的行數),如果兩個矩陣可以參與乘法運算,進入下一步,否則輸出錯誤資訊

第二步:檢查兩個矩陣相乘以後是否為零矩陣,如果相乘結果是零矩陣,直接返回一個零矩陣。

第三步:分別計算矩陣M和矩陣N中每行非零元的個數(分別存放到num_mnum_n陣列中),並計算出每行首非零元的位置(分別存放到cpot_mcpot_n中)。

第四步:依次取矩陣M中的非零元(第一次取出矩陣M中的第一個非零元),求出該非零元所在行和所在列乘積的和,然後把值放到結果三元組表的特定位置。

第五步:重複第四步,直到矩陣M中所有非零元都已經參與運算。

第六步:輸出結

 

 

 

六、        操作說明

1.      在建立稀疏矩陣的時候,可以每次輸入一個數據,也可以一次輸入多個數據,程式會自動根據輸入元素的個數對矩陣資料進行填充

2.      每次矩陣運算失敗時(無論是輸入的矩陣不符合矩陣運算的條件,參與運算其中一個矩陣為空矩陣,或者分配不到臨時空間),程式都會返回到主選單。輸入的資料都會被清空。

 

 

七、        附錄:程式碼

  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <windows.h>
  4 
  5 #define MAXSIZE 1000
  6 #define OK 0
  7 
  8 #define MALLOC_FAIL -1            // 表示分配空間時發生錯誤
  9 #define EMPTY_MATRIX -2            // 表示正嘗試對一個空矩陣進行運算操作
 10 #define MATRIX_NOT_MATCH -3        // 表示嘗試對不符合運算條件的矩陣進行運算操作(例如非相同行數列數矩陣相加)
 11 
 12 /* ----------- 結構體宣告部分 ---------------- */
 13 typedef struct
 14 {
 15     int row;    // 非零元的行下標
 16     int col;    // 非零元的列下標
 17     int e;        // 非零元的值
 18 } Triple;
 19 
 20 typedef struct
 21 {
 22     Triple *data;                // 非零元素的元素表
 23     int rownum;                    // 矩陣的行數
 24     int colnum;                    // 矩陣的列數
 25     int num;                    // 矩陣非零元的個數
 26 } TSMatrix, *PTSMatrix;
 27 
 28 /* ----------- 函式宣告部分 ------------------ */
 29 // 初始化稀疏矩陣結構
 30 int TSMatrix_Init(TSMatrix *M);
 31 // 以三元組的方式輸出稀疏矩陣
 32 void TSMatrix_PrintTriple(TSMatrix *M);
 33 // 以矩陣的方式輸出稀疏矩陣
 34 void TSMartix_PrintMatrix(TSMatrix *M);
 35 // 從一個二維陣列(普通矩陣)建立一個稀疏矩陣
 36 TSMatrix *TSMatrix_Create(int *a, int row, int col);
 37 // 從鍵盤錄入資料建立一個稀疏矩陣
 38 TSMatrix *TSMatrix_CreateFromInput();
 39 // 求稀疏矩陣 M 的轉置矩陣 T
 40 int TSMatrix_FastTranspose(TSMatrix M, TSMatrix *T);
 41 // 如果稀疏矩陣 M 和 N 的行數的列數相同,計算 Q = M + N
 42 int TSMatrix_Add(TSMatrix M, TSMatrix N, TSMatrix *Q);
 43 // 如果稀疏矩陣 M 的列數等於 N 的行數,計算 Q = M x N;
 44 int TSMatrix_Multply(TSMatrix M, TSMatrix N, TSMatrix *Q);
 45 // 把游標位置移動到該行行首
 46 void ResetCursor();
 47 
 48 /* ----------- 程式主函式 -------------------- */
 49 int main(void)
 50 {
 51     int info;
 52     char ch;
 53 
 54     // 從一個二維陣列建立一個係數矩陣
 55     TSMatrix *M;
 56     TSMatrix *N;
 57 
 58     // 用來接收運算結果的空間
 59     TSMatrix *T = (TSMatrix *)malloc(sizeof(TSMatrix));
 60 
 61     while (1)
 62     {
 63         fflush(stdin);
 64         system("cls");
 65 
 66         printf(" 稀疏矩陣基本操作演示 \n");
 67         printf("1. 矩陣的建立和轉置\n");
 68         printf("2. 矩陣的加法運算並以三元組輸出結果\n");
 69         printf("3. 矩陣的乘法運算並以矩陣輸出結果\n");
 70         printf("\n");
 71         printf("Q. 退出程式\n");
 72         printf("\n");
 73         printf("  請輸入選項:");
 74 
 75         scanf("%c", &ch);
 76 
 77         switch (ch)
 78         {
 79         case '1':
 80             system("cls");
 81 
 82             M = TSMatrix_CreateFromInput();
 83 
 84             if (M != NULL)
 85             {
 86                 printf("\n\n以三元組輸出稀疏矩陣:\n");
 87                 TSMatrix_PrintTriple(M);
 88                 printf("\n倒置後稀疏矩陣的三元組輸出:\n");
 89                 TSMatrix_FastTranspose(*M, T);
 90                 TSMatrix_PrintTriple(T);
 91 
 92                 system("pause");
 93             }
 94             else
 95             {
 96                 printf("建立矩陣過程發生錯誤");
 97                 system("pause");
 98             }
 99 
100             break;
101         case '2':
102             system("cls");
103 
104             M = TSMatrix_CreateFromInput();
105             N = TSMatrix_CreateFromInput();
106 
107             if (M == NULL || N == NULL)
108             {
109                 printf("建立矩陣過程中發生錯誤!\n");
110                 system("pause");
111                 break;
112             }
113 
114             info = TSMatrix_Add(*M, *N, T);
115 
116             if (info == MATRIX_NOT_MATCH)
117             {
118                 printf("這兩個矩陣不能運算呢!!  ⊙﹏⊙\n");
119             }
120             else if (info == OK)
121             {
122                 printf("\n運算結果:\n");
123                 TSMatrix_PrintTriple(T);
124             }
125 
126             system("pause");
127 
128             break;
129         case '3':
130             system("cls");
131 
132             M = TSMatrix_CreateFromInput();
133             N = TSMatrix_CreateFromInput();
134 
135             if (M == NULL || N == NULL)
136             {
137                 printf("建立矩陣過程中發生錯誤!\n");
138                 system("pause");
139                 break;
140             }
141 
142             info = TSMatrix_Multply(*M, *N, T);
143 
144             if (info == MATRIX_NOT_MATCH)
145             {
146                 printf("這兩個矩陣不能運算呢!!  ⊙﹏⊙\n");
147             }
148             else if (info == OK)
149             {
150                 printf("\n運算結果:\n");
151                 TSMartix_PrintMatrix(T);
152             }
153 
154             system("pause");
155 
156             break;
157         case 'q':
158         case 'Q':
159             exit(0);
160         }
161     }
162 
163     return 0;
164 }
165 
166 // 初始化稀疏矩陣結構
167 int TSMatrix_Init(TSMatrix *M)
168 {
169     M->data = (Triple *)malloc(MAXSIZE * sizeof(Triple));
170 
171     if (!M->data)
172         return MALLOC_FAIL;
173 
174     M->num = 0;
175     M->colnum = 0;
176     M->rownum = 0;
177 
178     return OK;
179 }
180 
181 // 從一個二維陣列建立一個稀疏矩陣
182 TSMatrix *TSMatrix_Create(int *a, int row, int col)
183 {
184     int i, j;
185     TSMatrix *P = (TSMatrix *)malloc(sizeof(TSMatrix));
186     TSMatrix_Init(P);
187 
188     // 設定稀疏矩陣的行數和列數
189     P->rownum = row;
190     P->colnum = col;
191 
192     for (i = 0; i < row; i++)
193     {
194         for (j = 0; j < col; j++)
195         {
196             // 如果第 i+1 行第 i+1 列元素是非零元素
197             if (0 != *(a + i * col + j))
198             {
199                 // 把非零元的元素和位置資訊儲存到稀疏矩陣中
200                 P->data[P->num].e = *(a + i * col + j);
201                 P->data[P->num].row = i + 1;
202                 P->data[P->num].col = j + 1;
203 
204                 // 把稀疏矩陣中的非零元個數加一
205                 P->num++;
206             }
207         }
208     }
209 
210     return P;
211 }
212 
213 // 以三元組的方式輸出稀疏矩陣
214 void TSMatrix_PrintTriple(TSMatrix *M)
215 {
216     int i;
217 
218     if (0 == M->num)
219     {
220         printf("稀疏矩陣為空!\n");
221         return;
222     }
223 
224     printf("  i    j    v  \n");
225     printf("===============\n");
226 
227     for (i = 0; i < M->num; i++)
228     {
229         printf("%3d  %3d  %3d\n", M->data[i].row, M->data[i].col, M->data[i].e);
230     }
231 
232     printf("===============\n");
233 }
234 
235 // 求稀疏矩陣 M 的轉置矩陣 T
236 int TSMatrix_FastTranspose(TSMatrix M, TSMatrix *T)
237 {
238     int *num, *cpot, i, t;
239 
240     // 如果矩陣 M 為空矩陣,返回錯誤資訊
241     if (M.num == 0)
242         return EMPTY_MATRIX;
243 
244     // 分配臨時的工作空間
245     num = (int *)malloc((M.colnum + 1) * sizeof(int));
246     cpot = (int *)malloc((M.colnum + 1) * sizeof(int));
247 
248     // 如果臨時的工作空間分配不成功
249     if (num == NULL || cpot == NULL)
250         return MALLOC_FAIL;
251 
252     // 初始化臨時工作空間(把 num 陣列用 0 填充)
253     for (i = 1; i <= M.rownum; i++)
254         num[i] = 0;
255 
256     // 統計倒置後每行的元素數量(即統計倒置前矩陣每列元素的數量)
257     for (i = 1; i <= M.num; i++)
258         num[M.data[i - 1].col]++;
259 
260     // 設定 T 矩陣每行首個非零元的位置
261     cpot[1] = 0;
262     for (i = 2; i <= M.colnum; i++)
263         cpot[i] = cpot[i - 1] + num[i - 1];
264 
265     // 把 T 矩陣的資訊清空
266     TSMatrix_Init(T);
267 
268     // 把矩陣 M 的資訊填充到 T 中。
269     // 矩陣倒置以後,T 的行數等於 M 的列數,T 的列數等於 M 的行數
270     T->num = M.num;
271     T->colnum = M.rownum;
272     T->rownum = M.colnum;
273 
274     // 對 M 矩陣中每個非零元素進行轉置操作
275     for (i = 0; i < M.num; i++)
276     {
277         t = cpot[M.data[i].col];
278 
279         T->data[t].col = M.data[i].row;
280         T->data[t].row = M.data[i].col;
281         T->data[t].e = M.data[i].e;
282 
283         ++cpot[M.data[i].col];
284     }
285 
286     // 轉置完成後釋放臨時工作空間
287     free(num);
288     free(cpot);
289 
290     return OK;
291 }
292 
293 // 如果稀疏矩陣 M 和 N 的行數的列數相同,計算 Q = M + N
294 int TSMatrix_Add(TSMatrix M, TSMatrix N, TSMatrix *Q)
295 {
296     int i = 0, j = 0, k = 0;
297 
298     if (M.colnum != N.colnum || M.rownum != N.rownum)
299         return MATRIX_NOT_MATCH;
300 
301     // 填充結果矩陣資訊
302     TSMatrix_Init(Q);
303     Q->colnum = M.colnum;
304     Q->rownum = M.rownum;
305     Q->num = 0;
306 
307     while (i < M.num && j < N.num)
308     {
309         // 如果 i j 指向元素是同一行的元素
310         if (M.data[i].row == N.data[j].row)
311         {
312             // 如果 i 和 j 指向的元素指向的是同一個元素
313             if (M.data[i].col == N.data[j].col)
314             {
315                 Q->data[k].row = M.data[i].row;
316                 Q->data[k].col = M.data[i].col;
317                 Q->data[k].e = M.data[i].e + N.data[j].e;
318                 Q->num++;
319 
320                 i++;
321                 j++;
322                 k++;
323             }
324             // 如果 i 指向元素的列下標大於 j 指向元素的列下標
325             // 把下標小(j 指向的元素)的放入到 Q 矩陣中
326             else if (M.data[i].col > N.data[j].col)
327             {
328                 Q->data[k].row = N.data[j].row;
329                 Q->data[k].col = N.data[j].col;
330                 Q->data[k].e = N.data[j].e;
331                 Q->num++;
332 
333                 j++;
334                 k++;
335             }
336             // 如果 i 指向元素的列下標小於 j 指向元素的列下標
337             // 把下標小(i 指向的元素)的放入到 Q 矩陣中
338             else if (M.data[i].col < N.data[j].col)
339             {
340                 Q->data[k].row = M.data[i].row;
341                 Q->data[k].col = M.data[i].col;
342                 Q->data[k].e = M.data[i].e;
343                 Q->num++;
344 
345                 i++;
346                 k++;
347             }
348         }
349         // 如果 i 指向的元素行下標大於 j 指向元素的行下標
350         else if (M.data[i].row > N.data[j].row)
351         {
352             Q->data[k].row = N.data[j].row;
353             Q->data[k].col = N.data[j].col;
354             Q->data[k].e = N.data[j].e;
355             Q->num++;
356 
357             k++;
358             j++;
359         }
360         // 如果 i 指向元素行下標小於 j 指向元素的行下標
361         else if (M.data[i].row < N.data[j].row)
362         {
363             Q->data[k].row = M.data[i].row;
364             Q->data[k].col = M.data[i].col;
365             Q->data[k].e = M.data[i].e;
366             Q->num++;
367 
368             i++;
369             k++;
370         }
371     }
372 
373     // 如果還有剩餘元素,按順序把元素新增到結果矩陣中
374     while (i < M.num)
375     {
376         Q->data[k].row = M.data[i].row;
377         Q->data[k].col = M.data[i].col;
378         Q->data[k].e = M.data[i].e;
379         Q->num++;
380 
381         i++;
382         k++;
383     }
384 
385     while (j < N.num)
386     {
387         Q->data[k].row = N.data[j].row;
388         Q->data[k].col = N.data[j].col;
389         Q->data[k].e = N.data[j].e;
390 
391         Q->num++;
392 
393         j++;
394         k++;
395     }
396 
397     return OK;
398 }
399 
400 // 如果稀疏矩陣 M 的列數等於 N 的行數,計算 Q = M x N;
401 int TSMatrix_Multply(TSMatrix M, TSMatrix N, TSMatrix *Q)
402 {
403     int *num_m, *cpot_m, *num_n, *cpot_n, i, j, k, s, col;
404     int a, ri, rj;
405 
406     // 如果兩個矩陣不滿足矩陣相乘的條件,返回錯誤資訊
407     if (M.colnum != N.rownum)
408         return MATRIX_NOT_MATCH;
409 
410     // 分配臨時空間
411     num_m = (int *)malloc((M.rownum + 1) * sizeof(int));
412     cpot_m = (int *)malloc((M.rownum + 1) * sizeof(int));
413     num_n = (int *)malloc((N.rownum + 1) * sizeof(int));
414     cpot_n = (int *)malloc((N.rownum + 1) * sizeof(int));
415 
416     // 填充結果矩陣的資訊
417     TSMatrix_Init(Q);
418 
419     Q->rownum = M.rownum;
420     Q->colnum = N.colnum;
421     Q->num = 0;
422 
423     // 如果相乘為零矩陣,直接返回
424     if (0 == M.num * N.num)
425         return OK;
426 
427     // 初始化臨時空間
428     for (i = 1; i <= M.colnum; i++)
429         num_m[i] = 0;
430 
431     for (i = 1; i <= N.colnum; i++)
432         num_n[i] = 0;
433 
434     // 計算矩陣每行非零元元素的數量
435     for (i = 0; i < M.num; i++)
436         num_m[M.data[i].row]++;
437 
438     for (i = 0; i < N.num; i++)
439         num_n[N.data[i].row]++;
440 
441     cpot_m[1] = cpot_n[1] = 0;
442 
443     for (i = 2; i <= M.rownum; i++)
444         cpot_m[i] = cpot_m[i - 1] + num_m[i - 1];
445 
446     for (i = 2; i <= N.rownum; i++)
447         cpot_n[i] = cpot_n[i - 1] + num_n[i - 1];
448 
449     // 計算過程
450     for (i = 1; i <= M.rownum; i++)
451     {
452         // 表示相乘結果在矩陣中的行下標
453         ri = i;
454 
455         for (j = cpot_m[i]; j < cpot_m[i] + num_m[i]; j++)
456         {
457             // 初始化累加器
458             s = 0;
459 
460             // 表示相乘結果在矩陣中的列下標
461             rj = M.data[j].col;
462 
463             // 獲得第 i 行首個非零元素的位置
464             k = cpot_m[i];
465 
466             // 對該行的每個元素進行相乘操作並累加
467             while (k - cpot_m[i] < num_m[i])
468             {
469                 col = M.data[k].col;
470 
471                 a = cpot_n[col];
472                 
473                 // 如果 a 指向元素仍然屬於第 a 元素
474                 // 遍歷,找到符合條件的元素相乘,並把相乘結果累加到累加器中
475                 while (a - cpot_n[col] < num_n[col])
476                 {
477                     if (N.data[a].row == M.data[j].col)
478                     {
479                         s = s + (M.data[j].e * N.data[a].e);
480                     }
481 
482                     a++;
483                 }
484 
485                 k++;
486             }
487 
488             if (s != 0)
489             {
490                 Q->data[Q->num].row = ri;
491                 Q->data[Q->num].col = rj;
492                 Q->data[Q->num].e = s;
493                 Q->num++;
494             }
495         }
496     }
497     
498     // 處理完成後釋放臨時空間
499     free(num_m);
500     free(cpot_m);
501     free(num_n);
502     free(cpot_n);
503 
504     return OK;
505 }
506 
507 // 以矩陣的方式輸出稀疏矩陣
508 void TSMartix_PrintMatrix(TSMatrix *M)
509 {
510     int count, i, k;
511 
512     // 求出原矩陣的元素個數
513     count = M->colnum * M->rownum;
514 
515     k = 0;
516 
517     for (i = 0; i < count; i++)
518     {
519         // 如果發現對應位置的非零元
520         if (M->data[k].row == ((i / M->colnum) + 1) && 
521             M->data[k].col == ((i % M->colnum) + 1))
522         {
523             printf("%3d", M->data[k].e);
524             k++;
525         }
526         else
527         {
528             printf("%3d", 0);
529         }
530 
531         if ((i % M->colnum) + 1 == M->colnum)
532             printf("\n");
533     }
534 }
535 
536 TSMatrix *TSMatrix_CreateFromInput()
537 {
538     int *a, i, j, k;
539     TSMatrix *T;
540 
541     ResetCursor();
542     printf("請輸入新建立的矩陣的行數和列數,分別輸入並利用空格間開:");
543 
544     // 輸入的同時對資料的有效性進行檢查
545     while (2 != scanf("%d%d", &i, &j))
546         printf("無效輸入,請重新輸入!\n");
547 
548     // 分配臨時儲存輸入資料的空間
549     a = (int *)malloc(i * j * sizeof(int));
550 
551     // 如果分配失敗,則返回一個空指標
552     if (a == NULL)
553         return NULL;
554 
555     // 開始從鍵盤中讀入元素
556     for (k = 0; k < i * j; k++)
557     {
558         ResetCursor();
559 
560         printf("請從鍵盤輸入第 %d 行第 %d 列元素:", (k / j) + 1, (k % j) + 1);
561 
562         while (1 != scanf("%d", (a + k)))
563         {
564             printf("無效輸入,請重新輸入!\n");
565         }
566     }
567 
568     T = TSMatrix_Create(a, i, j);
569 
570     // 釋放用於臨時儲存輸入的空間
571     free(a);
572 
573     return T;
574 }
575 
576 // 把游標位置移動到該行行首
577 void ResetCursor()
578 {
579     HANDLE hOut;
580     COORD cTarget;
581     CONSOLE_SCREEN_BUFFER_INFO info;
582     int y = 0;
583 
584     hOut = GetStdHandle(STD_OUTPUT_HANDLE);
585 
586     GetConsoleScreenBufferInfo(hOut, &info);
587     y = info.dwCursorPosition.Y;
588     cTarget.X = 0;
589     cTarget.Y = y;
590     SetConsoleCursorPosition(hOut, cTarget);
591 }