稀疏矩陣(三元組表示)基本操作 實驗報告 (轉置,加法,乘法)
一、 實驗內容
稀疏矩陣的壓縮儲存結構,以及稀疏矩陣的三元組表表示方法下的轉置、相加、相乘等演算法
二、 實驗目的
1. 熟悉陣列、矩陣的定義和基本操作
2. 熟悉稀疏矩陣的儲存方式和基本運算
3. 理解稀疏矩陣的三元組表型別定義,掌握稀疏矩陣的輸入、輸出和轉置演算法
三、 實驗原理
1. 使用三元組儲存矩陣中的非零元素(三元組分別儲存非零元素的行下標,列下標和元素值)。除了三元組表本身,儲存一個稀疏矩陣還需要額外的三個變數,分別儲存矩陣的非零元個數,矩陣的行數和矩陣的列數。
2. 稀疏矩陣的建立演算法:
第一步:根據矩陣建立一個二維陣列,表示原始矩陣
第二步:取出二維陣列中的元素(從第一個元素開始取),判斷取出元素是否為非零元素,如果為非零元素,把該非零元素的數值以及行下標和列下表儲存到三元陣列表裡,否則取出下一個元素,重複該步驟。
第三步:重複第二步,知道二維陣列中所有的元素已經取出。
3. 稀疏矩陣倒置演算法:
第一步:判斷進行倒置的矩陣是否為空矩陣,如果是,則直接返回錯誤資訊。
第二步:計算要倒置的矩陣每列非零元素的數量,存入到num陣列(其中num[i] 代表矩陣中第i列非零元素的個數)。以及倒置後矩陣每行首非零元的位置,存入cpot陣列中(其中cpot表示倒置後矩陣每行非零元的位置,對應表示原矩陣每列中第一個非零元的位置)。
第三步:確定倒置後矩陣的行數和列數。
第四步:取出表示要導致矩陣中三元組表元素 {e, I, j}(第一次取出第一個,依次取出下一個元素),從第二步
第五步:重複第四步,直到三元組表中所有的元素都完成倒置。
第六步:把完成倒置運算的三元組表輸出。
4. 稀疏矩陣加法演算法:
第一步:檢查相加兩個矩陣的行數和列數是否相同,如果相同,則進入第二步,否則輸出錯誤資訊。
第二步:定義變數i和j,用於控制三元組表的遍歷。
第三步:比較變數矩陣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_m和num_n陣列中),並計算出每行首非零元的位置(分別存放到cpot_m和cpot_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 }