【實驗目的】掌握求關系閉包的方法。
【實驗內容】編程求一個關系的閉包,要求傳遞閉包用warshall方法。
例 :A={8、6、4、2},A上的關系R={<8,6><6,8><6,4><4,2>}
結果:
自反閉包:
{ <8,8>,<8,6>,<6,8>,<6,6>,<6,4>,<4,4>,<4,2>,<2,2> }
1 1 0 0
1 1 1 0
0 0 1 1
0 0 0 1
對稱閉包:
{ <8,6>,<6,8>,<6,4>,<4,6>,<4,2>,<2,4> }
0 1 0 0
1 0 1 0
0 1 0 1
0 0 1 0
傳遞閉包:
{ <8,8>,<8,6>,<8,4>,<8,2>,<6,8>,<6,6>,<6,4>,<6,2>,<4,2> }
1 1 1 1
1 1 1 1
0 0 0 1
0 0 0 0
【源程序及解析】

#define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<stdlib.h> #define N 100 int n; // int r[N][N]; // 關系矩陣 int c[N][N]; // 可以初始化為關系矩陣, 最后用來存放 閉包矩陣 int set[N]; // 存放 集合中的元素 void initc(int t[][N]) // 將 閉包矩陣 化成 其他的矩陣 { for (int i = 0; i < n; i++) for (int j = 0; j < n; j++) c[i][j] = t[i][j]; } void show() { int flag = 0; printf("{ "); for (int i = 0; i < n; i++) for (int j = 0; j < n; j++) { if (c[i][j]) // 這個 三目運算 用的挺秀的 { printf(flag ? ",<%d,%d>" : "<%d,%d>", set[i], set[j]); flag = 1; } } printf(" }\n"); for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) printf("%d ", c[i][j]); puts(""); } } void funR() // 求 自反閉包 { initc(r); for (int i = 0; i < n; i++) c[i][i] = 1; printf("自反閉包:\n"); show(); } void funS() // 求 對稱閉包 { initc(r); for (int i = 0; i < N; i++) { for (int j = 0; j < N; j++) { if (c[i][j]) c[j][i] = 1; } } printf("對稱閉包:\n"); show(); } void funT1(void) // 求 傳遞閉包第一種方法 { initc(r); int b[N][N]; for (int m = 1; m < n; m++) // 求 矩陣的 n-1 次方 { for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { b[i][j] = 0; for (int k = 0; k < n; k++) b[i][j] += c[i][k] * r[k][j]; // 矩陣運算 c[i][j] += b[i][j]; // 將求得的 矩陣的 m 次方與前面的累 並 if (c[i][j]) c[i][j] = 1; } } } printf("傳遞閉包:\n"); show(); } void funT2(void) // 求 傳遞閉包第二種方法 { initc(r); for (int k = 0; k < N; k++) // 枚舉途徑 某中間頂點 k 的任兩個頂點對 i 和 j ,進而通過判斷 k->j 是否連接,來判斷 是否將 i->j 連接 { for (int i = 0; i < N; i++) if (c[i][k]) for (int j = 0; j < N; j++) { /* 有兩種情況 ①,若 i->j 已連接,則 k->j 是否連接 並無影響 ② 若 i->j 沒有連接,則 k->j 連接了,則將 i->j 連接 則 k->j 沒有連接,則不必將 i->j 連接 */ c[i][j] = c[i][j] + c[k][j]; if (c[i][j]) c[i][j] = 1; } } printf("傳遞閉包:\n"); show(); } int main(void) { printf("請輸入集合的元素個數:"); scanf("%d", &n); printf("請輸入集合內容:\n"); for (int i = 0; i < n; i++) { scanf("%d", &set[i]); } printf("請輸入二元關系的序偶個數:"); int lon; scanf("%d", &lon); printf("請輸入集合上的二元關系:\n"); for (int i = 0; i < lon; i++) { char s1, s2; int a, b; scanf(" %c%d,%d %c", &s1, &a, &b, &s2); for (int i = 0; i < n; i++) // 將有序對第一元素 變成 鄰接矩陣的橫坐標 if (set[i] == a) { a = i; break; } for (int i = 0; i < n; i++) // 將有序對第二元素 變成 鄰接矩陣的縱坐標 if (set[i] == b) { b = i; break; } r[a][b] = 1; } puts(""); funR(); funS(); funT1(); funT2(); system("pause"); return 0; } /* 測試數據 1: 4 8 6 4 2 4 <8,6> <6,8> <6,4> <4,2> 測試數據 2: 3 1 2 3 3 <1,2> <2,3> <3,1> */
一,求自反閉包
r(R) = R U IA (IA 為 R 上的恆等關系)
所以只需要將 關系矩陣 的 主對角線 全部變成 1,就可以了.
void funR() // 求 自反閉包 { initc(r); for (int i = 0; i < n; i++) c[i][i] = 1; printf("自反閉包:\n"); show(); }
二,求對稱閉包
s(R) = R U R' (這里的 R‘ 是指 R 的逆)
所以只需要在 關系矩陣 的基礎上,若 r(ij) = 1, 則 r(ji) = 1
void funS() // 求 對稱閉包 { initc(r); for (int i = 0; i < N; i++) { for (int j = 0; j < N; j++) { if (c[i][j]) c[j][i] = 1; } } printf("對稱閉包:\n"); show(); }
三,求傳遞閉包
這里有兩種方法。
① t(R) = R U R2 U R3 U ... U Rn (這里 Rn 指的是 R的n次冪,n 指的是 R 的 邊數,代碼中是 點數)
所以只需要 進行 矩陣的 乘法運算 和 布爾運算 就可以了
void funT1(void) // 求 傳遞閉包第一種方法 { initc(r); int b[N][N]; for (int m = 1; m < n; m++) // 求 矩陣的 n-1 次方 { for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { b[i][j] = 0; for (int k = 0; k < n; k++) b[i][j] += c[i][k] * r[k][j]; // 矩陣運算 c[i][j] += b[i][j]; // 將求得的 矩陣的 m 次方與前面的累 並 if (c[i][j]) c[i][j] = 1; } } } printf("傳遞閉包:\n"); show(); }
② 用 warshall方法
即 枚舉途徑 某中間頂點 k 的任兩個頂點對 i 和 j ,進而通過判斷 k->j 是否連接,來判斷 是否將 i->j 連接
然后 ① 若 i->j 已連接,則 k->j 是否連接 並無影響
② 若 i->j 沒有連接,則 k->j 連接了,則將 i->j 連接
, 則 k->j 沒有連接,則不必將 i->j 連接
void funT2(void) // 求 傳遞閉包第二種方法 { initc(r); for (int k = 0; k < N; k++) // 枚舉途徑 某中間頂點 k 的任兩個頂點對 i 和 j ,進而通過判斷 k->j 是否連接,來判斷 是否將 i->j 連接 { for (int i = 0; i < N; i++) if (c[i][k]) for (int j = 0; j < N; j++) { /* 有兩種情況 ①,若 i->j 已連接,則 k->j 是否連接 並無影響 ② 若 i->j 沒有連接,則 k->j 連接了,則將 i->j 連接 則 k->j 沒有連接,則不必將 i->j 連接 */ c[i][j] = c[i][j] + c[k][j]; if (c[i][j]) c[i][j] = 1; } } printf("傳遞閉包:\n"); show(); }
四,補充內容
1,集合與關系矩陣的對應關系
通過 一個 set 數組,按順序對應關系矩陣.
2, 打印閉包的方法
其中,遇到的一個問題是 ,沒有算完,不知道 “,” 有幾個
所以巧妙運用 flag 進行處理
3,initc()
因為 原先的 關系矩陣 不能 更改。所以需要一個新的 關系矩陣 來
存 閉包的關系,所以 數組c 就是 這個新的矩陣。
另外將 c 初始化為 R的關系矩陣 這樣處理比較方便,所以 initc() 就是這樣一個函數。
=========== ========= ======== ======= ====== ===== ==== === == =
誡子書 諸葛亮(三國)