离散实验——关系闭包运算

【实验目的】掌握求关系闭包的方法。

【实验内容】编程求一个关系的闭包,要求传递闭包用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(" }
");
    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("自反闭包:
");
    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("对称闭包:
");
    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("传递闭包:
");
    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("传递闭包:
");
    show();
}
int main(void)
{
    printf("请输入集合的元素个数:");
    scanf("%d", &n);
    printf("请输入集合内容:
");
    for (int i = 0; i < n; i++)
    {
        scanf("%d", &set[i]);
    }

    printf("请输入二元关系的序偶个数:");
    int lon; scanf("%d", &lon);
    printf("请输入集合上的二元关系:
");
    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>

*/
View Code

一,求自反闭包

r(R) = R U IA  (IA 为 R 上的恒等关系)

 所以只需要将 关系矩阵 的 主对角线  全部变成 1,就可以了.

void funR()   // 求 自反闭包
{
	initc(r);
	for (int i = 0; i < n; i++)
		c[i][i] = 1;
	printf("自反闭包:
");
	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("对称闭包:
");
	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("传递闭包:
");
	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("传递闭包:
");
	show();
}

  

 

 四,补充内容

1,集合与关系矩阵的对应关系

通过 一个 set 数组,按顺序对应关系矩阵.

2, 打印闭包的方法

其中,遇到的一个问题是 ,没有算完,不知道 “,” 有几个

所以巧妙运用 flag 进行处理

3,initc() 

因为 原先的 关系矩阵 不能 更改。所以需要一个新的 关系矩阵 来

存 闭包的关系,所以  数组c 就是 这个新的矩阵。

另外将 c  初始化为 R的关系矩阵 这样处理比较方便,所以 initc() 就是这样一个函数。

 

 

=========== ========= ======== ======= ====== ===== ==== === == =

诫子书  诸葛亮(三国) 

 
夫君子之行,静以修身,俭以养德。非淡泊无以明志,非宁静无以致远。
夫学须静也,才须学也,非学无以广才,非志无以成学。淫慢则不能励精,险躁则不能治性。
年与时驰,意与日去,遂成枯落,多不接世,悲守穷庐,将复何及!

 

 

 

原文地址:https://www.cnblogs.com/asdfknjhu/p/13064348.html