学习离散数学的时候遇到了这个算法,此算法用于计算闭包矩阵(closure),其复杂度较普通的直接合并的方法要简单,为O(n^3),以下简单介绍一下这个算法~
(以下介绍传递闭包矩阵,其它类型的矩阵与传递闭包思想一致,且相对简单,大家可以自己写写)
1. 思想
2. 代码实现
i) 我们最初知道的仅仅是集合之间元素的关系,那么我们需要知道这个集合内有几个元素,并将它们赋值。
cout<<"The number of element in your set: ";
int size;
cin>>size;
int mySet[size];
for (int i=0; i<size; i++) {
cout<<i+1<<". ";
cin>>mySet[i];
}
这个是非常简单的,就不作解释啦。
ii) 接下来,我们通过集合内元素的关系(relation)来生成关系矩阵,它是n*n的。
首先,我们需要user来告诉我们集合内元素的关系,我们需要定义一种表示关系的符号:
以下, 一组关系内部的两个元素用逗号(",")分隔, 关系之间用冒号(":")分隔。描述结束用q来标志结束。
如: 1,3 : 2,5 :3,2: 3,1 q
表示1到3为一组关系,2到5为一组关系(注意3到1与1到3是不同的关系),以此类推。
这是笔者自己定义的,你们可以自己定义。
这里还需要注意和说明几点:
a) 当user输入的关系中的某个元素是原来集合中没有的元素,我们就需要舍弃这组关系。
b) 这里是用new来声明二维数组,原因在于后面将要使用的函数,在后面会再提到。
bool **mat=new bool *[size];
for (int i=0; i<size; i++) {
mat[i]=new bool[size];
}
c) 这里是用布尔数组,原因是这是一个0-1数组,后面在运算时也将有所简化。
下面是具体实现:
bool **mat=new bool *[size];
for (int i=0; i<size; i++) {
mat[i]=new bool[size];
}
//preset the matrix to zero
for (int i=0; i<size; i++) {
for (int j=0; j<size; j++)
mat[i][j]=0;
}
//transfer relation to relation matrix
int temp;
char ch=0;
int li = 0,ri = 0;//to store the left value and right value in a relation
bool leftGood=false,rightGood=false,leftTurn=true;//good: the element exist;turn for left value to store->true
int index;//the index of the vl;
do {
cin>>temp;
if (cin.std::__1::ios_base::good()) {
if ((index=returnVl(temp, size, mySet))!=-1) {//if the value exist
if (leftTurn) {
li=index;
leftTurn=false;
leftGood=true;
}else if(leftGood){
ri=index;
rightGood=true;
leftTurn=true;
}
}else
{
if (leftTurn) {
leftGood=false;
}else
{
rightGood=false;
}
}
}else
{
cin.clear();
ch=cin.get();
if (ch!=',') {
if (leftGood&&rightGood) {
mat[li][ri]=1;
leftGood=rightGood=false;
}
leftTurn=true;
}
}
} while (ch!='q');
用到的检查是否含有该元素的函数定义(有则返回其index,无则返回-1,因为index不可能是-1):
int returnVl(int vl,int size,int *arr)
{
int i=0;
for (i=0; i<size&&vl!=arr[i]; i++) ;
if((i!=size)||(arr[size-1]==vl))
{
return i;
}else
return -1;
}
iii) 接下来我们要计算每一个warshell 矩阵。
复杂度:
这样,我们声明一个函数,来计算Wn.
void wsMat(bool **arr,int size,int n)//to acquire the nth warshall matrix
{
for (int k=0; k<n; k++) {
for (int i=0; i<size; i++) {
for (int j=0; j<size; j++) {
// if (arr[i][j]==1||(arr[i][k]==1&&arr[k][j]==1)) {
// arr[i][j]=1;
// }
arr[i][j]=arr[i][j]+arr[i][k]*arr[k][j];
}
}
}
}
注释部分为原来的版本,因为是bool数组,修改为现有版本后变得尤为简洁。
现在来解释为什么前面要用new来声明数组,因为我们本可以使用
bool mat[size][size];
来声明。但是,在这里size作为一个variable,导致mat的类型不是bool **,所以在后面作为函数传入时IDE提示no matching function. 所以只能使用前面的方法。(这里笔者还要研究在深入一些)
iv) 输出结果
这里笔者加了3,6这组关系来测试是否会无视这对关系。
总结:
Warshell's Algorithms 的应用主要在于寻找最短路径。