离散实验——关系闭包运算
【实验目的】掌握求关系闭包的方法。
【实验内容】编程求一个关系的闭包,要求传递闭包用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() 就是这样一个函数。
=========== ========= ======== ======= ====== ===== ==== === == =
诫子书 诸葛亮(三国)