离散数学的应用(C语言实现)

题目:设R为有限集合A上的关系,则编写程序实现如下问题:
(1)试使用关系性质判断的等价条件(如:自反、对称、传递)判断关系R是否是等价关系?
思路:用线性表表示关系矩阵,生成矩阵函数,遍历函数
(2)若R是等价关系,则求由R诱导的集合A的划分。
思路:即商集A/R,元素为集合的集合,划分的函数
(3)若R不是等价关系,则求包含R最小的等价关系。
思路:往关系R中增加有限个元素,编写添加元素的函数

一.集合与关系在计算机中的表示

集合中的元素使用链表存储。二元关系用关系矩阵表示,在程序中我使用结构体数组储存,每个数组成员都有行row、列col、值value(1表示该关系存在,0为否)。

二.判断等价关系

根据定义分别判断关系是否满足自反性、对称性、传递性。再在总的判断函数中判断是否满足这三个性质,从而判断是否为等价关系。
自反性:在单循环中遍历关系矩阵,当主对角线上元素的值value都为1时,关系满足自反性,存在value为0,不满足自反性。
对称性:在双重循环中遍历关系矩阵,在第二层循环中寻找与第一层循环中行列对称的元素(关系),判断该元素的值value是否为1,为1,关系满足对称性;为0,不满足对称性。
传递性:在三重循环中遍历关系矩阵,在第一二层循环中寻找符合条件的两个关系,即col1 = row2,再在第三层循环中遍历关系矩阵,寻找(row1,col2)的元素(关系),判断该元素的值是否为1,若为1,关系满足传递性;为0,不满足对称性。
在判断函数中分别判断该关系是否满足上述三个性质,同时对每个性质设一个返回值,最终根据返回值判断该关系满足了哪个性质,以及是否为等价关系。

三.求划分

根据划分的定义,首先求关系’xRy’中x的像集image,再对所有像集进行比较,记录像集image相同的y,将像集image相同的y构成一个集合,最后再将这些集合作为元素构成划分。

四.求最小等价关系

利用闭包求最小等价关系,根据定义,求出关系的最小自反闭包、对称闭包、传递闭包,再求这些闭包的并集,这个并集就是最小等价关系。首先根据等价关系的判断函数判断该关系不满足自反性、对称性、传递性中的哪一个,再对不满足的性质求闭包。

代码结构

在这里插入图片描述
一.“set.h”
函数功能
在这里插入图片描述

集合类及相关函数的头文件。

二.“relation.h”
函数功能
在这里插入图片描述

关系类(关系矩阵)及其相关函数。

三.“main.cpp”
主函数

在这里插入图片描述

四.“relationfunction.cpp”
关键代码解释
relation initializeRelation(slink *s)初始化关系矩阵

relation initializeRelation(slink *s){
    int n = TraversalSet(s);//得到集合中元素的个数
    relation r;
     
    r =(relation)malloc(sizeof(relation));//集合中元素个数为n个,创建一个n*n的矩阵,size == n*n
    r->count = n;
    r->size = n*n;
    r->matrix = (RNode*)malloc(r->size*sizeof(RNode));
    r->element = (int*)malloc(r->count*sizeof(int));
    int element[n],i = 0,col = 0,row = 0,index = 0;
    
    //遍历集合,储存在element[]数组中,供行列赋值使用
    slink* p = s->next;
    while(p!=NULL){
        element[i] = p->element;
        r->element[i] = element[i];
        i++;
        p = p -> next;
     }
    
    //矩阵行列赋值初始化
    for(i = 0;i < n;i++){
        row = element[i];
        for(int j = 0;j < n;j++){
            col = element[j];
            r->matrix[index].row = row;
            r->matrix[index].col = col;
            r->matrix[index].value = 0;
            index++;
        }
    }

    int a,b;//a行b列
    printf("请输入集合A上的关系:");
    scanf("%d %d",&a,&b);
    while(getchar()!='\n'){//键入为回车就结束循环
        for(i = 0;i < r->size;i++)
            if(r->matrix[i].row == a && r->matrix[i].col == b)//对输入的点赋值为1,代表该元素存在
                r->matrix[i].value = 1;
            
        scanf("%d %d",&a,&b);
    }

    for(i = 0;i < r->size;i++)
        if(r->matrix[i].row == a && r->matrix[i].col == b)
            r->matrix[i].value = 1;
        
    return r;
}

根据集合的元素构造一个n*n的关系矩阵(n为集合中元素个数),对每个元素的值value赋值为0,代表该关系不存在。然后用键盘输入关系,遍历矩阵找到该关系,将矩阵对应元素的值赋值为1。

void partition(relation r)求划分函数

void partition(relation r){

    int image[r->count][r->count];//像集,不能改变
    int index = -1,pos = 0,length = 0;
    int record[r->count];
    int partition[r->count][r->count];//记录哪几个像集相同
    
    //初始化数组
    //直接赋值会有一个随机值,不知道为啥
    memset(image, 0, sizeof(image));
    memset(partition, 0, sizeof(partition));
    memset(record, 0, sizeof(record));

    //生成该关系的像集
    for(int i = 0;i < r->size;i++){
        if(i%r->count == 0){//换行
            index++;
            pos = 0;
        }
           
        if(r->matrix[i].value == 1){
            image[index][pos++] = r->matrix[i].col;
        }
    }

    //寻找相同的像集
    for(int i = 0;i < r->count;i++){
        int k = 0;
        record[i] = 1;
        for(int j = i + 1;j < r->count;j++){

            //像集相同,为同一划分
            //比较完一行
            if(k == r->count){
                record[j] = 1;
                k = 0;//下一行从第一列开始比较
                j++;//换行
            }
            
            if(image[i][k] == image[j][k]){
                k++;
                j--;//继续遍历并比较该行元素
            }
        }

        for(int t = 0; t < r->count;t++){
            if(record[t] == 1){
                partition[i][t] = t+1;
                record[t] = 0;//成功!
            }
        }
    }

    for(int i = 0;i < r->count;i++){
        int k = 0;
        for(int j = i + 1;j < r->count;j++){

            if(k == r->count){
                record[j] = 1;
                k = 0;//下一行从第一列开始比较
                j++;//换行
            }
            
            if(partition[i][k] == partition[j][k]){
               partition[j][k] = 0;
            }
            k++;
            j--;
        }
    }
    
    printf("\npartition:{");
    for(int i = 0;i < r->count;i++){
       if(length == r->count)
           break;
       if(pos < 0)
           printf(",");//不同划分之间的逗号
       pos = 0;//重置信号,表示划分已改变
       printf("{");
       for(int j = 0;j < r->count;j++){
           if(partition[i][j] != 0){
              if(pos < 0)
                printf(",");//同一划分中元素个数大于1时
              pos = -1;
              printf("%d",partition[i][j]);
              length++;
           }
       }
       printf("}");
    }
    printf("}");
}

在一个单循环中遍历关系矩阵,并生成像集image,通过第一个判断语句改变index的值以及重置pos,以达到换行和从第一列查找的效果。

利用record数组来记录像集相同的行row(即关系xRy中的x)。
如果在遍历的过程中遇到相同的列col(即关系xRy中的y),则继续比较,即j–,表示继续在这一行进行比较,k自增表示向下一列比较。当k == r->count(即k的值等于集合中元素个数n)时,j自增,表示向下一行进行遍历,k重置为0,表示从第一列开始遍历,record[j]记为1,代表这一行(x)与第i行(x)有相同的像集,而i总是在j之前。
此时会有record重复的问题,即划分重复问题。
解决方法如下。

先根据record记录生成对应的划分。

就和判断相同像集的方法一样,只要再次利用双重循环,当第i行(x)与第j行(x)的划分相等时(有重复时),将第j行的划分值置为0,因为i总是在j之前,所以不用担心划分丢失的问题。

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include "relation.h"
#include "set.h"
#include <stdlib.h>
#include <string.h>

//生成关系矩阵
relation initializeRelation(slink *s){
    int n = TraversalSet(s);//得到集合中元素的个数
    relation r;
     
    r =(relation)malloc(sizeof(relation));//集合中元素个数为n个,创建一个n*n的矩阵,size == n*n
    r->count = n;
    r->size = n*n;
    r->matrix = (RNode*)malloc(r->size*sizeof(RNode));
    r->element = (int*)malloc(r->count*sizeof(int));
    int element[n],i = 0,col = 0,row = 0,index = 0;
    
    //遍历集合,储存在element[]数组中,供行列赋值使用
    slink* p = s->next;
    while(p!=NULL){
        element[i] = p->element;
        r->element[i] = element[i];
        i++;
        p = p -> next;
     }
    
    //矩阵行列赋值初始化
    for(i = 0;i < n;i++){
        row = element[i];
        for(int j = 0;j < n;j++){
            col = element[j];
            r->matrix[index].row = row;
            r->matrix[index].col = col;
            r->matrix[index].value = 0;
            index++;
        }
    }

    int a,b;//a行b列
    printf("请输入集合A上的关系:");
    scanf("%d %d",&a,&b);
    while(getchar()!='\n'){//键入为回车就结束循环
        for(i = 0;i < r->size;i++)
            if(r->matrix[i].row == a && r->matrix[i].col == b)//对输入的点赋值为1,代表该元素存在
                r->matrix[i].value = 1;
            
        scanf("%d %d",&a,&b);
    }

    for(i = 0;i < r->size;i++)
        if(r->matrix[i].row == a && r->matrix[i].col == b)
            r->matrix[i].value = 1;
        
    return r;
}


//判断关系是否具有自反性、对称性、传递性
bool judgeReflexive(relation r){
    //判断自反性,即i = size(主对角线)时,r.col == r.row
    for(int i = 0;i < r->size;i++)
        if(r->matrix[i].col == r->matrix[i].row)
          if(r->matrix[i].value == 0)
            return false;
    return true;
}

//判断是否满足对称性
bool judgeSymmetric(relation r){
    for(int i = 0;i < r->size;i++)
        if(r->matrix[i].col != r->matrix[i].row && r->matrix[i].value == 1)
            for(int j = 0;j < r->size;j++)
                if(r->matrix[i].col == r->matrix[j].row && r->matrix[j].col == r->matrix[i].row)
                    if(r->matrix[j].value == 0)
                      return false;

    return true;
}

//判断是否满足传递性
bool judgeTransitive(relation r){
    for(int i = 0;i < r->size;i++)
        for(int j = 0;j < r->size;j++)
            if(r->matrix[i].value == 1 && r->matrix[j].value == 1)
               if(r->matrix[i].col == r->matrix[j].row)
                 for(int k = 0;k < r->size;k++)
                    if(r->matrix[k].row == r->matrix[i].row && r->matrix[k].col == r->matrix[j].col)
                        if(r->matrix[k].value == 0)
                            return false;

    return true;
}

//判断该关系是否为等价关系
//每一性质都返回一个整数值,作为后续求最小等价关系的判断依据
//返回值的和不能重叠,比如不能出现"2,3,5",此时无法判断:同时满足自反性和对称性,与仅满足传递性
int judge(relation r){

    int result = 0;
    if(judgeReflexive(r)){
        printf("满足自反性!\n");
        result += 1;
    }
    else
    printf("不满足自反性!\n");

    if(judgeSymmetric(r)){
        printf("满足对称性!\n");
        result += 2;
    }
    else
    printf("不满足对称性!\n");

    if(judgeTransitive(r)){
        printf("满足传递性!\n");
        result += 10;
    }
    else
    printf("不满足传递性!\n");

    return result;
}

//遍历关系矩阵
void traversal(relation r){
    int i = 0;
    printf("关系为:\n");
    for(int i = 0;i < r->size;i++)
        if(r->matrix[i].value!=0)
          printf("(%d,%d) ",r->matrix[i].row,r->matrix[i].col);
}

//求该等价关系的划分
void partition(relation r){

    int image[r->count][r->count];//像集,不能改变
    int index = -1,pos = 0,length = 0;
    int record[r->count];
    int partition[r->count][r->count];//记录哪几个像集相同
    
    //初始化数组
    //直接赋值会有一个随机值,不知道为啥
    memset(image, 0, sizeof(image));
    memset(partition, 0, sizeof(partition));
    memset(record, 0, sizeof(record));

    //生成该关系的像集
    for(int i = 0;i < r->size;i++){
        if(i%r->count == 0){//换行
            index++;
            pos = 0;
        }
           
        if(r->matrix[i].value == 1){
            image[index][pos++] = r->matrix[i].col;
        }
    }

    //寻找相同的像集
    for(int i = 0;i < r->count;i++){
        int k = 0;
        record[i] = 1;
        for(int j = i + 1;j < r->count;j++){

            //像集相同,为同一划分
            //比较完一行
            if(k == r->count){
                record[j] = 1;
                k = 0;//下一行从第一列开始比较
                j++;//换行
            }
            
            if(image[i][k] == image[j][k]){
                k++;
                j--;//继续遍历并比较该行元素
            }
        }

        for(int t = 0; t < r->count;t++){
            if(record[t] == 1){
                partition[i][t] = t+1;
                record[t] = 0;//成功!
            }
        }
    }

    for(int i = 0;i < r->count;i++){
        int k = 0;
        for(int j = i + 1;j < r->count;j++){

            if(k == r->count){
                record[j] = 1;
                k = 0;//下一行从第一列开始比较
                j++;//换行
            }
            
            if(partition[i][k] == partition[j][k]){
               partition[j][k] = 0;
            }
            k++;
            j--;
        }
    }
    
    printf("\npartition:{");
    for(int i = 0;i < r->count;i++){
       if(length == r->count)
           break;
       if(pos < 0)
           printf(",");//不同划分之间的逗号
       pos = 0;//重置信号,表示划分已改变
       printf("{");
       for(int j = 0;j < r->count;j++){
           if(partition[i][j] != 0){
              if(pos < 0)
                printf(",");//同一划分中元素个数大于1时
              pos = -1;
              printf("%d",partition[i][j]);
              length++;
           }
       }
       printf("}");
    }
    printf("}");
}

//求自反、对称、传递闭包的并集
//即将判断等价关系中的返回语句改为赋值语句
void closure(relation r){ 

    if(!judgeReflexive(r)){
      for(int i = 0;i < r->size;i++)
        if(r->matrix[i].col == r->matrix[i].row)
          if(r->matrix[i].value == 0)
            r->matrix[i].value = 1;
    }
    else if(!judgeSymmetric(r)){
        for(int i = 0;i < r->size;i++)
          if(r->matrix[i].col != r->matrix[i].row && r->matrix[i].value == 1)
             for(int j = 0;j < r->size;j++)
                if(r->matrix[i].col == r->matrix[j].row && r->matrix[j].col == r->matrix[i].row)
                    if(r->matrix[j].value == 0)
                       r->matrix[j].value = 1;
    }
    else if(!judgeTransitive(r)){
      for(int i = 0;i < r->size;i++)
        for(int j = 0;j < r->size;j++)
            if(r->matrix[i].value == 1 && r->matrix[j].value == 1)
               if(r->matrix[i].col == r->matrix[j].row)
                 for(int k = 0;k < r->size;k++)
                    if(r->matrix[k].row == r->matrix[i].row && r->matrix[k].col == r->matrix[j].col)
                        if(r->matrix[k].value == 0)
                            r->matrix[k].value = 1;
    }

    traversal(r);
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 3
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 邻接矩阵是用来表示图的常用数据结构,它是一个二维的矩阵,其中每个元素表示两个顶点之间是否有边相连。如果顶点 $i$ 和顶点 $j$ 之间有边相连,则邻接矩阵中第 $i$ 行第 $j$ 列的元素为 $1$,否则为 $0$。 下面是求无向图邻接矩阵的 C 语言代码: ```c #include <stdio.h> #define MAXV 100 // 最大顶点数 int matrix[MAXV][MAXV]; // 邻接矩阵 int V, E; // 顶点数和边数 int main() { int u, v; scanf("%d %d", &V, &E); // 输入顶点数和边数 for (int i = 0; i < E; i++) { scanf("%d %d", &u, &v); // 输入边 matrix[u][v] = matrix[v][u] = 1; // 在邻接矩阵中标记边 } // 输出邻接矩阵 for (int i = 1; i <= V; i++) { for (int j = 1; j <= V; j++) { printf("%d ", matrix[i][j]); } printf("\n"); } return 0; } ``` 这段代码首先定义了一个 $100 \times 100$ 的二维数组 `matrix`,表示邻接矩阵。然后从标准输入中读入顶点数 `V` 和边数 `E`,并利用循环读入每一条边,将邻接矩阵中对应的元素标记为 $1$。最后再利用循环输出邻接矩阵即可。 ### 回答2: 离散数学是数学的一个分支领域,主要研究离散结构和离散对象的性质和关系。邻接矩阵是描述无向图或有向图的常用方式之一,可以通过矩阵的形式表示图中各个顶点之间的连接情况。 在C语言中,可以使用二维数组来表示邻接矩阵。假设有一个图的顶点数为n,那么可以定义一个n * n大小的二维数组来表示邻接矩阵。 首先,需要定义一个函数来创建邻接矩阵,函数的参数为图的顶点数n和一个存放边信息的数组edges[]。edges数组用来表示每条边连接的两个顶点。 ```c void createAdjMatrix(int n, int edges[][2]) { int adjMatrix[n][n]; // 定义n * n大小的二维数组用来存储邻接矩阵 // 初始化邻接矩阵 for(int i = 0; i < n; i++) { for(int j = 0; j < n; j++) { adjMatrix[i][j] = 0; // 初始化所有元素为0 } } // 根据edges数组中的信息来更新邻接矩阵 for(int i = 0; i < n; i++) { int src = edges[i][0]; // 起始顶点 int dest = edges[i][1]; // 终止顶点 adjMatrix[src][dest] = 1; // 表示src和dest之间有边 // 如果是无向图,还需要添加下面这行代码 // adjMatrix[dest][src] = 1; } // 输出邻接矩阵 for(int i = 0; i < n; i++) { for(int j = 0; j < n; j++) { printf("%d ", adjMatrix[i][j]); } printf("\n"); } } ``` 以上的代码将会根据传入的顶点数和边信息创建一个邻接矩阵,并输出该矩阵。 需要注意的是,上述代码只给出了无向图的情况,如果是有向图,则不需要添加注释掉的那句代码。 使用该函数时,可以在主函数中定义一个边信息数组,并传入该函数中进行调用: ```c int main() { int n = 5; // 顶点数 int edges[][2] = {{0, 1}, {1, 2}, {2, 3}, {3, 4}, {4, 0}}; // 边信息数组 createAdjMatrix(n, edges); // 创建并输出邻接矩阵 return 0; } ``` 以上就是用C语言求解邻接矩阵的一个简单示例。 ### 回答3: 离散数学是研究离散对象和离散关系的数学分支,而邻接矩阵是用于表示图的一种常见方式。C语言是一种广泛应用于计算机编程的编程语言,因此可以利用C语言实现邻接矩阵。 邻接矩阵是一个二维数组,用于表示图中各个顶点之间的连接关系。具体实现邻接矩阵的方式可以通过使用C语言的二维数组来表示,其中矩阵的行和列分别对应图中的各个顶点。 以下是一个用C语言实现邻接矩阵的简单示例: ```C #include <stdio.h> #define MAX_VERTICES 100 int adjMatrix[MAX_VERTICES][MAX_VERTICES]; // 定义邻接矩阵 void addEdge(int u, int v) { adjMatrix[u][v] = 1; // 将顶点u与顶点v的连接关系设为1 adjMatrix[v][u] = 1; // 因为是无向图,所以顶点v与顶点u的连接关系也设为1 } int main() { int n, m; // n表示顶点数,m表示边数 scanf("%d %d", &n, &m); // 输入顶点数和边数 for (int i = 0; i < m; i++) { int u, v; scanf("%d %d", &u, &v); // 输入边的两个顶点u和v,表示u与v之间存在一条边 addEdge(u, v); // 在邻接矩阵中添加边的连接关系 } // 打印邻接矩阵 for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { printf("%d ", adjMatrix[i][j]); } printf("\n"); } return 0; } ``` 以上的代码实现了一个简单的邻接矩阵,通过输入顶点数和边数,以及各条边的起始顶点和结束顶点,即可得到对应的邻接矩阵。利用C语言的二维数组特性,可以方便地表示和操作邻接矩阵,并且适用于较小规模的图结构。当图规模较大时,可能需要考虑其他数据结构算法的优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值