由于数据库作业要求用程序求一个函数依赖集中属性集的闭包和此依赖集的闭包,便用c++写了这个程序,刚好在这分享给大家,代码写得丑,望大家勿喷。
首先我们将求闭包具体化,即建立在下面的规则下:
1. 由用户输入函数依赖,当用户输入End时,表示所有依赖都输入完毕。(即函数依赖是由用户自己定的,程序中不能假定某个具体的依赖)。
2. 函数依赖的形式是AB-->C, A-->BE这样的形式,为了简单起见,我们假定所有的属性都是用英文的大写字母表示,由A到Z。(提示,你可以让用户先输入依赖左边的属性, 然后再输入依赖右边的属性,用来表示A-->B这样的形式)
3. 用户输入完毕所有的依赖后,显示“请输入属性集求闭包”的提示,当用户输入1个或者多个属性时,求出对应的闭包。(如,用户输入A,则显示A+的值, 用户输入AB则求 出AB+的值。显示完毕后,再次显示“请输入属性集求闭包”,让用户继续输入属性,直到用户输入END后程序结束。
这样,在上述规则的约束下,我们便可以将求闭包的算法具体化了。
下面我先简述一下此算法的规则:
在此算法中,我们会首先求出各个单一属性的闭包,这样的话,我们就得到了两组属性(泛化地说),将左边看成一组,将右边看成另一组,然后对左边的属性集进行排列组合,产生所有的可能情况,然后对左边的每个组合相对应的右边的属性的集合也进行排列组合,最后得出所有的函数依赖,也就是要求的函数依赖集的闭包。
当然,这样说的话可能会有点难懂,下面我用一个简短的例子进行解说:
例:A->B,B->C,求此函数依赖集的闭包
解:(1)首先求出各个单一属性的闭包:(A)+ = ABC,(B)+ = BC,(C)+ = C,其中(A)+表示属性A的闭包。
(2)求出左边属性的排列组合并求出相对应右边属性的集合
A:ABC, B:BC, C:C, AB:ABC, AC:ABC, BC:BC, ABC:ABC
(3)对(2)中求出的组合中,再对右边的属性集合进行排列组合
A:A,B,C,AB,BC,AC,ABC
B:B,C,BC
C:C
A,B,C,AB,BC,AC,ABC
A,B,C,AB,BC,AC,ABC
BC:B,C,BC
A,B,C,AB,BC,AC,ABC
(4)只需要将(3)中的结果输出就好
例如左边为B时,得到的是B->B,B->C,B->BC(在我们的程序中是将B->NULL这种类型保留了的,当然你也可以根据自己的需求选择需不需要输出这个)
闲话不多说了,下面直接上代码:
#include<iostream>
#include<vector>
#include<string>
#include<set>
#include<algorithm>
using namespace std;
//定义一个结构体,用于表示函数依赖
struct Node
{
string m_left; //存放函数依赖的左边属性集
string m_right; //用于存放函数依赖集的右边属性集
Node(const string &left,const string &right):m_left(left),m_right(right){}
};
//定义一个函数依赖集的类
class FunSet
{
public:
FunSet(vector<Node> s=vector<Node>()):m_set(s){}
void pushFun(const Node &n)
{
m_set.push_back(n);
}
string attrClosure(const string &attr);
void closure();
bool contains(const string &attrSet,const string attr);
private:
vector<Node> m_set; //用于存放所有的函数依赖的集合
void print(string attrs,int n,int sum,const string &leftAttrs);
void calculateAttrs(vector<pair<char,string> > attrClosures,int n);
};
/*
此函数是递归函数,用于将左边属性对应的右边属性集中的所有可能情况输出
*/
void FunSet::print(string attrs,int n,int sum,const string &leftAttrs)
{
if(n>=sum)
{
cout << leftAttrs << "->";
int num=0;
for(int i=0;i<n;i++)
{
if(attrs[i]!=NULL)
cout << attrs[i];
else
num++;
}
if(num==n)
cout << "NULL";
cout << endl;
return ;
}
print(attrs,n+1,sum,leftAttrs); //此时是attrs中下标为n处的值为真的属性
attrs[n]=NULL; //将attrs属性集中下标为n处的属性置为NULL
print(attrs,n+1,sum,leftAttrs);
}
/*
此递归函数用于将左边属性集的所有组合情况找出来
利用的规则是分别找出左边属性集的所有可能排列,在找出相对应的右边属性集的所有可能排列
此过程中用到了二进制位的思想,即将每个属性为看成要么选中,要么不选,不选时置为NULL,然后进行递归
*/
void FunSet::calculateAttrs(vector<pair<char,string> > attrClosures,int n)
{
if(n>=attrClosures.size())
{
int count=0; //用于记录左边属性集中NULL的个数
string leftAttrs; //用于存放左边的属性
string rightAttrs; //用于存放左边属性的闭包
for(int i=0;i<n;i++)
{
if(attrClosures[i].first!=NULL)
leftAttrs+=attrClosures[i].first;
else
count++;
rightAttrs+=attrClosures[i].second;
}
if(count==n)
return ;
//下面三行代码用于取出rightAttrs中重复的属性
sort(rightAttrs.begin(),rightAttrs.end());
auto end_unique=unique(rightAttrs.begin(),rightAttrs.end());
rightAttrs.erase(end_unique,rightAttrs.end());
int num=rightAttrs.size();
print(rightAttrs,0,num,leftAttrs);
return ;
}
calculateAttrs(attrClosures,n+1);
attrClosures[n].first=NULL;
attrClosures[n].second=string();
calculateAttrs(attrClosures,n+1);
}
/*
此函数用于判断attr中的所有字符是否出现在attrSet中,如果是则返回true,否则返回false
*/
bool FunSet::contains(const string &attrSet,const string attr)
{
bool result=true;
for(int i=0;i<attr.size();i++)
{
int num=attrSet.find(attr[i]);
if(num<0)
{
result=false;
break;
}
}
return result;
}
/*
此函数用于求属性集的闭包
*/
string FunSet::attrClosure(const string &attr)
{
int size=-1;
string result=attr; //将要求的属性放在结果集中
while(attr.size()!=size) //当发现属性的闭包不会再改变时,就说明已经求得结果了
{
size=result.size();
for(int i=0;i<m_set.size();i++)
{
bool flag=contains(result,m_set[i].m_left); //判断函数依赖的左边是否在求得的闭包中
if(flag) //若在,则更新闭包,否则判断下一个
result=result+m_set[i].m_right;
}
}
sort(result.begin(),result.end());
auto end_unique=unique(result.begin(),result.end());
result.erase(end_unique,result.end());
return result;
}
/*
此函数用于求函数依赖集的闭包
*/
void FunSet::closure()
{
set<char> attrsSet; //利用set集合中元素不会重复的规则过滤属性集中重复的属性
//下面的for循环是遍历所有函数依赖中左边属性和右边属性以得到所有属性的一个集合
for(int i=0;i<m_set.size();i++)
{
int index=m_set[i].m_left.size();
for(int j=0;j<index;j++)
{
attrsSet.insert(m_set[i].m_left[j]);
}
index=m_set[i].m_right.size();
for(int j=0;j<index;j++)
{
attrsSet.insert(m_set[i].m_right[j]);
}
}
vector<pair<char,string> > attrClosures; //attrClosures用来存放各个单一属性对应的闭包
for(auto it=attrsSet.begin();it!=attrsSet.end();++it)
{
string attr;
attr=attr+*it;
attrClosures.push_back(pair<char,string>(*it,attrClosure(attr)));
}
calculateAttrs(attrClosures,0);
}
int main()
{
string left,right;
FunSet funset;
cout << "输入函数依赖时,函数依赖的左边和右边用空格隔开,输入END结束" << endl;
while(true)
{
cout << "请输入:" ;
cin >> left;
if(left=="END")
break;
else
cin >> right;
funset.pushFun(Node(left,right));
}
string attrs;
int choice;
while(true)
{
cout << "请输入属性集求闭包:" << endl;
cin >> attrs;
cout << attrs << "+:"<< endl;
cout << funset.attrClosure(attrs) << endl;
cout << "是否继续求属性集的闭包? 输入1代表继续,输入0待表结束" << endl;
cout << "请输入您的选择:";
while(true)
{
cin >> choice;
if(choice<0||choice>1)
{
cout << "**********您的输入有误!*******" << endl;
cout << "请重新输入:";
}
else
break;
}
if(choice==0)
break;
}
cout << "您输入的函数依赖的闭包F+是:" << endl;
funset.closure();
return 0;
}