3.1實驗目的
關系是集合論中的一個十分重要的概念,關系性質的判定是集合論中的重要內容。通過該組實驗,更加深刻地理解關系的概念和性質,並掌握關系性質的判定及關系的閉包的求法。
3.2實驗內容
1、鍵盤輸入集合A中的所有元素,並輸入關系R中的所有序偶對,建立關系R的關系矩陣;
2、判斷關系所具有的性質;
3、求關系R的逆關系,及關系的合成運算;
4、求關系R的r(R)、s(R)、t(R)。(注意關系的傳遞閉包采用Warshall算法)。
5、判斷關系R是否為等價關系,若是等價關系,則求出其所有等價類;
6、選做:求集合A上的等價關系數
3.3主要算法思想
1、鍵盤輸入集合A中的所有元素,並輸入關系R中的所有序偶對,建立關系R的關系矩陣;
①用字符串ListA保存集合A中所有元素,每個字符就是一個元素
②然后用List_Relation保存關系R。
③然后定義一個方法建立關系R的關系矩陣。
2、判斷關系所具有的性質
關系性質的充要條件:
設R為A上的關系, 則
(1) R在A上自反當且僅當 IA⊆R
(2) R在A上反自反當且僅當 R∩IA=Ø
(3) R在A上對稱當且僅當 R=R-1
(4) R在A上反對稱當且僅當 R∩R-1⊆IA
(5) R在A上傳遞當且僅當 R°R⊆R
判斷自反性:
①關系R是自反性說明 R包含集合A的恆等關系;
②所以根據在1建立的關系矩陣來判斷,利用一層循環判斷對角線的值是否為1,若對角線上的值有一個為0返回false,都為1的話最后返回true;
///判斷自反性
///List_Relation是自反性說明 List_Relation包含集合A的恆等關系
///所以利用循環判斷每個元素
bool ReflexivityJudge(int** R) {
for (int i = 0; i < ListA.length(); i++)
if (R[i][i] == 0)
return false;
return true;
}
判斷反自反性:
①關系R是反自反性說明在關系矩陣中對角線的值都為0;
②所以還是根據在1建立的關系矩陣來判斷,還是利用一層循環判斷對角線的值是否為1,若對角線上的值有一個為1返回false,都為0的話最后返回true;
///判斷反自反性
///R在A上反自反當且僅當 R∩IA=空集
///意思就是沒有一個環
bool InverserReflexivityJudge(int** R) {
int n = ListA.length();
for (int i = 0; i < n; i++) {
if (R[i][i] == 1) return false;
}
return true;
}
判斷對稱性:
①關系R是對稱性則說明它的關系矩陣一定是對稱矩陣;
②所以利用兩層循環遍歷矩陣所有位置,並每次判斷當前位置的值與他對稱位置的值是否相等(R[i][j]==R[j][i]),若有一次不相等返回false,全部相等則返回true;
///判斷對稱性
///List_Relation滿足對稱性,則說明它的關系矩陣一定是對稱矩陣
bool SymmetryJudge(int** R) {
for (int i = 0; i < ListA.length(); i++)
for (int j = 0; j < i; j++)if (R[i][j] != R[j][i])return false;
return true;
}
判斷反對稱性:
①R在A上反對稱當且僅當 R∩R-1ÍIA,說明在關系矩陣中除了對角線外,其余位置的值和對應的對稱位置的值不能相等。
②利用兩層循環遍歷矩陣所有位置,當有一次滿足(當前位置不在對角線上&&當前位置的值與對稱位置的值都為1)則返回false,若都沒有滿足則返回true;
///判斷反對稱性
/// R在A上反對稱當且僅當 (R∩R的逆)包含於IA
///
bool InverseSymmtryJudge(int** R) {
for (int i = 0; i < ListA.length(); i++)
for (int j = 0; j < i; j++)if (R[i][j] == 1 && (R[i][j] == R[j][i]) && (i != j))return false;
return true;
}
判斷傳遞性:
①R在A上傳遞當且僅當 R°RÍR,所以需要先求R°R后的矩陣S。
②求完合成后的矩陣S后,則利用兩層循環遍歷S和R兩個矩陣,因為矩陣都是由0和1組成,所以判斷SÍR只需要判斷S的每個值是否大於對應R的值,若大於則返回false,沒有大於的就返回true;
/// 判斷傳遞性
/// R在A上傳遞當且僅當 (R·R)包含於R
bool TransitivityJudge(int** R) {
int n = ListA.length();
InitMatrix(S);
Synthetise(R);//求R合成R,傳R進去,修改的是S
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (S[i][j] > R[i][j]) return false;
}
}
return true;
}
3、求關系的R的逆關系,及關系的合成運算
求關系R的逆關系:
①定義一個R_InverseMatrix用來保存R的逆關系矩陣,兩層循環遍歷R關系矩陣然后把當前位置的值賦值給R_InverseMatrix的對稱位置;
合成運算:
①先兩層循環遍歷,R[i][j]若值為1則再增加依次循環,遍歷R[k][i]。如果R[k][i]值為1則合成后的關系矩陣S[k][j]=1;
4、求關系R的r(R)、s(R)、t(R)(注意關系的傳遞閉包采用Warshall算法)
求自反閉包r(R):
①相當於是求R和恆等關系的並集,所以直接把矩陣對角線的值置為1就行了;
②然后再根據自反閉包矩陣來求出r(R);
對稱閉包s(R):
①如果R中存在<x,y>,且沒有<y,x>的,就把<y,x>添加到R中
②然后再根據對稱閉包矩陣來求出s(R);
傳遞閉包t(R):
①用Warshall算法,考慮 n+1個矩陣的序列M0, M1, …, Mn, 將矩陣 Mk 的 i 行 j 列的元素記作Mk[i,j]. 對於k=0,1,…,n, Mk[i,j]=1當且僅當在R 的關系圖中存在一條從 xi 到 xj 的路徑,並且這條路徑除端點外中間只經過{x1, x2, …, xk}中的頂點. 不難證明M0就是R 的關系矩陣,而 Mn 就對應了R 的傳遞閉包;
②Warshall算法:
輸入:M (R 的關系矩陣)
輸出:Mt (t(R)的關系矩陣)
1. Mt=M
2. for k=1 to n do
3. for i=1 to n do
4. for j=1 to n do
5. Mt[i, j] =Mt[i, j] + Mt[i, k]*Mt[k, j]
5、判斷關系R是否為等價關系,若是等價關系,則求出其所有等價類;
判斷是否為等價關系:
①若關系R同時滿足自反、對稱、傳遞則說明是等價,可以用上面2中的方法直接判斷
求所有等價類:
①若有<x,y>∈R,則說明x,y都是在同一個等價類中。
6、求集合A上的等價關系數
①利用Stirling 數計算公式:
1.S(n, 0) = 0
2.S(n, 1) = 1
3.S(n, 2) = 2^(n − 1) − 1
4.S(n, n − 1) = C(n, 2)
5.S(n, n) = 1
②Stirling 數遞推公式 : S(n, r) = r S(n − 1, r) + S(n − 1, r − 1),使用遞歸算法計算;
③最后集合A的關系數=S(n,1)+S(n,2)+.....+S(n,n);
3.4源程序及測試結果


3.5完整代碼
#include <iostream>
#include <string>
using namespace std;
string ListA;//定義全局集合
string List_Relation,List_InverseRelation;//定義全局集合的關系和逆關系
int** R_Matrix,**R_InverseMatrix;//R_matrix為List_Relation的關系矩陣,R_InverseMatrix為逆關系矩陣,
int** S;//R合成R后的關系矩陣
char** EC;//等價關系R的等價類
//返回字符在集合中的下標
int Get_Index(string List, char ch)
{
int i;
for (i = 0; i < List.length(); i++)if (List[i] == ch)return i;
}
//初始化矩陣
void InitMatrix(int**& M) {
int n = ListA.length();
//動態創建二維數組
M = new int* [n];
for (int i = 0; i < n; i++)
M[i] = new int[n];
//先將矩陣全置為0
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)M[i][j] = 0;
}
//輸入關系並創建關系矩陣
void CreateRelation()
{
int x, y;//定義矩陣中的位置y代表行數,x代表列數
//輸入關系有序偶對 請按照{<1,2>,<2,3>}輸入
cout << "注:請按照{<1,2>,<2,3>}這種格式輸入" << endl;
cout << "請輸入集合A上的關系R=";
cin >> List_Relation;
int n = List_Relation.length();
InitMatrix(R_Matrix);//初始化矩陣
for (int i = 2; i < n; i+=6) {
y = Get_Index(ListA,List_Relation[i]);
x = Get_Index(ListA, List_Relation[i + 2]);
R_Matrix[y][x] = 1;
}
}
//根據List_Relation得到它的逆關系List_InverseRelation和逆矩陣
void GetInverseRelation() {
int n= ListA.length();
List_InverseRelation = List_Relation;
InitMatrix(R_InverseMatrix);
R_InverseMatrix = R_Matrix;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (R_Matrix[i][j] != 0)
R_InverseMatrix[j][i] = R_Matrix[i][j];
}
}
for (int i = 2; i < n; i += 6) {
char temp;
temp = List_InverseRelation[i];
List_InverseRelation[i] = List_InverseRelation[i + 2];
List_InverseRelation[i + 2] = temp;
}
}
//關系合成(只能自己合成自己)
//S是合成后的關系矩陣
void Synthetise(int **R) {
int n = ListA.length();
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (R[i][j] == 1) {
for (int k = 0; k < n; k++) {
if (R[k][i] == 1) S[k][j] = 1;
}
}
}
}
}
//生成R合成R后的關系字符串
string GetSynthetiseStr(){
int n = ListA.length();
string SynthetiseStr;//R合成R后的關系字符串
SynthetiseStr = "{";
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++) {
if (S[i][j] == 1) {
SynthetiseStr = SynthetiseStr + "<" + ListA[i] + "," + ListA[j] + ">,";
}
}
}
SynthetiseStr.erase(SynthetiseStr.length() - 1, 1);
SynthetiseStr += '}';
return SynthetiseStr;
}
//輸出矩陣
void MatrixOut(int **M) {
int n = ListA.length();
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
cout << M[i][j] << " ";
}
cout << endl;
}
}
#pragma region 關系的判斷
///判斷自反性
///List_Relation是自反性說明 List_Relation包含集合A的恆等關系
///所以利用循環判斷每個元素
bool ReflexivityJudge(int** R) {
for (int i = 0; i < ListA.length(); i++)
if (R[i][i] == 0)
return false;
return true;
}
///判斷反自反性
///R在A上反自反當且僅當 R∩IA=空集
///意思就是沒有一個環
bool InverserReflexivityJudge(int** R) {
int n = ListA.length();
for (int i = 0; i < n; i++) {
if (R[i][i] == 1) return false;
}
return true;
}
///判斷對稱性
///List_Relation滿足對稱性,則說明它的關系矩陣一定是對稱矩陣
bool SymmetryJudge(int** R) {
for (int i = 0; i < ListA.length(); i++)
for (int j = 0; j < i; j++)if (R[i][j] != R[j][i])return false;
return true;
}
///判斷反對稱性
/// R在A上反對稱當且僅當 (R∩R的逆)包含於IA
///
bool InverseSymmtryJudge(int** R) {
for (int i = 0; i < ListA.length(); i++)
for (int j = 0; j < i; j++)if (R[i][j] == 1 && (R[i][j] == R[j][i]) && (i != j))return false;
return true;
}
/// 判斷傳遞性
/// R在A上傳遞當且僅當 (R·R)包含於R
bool TransitivityJudge(int** R) {
int n = ListA.length();
InitMatrix(S);
Synthetise(R);//求R合成R,傳R進去,修改的是S
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (S[i][j] > R[i][j]) return false;
}
}
return true;
}
#pragma endregion
#pragma region 求關系的閉包
///自反閉包 相當於是求R和恆等關系的並集 直接把矩陣對角線的值置為1就行了
/// r(R)=R∪R^0
/// R^0=I(A)
void ReflexivityClosure() {
string ReflexivityStr;//用來保存自反閉包的集合字符串
int n = ListA.length();
//int** R;//自反閉包關系矩陣
//R = new int* [n];
//for (int i = 0; i < n; i++)R[i] = new int[n];
//R = R_Matrix;
ReflexivityStr = "{";
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++) {
if (i == j) {
cout << "1 ";
ReflexivityStr = ReflexivityStr + "<" + ListA[i] + "," + ListA[j] + ">,";
continue;
}
else cout << R_Matrix[i][j] << " ";
if (R_Matrix[i][j] == 1) {//矩陣中為1的才有關系,則要保存在集合字符串中
ReflexivityStr = ReflexivityStr + "<" + ListA[i] + "," + ListA[j] + ">,";
}
}
cout << endl;
}
ReflexivityStr.erase(ReflexivityStr.length()-1,1);
ReflexivityStr += '}';
cout << "r(R)="<<ReflexivityStr;
}
///對稱閉包
/// 如果有<x,y>且沒有<y,x> 則添加<y,x>到集合中
/// s(R)=R∪R^-1
void SymmtryClosure() {
string SymmtryStr;//用來保存對稱閉包的集合字符串
int n = ListA.length();
int** R;//對稱閉包關系矩陣
R = new int* [n];
for (int i = 0; i < n; i++)R[i] = new int[n];
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
R[i][j] = R_Matrix[i][j];
SymmtryStr = "{";
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
//關系中如果有<x,y>且沒有<y,x> 則添加<y,x>到集合中
if (R[i][j] == 1 && R[j][i] != 1) {
R[j][i] = 1;
}
}
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
cout << R[i][j] << " ";
if (R[i][j] == 1)
{
SymmtryStr = SymmtryStr + "<" + ListA[i] + "," + ListA[j] + ">,";
}
}
cout << endl;
}
SymmtryStr.erase(SymmtryStr.length() - 1, 1);
SymmtryStr += '}';
cout << "s(R)=" << SymmtryStr;
}
///傳遞閉包(采用Warshall算法)
/// t(R)=R∪R^2∪R^3∪…
void TransitivityClosure() {
string TransitivityStr;//用來保存對稱閉包的集合字符串
int n = ListA.length();
int** R;//對稱閉包關系矩陣
R = new int* [n];
for (int i = 0; i < n; i++)R[i] = new int[n];
for (int i = 0; i < n; i++)
for(int j=0;j<n;j++)
R[i][j] = R_Matrix[i][j];
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
if (R[j][i]==1)
{
for (int k = 0; k < n; k++)
{
R[j][k] = R[j][k] | R[i][k];//邏輯加
}
}
}
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
cout << R[i][j] << " ";
if (R[i][j] == 1)
{
TransitivityStr = TransitivityStr + "<" + ListA[i] + "," + ListA[j] + ">,";
}
}
cout << endl;
}
TransitivityStr.erase(TransitivityStr.length() - 1, 1);
TransitivityStr += '}';
cout << "t(R)=" << TransitivityStr;
}
#pragma endregion
///判斷關系R是否為等價關系
/// 如果R同時滿足自反、對稱、傳遞則是等價
bool EqualJudge(int **R) {
if (ReflexivityJudge(R_Matrix) && SymmetryJudge(R_Matrix) && TransitivityJudge(R_Matrix))
return true;
else
return false;
}
/// 求出所有等價類
/// 如果<x,y>∈R,則說明x,y在同一個等價類
/// 例如:等價關系R={<1,2><2,1><1,3><3,1><2,3><3,2><4,5><5,4>}∪IA
/// 則等價類有兩個{1,2,3},{4,5}
void GetEqualClass(int **R) {
int n = ListA.length();
string A = ListA;
EC = new char*[n];//最大的等價類個數就是元素個數
int Num=0;//等價類個數
int ip;
for (int i = 0; i < n; i++) {
if (A[i]) {
ip = 0;
EC[Num] = new char[n];
EC[Num][ip++] = A[i];
for (int j = i + 1; j < n; j++) {
if (A[i] && R[i][j]) {
EC[Num][ip++] = A[j];
A[j] = 0;
}
}
EC[Num][ip] = '\0';
Num++;
}
}
cout << "等價類有" << Num << "個,分別是";
for (int i = 0; i < Num; i++) {
cout << "{";
for (int j = 0; j < strlen(EC[i]); j++) {
if (j == strlen(EC[i]) - 1)
cout << EC[i][j];
else
cout << EC[i][j] << ",";
}
cout << "} ";
}
}
//計算組合n是下指數,m是上指數
int C(int n, int m) {
int N_Fact = 1;//n的階乘
int M_Fact = 1;//m的階乘
int NSM_Fact = 1;//n-m的階乘
for (int i = 1; i <= n; i++)
N_Fact *= i;
for (int i = 1; i <= m; i++)
M_Fact *= i;
for (int i = 1; i <= n - m; i++)
NSM_Fact *= i;
return N_Fact / (NSM_Fact * M_Fact);
}
///第二類 Stirling 數計算方法
/// 1.Stirling 數計算公式 :
//① S(n, 0) = 0
//② S(n, 1) = 1
//③ S(n, 2) = 2^(n − 1) − 1
//④ S(n, n − 1) = C(n, 2)
//⑤ S(n, n) = 1
//2.Stirling 數遞推公式 : S(n, r) = r S(n − 1, r) + S(n − 1, r − 1)
int Stirling(int n, int m) {
if (m == 0)
return 0;
else if (m == 1)
return 1;
else if (m == 2)
return pow(2, n - 1) - 1;
else if (m == n - 1)
return C(n, 2);
else if (n == m)
return 1;
else
{
return m * Stirling(n - 1, m) + Stirling(n - 1, m - 1);
}
}
/// 求出等價關系數
/// 算等價關系數相當於是算有多少種組合,又因為集合A的等價關系與划分個數是一一對應的,因此求其划分個數即可
/// 在有n個元素的集合里面,有1個元素的划分、2個元素的划分.....到n個元素的划分
/// 最后再把所有划分的個數加起來
/// 等價關系數=S(n,1)+S(n,2)+.....+S(n,n)
void GetEqualClassNum() {
int allNum=0;
int n = ListA.length();
for (int i = 1; i <= n; i++)
allNum += Stirling(n, i);
cout << "集合A的等價關系有" << allNum << "個;";
}
//創建集合
void ListCreat() {
while (true)
{
cout << "請輸入集合A的的元素:";
cin >> ListA;
bool flag;
for (int j = 0; j < ListA.length(); j++) {
flag = true;
for (int k = j + 1; k < ListA.length(); k++) {
if (ListA[j] == ListA[k])
{
flag = false;
break;
}
}
if (!flag)
break;
}
if (!flag) {
cout << "集合中不允許存在相同元素!,請重新輸入!" << endl;;
}
else
{
break;
}
}
}
void main() {
//1、鍵盤輸入集合A中的所有元素,並輸入關系R中的所有序偶對,建立關系R的關系矩陣
cout << "1、鍵盤輸入集合A中的所有元素,並輸入關系R中的所有序偶對,建立關系R的關系矩陣;" << endl;
ListCreat();
CreateRelation();
cout << "關系R的關系矩陣:" << endl;
MatrixOut(R_Matrix);
//2、判斷關系所具有的性質
cout << endl << "2、判斷關系所具有的性質" << endl;
cout << "關系R:" << endl;
cout << "*****************\n";
if (ReflexivityJudge(R_Matrix))
cout << "具有自反性\t*" << endl;
if(InverserReflexivityJudge(R_Matrix))
cout << "具有反自反性\t*" << endl;
if (SymmetryJudge(R_Matrix))
cout << "具有對稱性\t*" << endl;
if (InverseSymmtryJudge(R_Matrix))
cout << "具有反對稱性\t*" << endl;
if (TransitivityJudge(R_Matrix))
cout << "具有傳遞性\t*" << endl;
cout << "*****************\n";
//3、求關系R的逆關系,及關系的合成運算;
cout << endl << "3、求關系R的逆關系,及關系的合成運算" << endl;
GetInverseRelation();
cout << "關系R的逆關系=" << List_InverseRelation << endl;
cout << "關系R的逆關系矩陣:" << endl;
MatrixOut(R_InverseMatrix);
cout << "R·R(R合成R)后的關系=" << GetSynthetiseStr() << endl;
cout << "R·R(R合成R)后的關系矩陣:" << endl;
MatrixOut(S);
//4、求關系R的r(R)、s(R)、t(R)
cout << endl << "4、求關系R的r(R)、s(R)、t(R)" << endl;
cout << "自反閉包矩陣:" << endl;
ReflexivityClosure();
cout << endl;
cout << "對稱閉包矩陣:" << endl;
SymmtryClosure();
cout << endl;
cout << "傳遞閉包矩陣:" << endl;
TransitivityClosure();
cout << endl;
//5、判斷關系R是否為等價關系,若是等價關系,則求出其所有等價類
cout <<endl<< "5、判斷關系R是否為等價關系,若是等價關系,則求出其所有等價類" << endl;
cout << "關系R";
if (EqualJudge(R_Matrix)) {
cout << "是等價關系\n";
GetEqualClass(R_Matrix);
}
else
cout << "不是等價關系";
cout << endl;
//6.求集合A上的等價關系數
cout << endl << "6.求集合A上的等價關系數" << endl;
GetEqualClassNum();
}
①關系R是自反性說明 R包含集合A的恆等關系;
②所以根據在1建立的關系矩陣來判斷,利用一層循環判斷對角線的值是否為1,若對角線上的值有一個為0返回false,都為1的話最后返回true;
判斷反自反性:
①關系R是反自反性說明在關系矩陣中對角線的值都為0;
②所以還是根據在1建立的關系矩陣來判斷,還是利用一層循環判斷對角線的值是否為1,若對角線上的值有一個為1返回false,都為0的話最后返回true;
判斷對稱性:
①關系R是對稱性則說明它的關系矩陣一定是對稱矩陣;
②所以利用兩層循環遍歷矩陣所有位置,並每次判斷當前位置的值與他對稱位置的值是否相等(R[i][j]==R[j][i]),若有一次不相等返回false,全部相等則返回true;
