题目:设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);
}