文章目录
一、最小函数依赖集
如果函数依赖集F满足下列条件,则称F为最小函数依赖集或最小覆盖。
① F中的任何一个函数依赖的右部仅含有一个属性;
② F中不存在这样一个函数依赖X→A,使得F与F-{X→A}等价;
③ F中不存在这样一个函数依赖X→A,X有真子集Z使得F-{X→A}∪{Z→A}与F等价。
定理及原理
二、C++代码实现求最小函数依赖集
思路与步骤
分三步:
步骤一:拆分
检查函数依赖:把每个右边项有k元属性组的式子改为k个单元函数
例:A->BC 应该拆分为为 A->B 和 A->C
步骤二:去掉重复的传递函数依赖
逐一检查F中的各函数依赖FDi:X->A,令G = F - {X->A},若A属于XG+,则从F中去掉此函数依赖
例:B->A,B->C, C->A,其中“B->A”可由“B->C和C->A”推出,因此B->A是多余的,应去掉
步骤三:逐一检查每个函数依赖来去掉部分函数依赖
逐一取出F中各函数依赖FDi:X->A,设X = B1 B2 … Bm,m≥2,逐一考查Bi(i=1,2,…,m),
若A属于(X-Bi)F+,则以X-Bi取代X。[即A和Bi无关]
例:C->A,函数依赖ACD->B中的属性A是多余的,去掉A得CD→B
C++代码
//求函数依赖集F的最小依赖集
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
//计算函数依赖右部(箭头右边)的属性个数
int rightCount(string str) {
int i = str.find('>'); //记录箭头的位置
return str.size() - 1 - i; //返回箭头右边属性个数
}
//计算函数依赖左部(箭头左边)的属性个数
int leftCount(string str) {
int i = str.find('-'); //记录箭头的位置
return i; //返回箭头左边属性个数
}
//步骤一:拆分
//检查函数依赖:把每个右边项有k元属性组的式子改为k个单元函数
//例:A->BC 应该拆分为为 A->B 和 A->C
vector<string> step1(const vector<string> &F) {
vector<string> F0 = F;
//从后往前遍历,这样删除函数依赖时不会影响到遍历的下标i所对应的元素
for(int i = F.size() - 1; i >= 0; i--) {
if(rightCount(F[i]) > 1) { //如果右部的属性大于一个
string leftStr = F[i].substr(0,leftCount(F[i])); //截取箭头左边的属性
int k = F[i].size() - 1;
//将拆开后的函数依赖放入集合F中
for(int j = 0; j < rightCount(F[i]); j++) {\
string str_temp = leftStr;
str_temp += "->";
str_temp += F[i][k--];
F0.push_back(str_temp);
}
//将原来的函数依赖去除掉
//创建迭代器并指向改函数依赖在集合中的位置
auto iter = F0.begin();
iter += i;
F0.erase(iter);
}
}
return F0;
}
//检查XG中是否包含substr
bool judge(string sub_str, string XG) {
for(int i = 0; i < sub_str.size(); i++) {
if(XG.find(sub_str[i]) == XG.npos) return false;
}
return true;
}
//步骤二:去掉重复的传递函数依赖
//逐一检查F中的各函数依赖FDi:X->A,令G = F - {X->A},若A属于XG+,则从F中去掉此函数依赖
//例:B->A,B->C, C->A,其中“B->A”可由“B->C和C->A”推出,因此B->A是多余的,应去掉
vector<string> step2(vector<string> &F) {
//从后往前遍历,这样删除函数依赖时不会影响到遍历的下标i所对应的元素
for(int i = F.size() - 1; i >= 0; i--) {
string X = F[i].substr(0,leftCount(F[i])); //截取箭头左边的属性
vector<string> G = F;
//迭代器指向当前遍历的函数依赖
auto iter = G.begin();
iter += i;
G.erase(iter); //G = F - {X->A}
string XG = X;
for(int k = 0; k < G.size(); k++) {
if(judge(G[k].substr(0, leftCount(G[k])), XG) && XG.find(G[k][G[k].size() - 1]) == XG.npos) {
XG += G[k][G[k].size() - 1];
k = -1;
}
}
if(XG.find(F[i][F[i].size() - 1]) != XG.npos) {
//更新F
auto iter2 = F.begin();
iter2 += i;
F.erase(iter2);
}
}
return F;
}
//步骤三:逐一检查每个函数依赖来去掉部分函数依赖
//逐一取出F中各函数依赖FDi:X->A,设X = B1 B2 … Bm,m≥2,逐一考查Bi(i=1,2,…,m),若A属于(X-Bi)F+,则以X-Bi取代X。[即A和Bi无关]
//例:C->A,函数依赖ACD->B中的属性A是多余的,去掉A得CD→B
vector<string> step3(vector<string> &F) {
for(int i = 0; i < F.size(); i++) {
if(leftCount(F[i]) == 1) {
char left = F[i][0];
char right = F[i][F[i].size() - 1];
for(int j = 0; j < F.size(); j++) {
//只需要检查左部不是单个属性的函数依赖
if(leftCount(F[j]) > 1) {
//截取左边的属性
string str = F[j].substr(0, leftCount(F[j]));
if(str.find(left) != str.npos && str.find(right) != str.npos)
F[j].erase(F[j].find(right), 1);
}
}
}
}
return F;
}
//求函数依赖集F的最小依赖集
vector<string> Minimum_Dependency_Set(vector<string> &F) {
//依次执行三个步骤
F = step1(F);
F = step2(F);
F = step3(F);
return F;
}
//打印函数依赖集
void show(const vector<string> &F) {
for(int i = 0; i <F.size(); i++) {
if(i == F.size() - 1)
cout << F[i];
else
cout << F[i] << ",";
}
}
int main()
{
vector<string> F; //函数依赖集
cout << "输入函数依赖集F(箭头采用“->”形式,例:A->B;输入#号终止输入)" << endl;
cout << "------请输入:" ;
string str;
while(cin >> str) {
if(str == "#") break;
F.push_back(str);
}
cout << endl;
//打印原始函数依赖集
cout << "原始函数依赖集F = {";
show(F);
cout << "}" << endl;
F = Minimum_Dependency_Set(F);
cout << endl;
//打印最小函数依赖集
cout << "最小函数依赖集F(min) = {";
show(F);
cout << "}" << endl;
return 0;
}