關係規範化之函式依賴集閉包和屬性集X對於函式依賴集F的閉包
最近在學資料庫原理,關係規範化中介紹了幾個演算法,最基礎也最重要的就是求屬性集X關於某個函式依賴集合F的閉包。
/*8周的功夫一本資料庫基本原理就學完了,很快要考試了,所以5-1假期也沒打算出去玩,就在學校了複習、休息等等。就在複習的過程中,突然發現書上對於函式依賴集合的閉包,以及屬性集合的閉包的區別,幾乎沒有介紹。我看了好幾遍才看懂,所以特此補充,以便同我有相同疑問的朋友理解。*/
/*這是在2016年五一勞動節修改的,哈哈,我是中國好程式設計師,勞動節就勞動得度過[]~( ̄▽ ̄)~*。之前求閉包沒有采用遞迴的方式,過程十分繁瑣,不值得推薦,所以刪去的之前的那段程式碼。採用遞迴,更加貼近定義,思路更加清晰。此外,精簡優化了一下程式碼,刪除了一些不必要的函式,或者轉換更好的思路,程式碼量得到了精簡。*/
首先說一下,函式依賴集的閉包
----------------------------------------------------分割線-------------------------------------------------------------------------------------------
函式依賴的閉包
定義:若F為關係模式R(U)的函式依賴集,我們把F以及所有被F邏輯蘊涵的函式依賴的集合稱為F的閉包,記為F+。
什麼是“被F邏輯蘊涵"呢?
函式依賴的邏輯蘊涵:
定義:設有關係模式R(U)及其函式依賴集F,如果對於R的任一個滿足F的關係r函式依賴X→Y都成立,則稱F邏輯蘊涵X→Y,或稱X→Y可以由F推出。
例:關係模式 R=(A,B,C),函式依賴集F={A→B,B→C}, F邏輯蘊涵A→C
所以說:函式依賴集F的閉包,裡面的元素還是函式依賴(就是X→Y這種東西),函式依賴集F的閉包包括F自身,加上能從F中通過Armstong公理推匯出的,新的函式依賴。
即:F+={X→Y|X→Y∈F∨“應用Armstong公理從F中匯出的任何X→Y”}
為了判斷函式依賴X->Y是否在F+中,只要計算出F+即可。因為F+是由F根據Armstrong公理匯出的函式依賴的集合。因此,原則上說,只要按照Armstrong公理系統中的推理規則就可以計算出F+。但是,閉包F+的計算是一件很麻煩的事情,因為計算F+的問題是一個NP完全問題,即若F={X->A1, X->A2, …, X->An,},則需要計算F+的O(2n)個函式依賴,因此,當
n比較大時,實際計算F+是不可行的。即使F的元素不多時, F+中的元素也可
一個重要的定理:X->Y屬於F+,等價於Y屬於X關於F的閉包。X,Y都是F中的屬性。
那什麼是屬性X關於F的閉包呢?
----------------------------------------------------分割線-------------------------------------------------------------------------------------------
2.屬性集X(X∈U)對於U上的一個函式依賴集F的閉包X_F^+
(1)定義: 設關係模式R(U,F ),U 為其屬性集,F 為其函式依賴集,則稱在所有用Armstrong 公理從F 推出的函式依賴X → Ai 中,Ai 的屬性集合為X 的屬性閉包。
也就是說,屬性集X關於函式依賴集合的閉包,裡面的元素是屬性(而不是函式依賴),這些屬性是F根據Armstrong 公理推匯出來的。
好吧為了充分理解,補充一下什麼是Armstrong 公理:
Armstrong公理
1、定理:若U為關係模式R的屬性全集,F為U上的一組函式依賴,設X、Y、Z、W均為R的子集,對R(U,F)有:
F1(自反性):若X≥Y(表X包含Y),則X→Y為F所蘊涵;(F1':X→X)
F2(增廣性): 若X→Y為F所蘊涵,則XZ→YZ為F所蘊涵;(F2':XZ→Y)
F3(傳遞性): 若X→Y,Y→Z為F所蘊涵,則X→Z為F所蘊涵;
F4(偽增性):若X→Y,W≥Z(表W包含Z)為F所蘊涵,則XW→YZ為F所蘊涵;
F5(偽傳性): 若X→Y,YW→Z為F所蘊涵, 則XW→Z為F所蘊涵;
F6(合成性): 若X→Y,X→Z為F所蘊涵,則X→YZ為F所蘊涵;
F7(分解性): 若X→Y,Z≤Y (表Z包含於Y)為F所蘊涵,則X→Z為F所蘊涵。
函式依賴推理規則F1∽F7都是正確的。
2、Armstrong公理:
推理規則F1、F2、F3合稱Armstrong公理;
F4 ∽ F7可由F1、F2、F3推得,是Armstrong公理的推論部分。
(2)計算: 求屬性集X 關於函式依賴F 的屬性閉包X+。設關係模式R 的全部屬性集為U,在U 上的函式依賴集為F,U 的一個子集為X,計算X 關於F 的屬性閉包X+ 。
具體方法如下:
①置初始X(0)= Ф,X(1)=X;
②如果X(0)≠ X(1),置X(0)=X(1),否則轉④;
③對F 中的每一個函式依賴Y → Z,若Y X(1),置X(1)=X(1)∪ Z,轉②;
④結束,即X(1)為X+。
下面我們藉助一個例子來說明屬性閉包的計算過程。
例1: 設有關係模式R(U,F),其中U={A,B,C,D,E},F={ AB → C,B → D,C → E,CE → B,AC → B},計算(AB)+。
解: 由上述演算法得:
第一次: ① X(0)= Ф,X(1)=AB;
②由於X(0)≠ AB,置X(0)=AB;
③搜尋F中的每一個函式依賴,得AB →C與B→D為AB,B X(1),置X(1)=AB ∪ C ∪ D=ABCD;
第二次: ② X(0)≠ ABCD,則X(0)=ABCD;
③找到C → E 與AC → B,置X(1)=ABCD ∪ E ∪ B=ABCDE;
第三次: ② X(0)≠ ABCDE,則X(0)=ABCDE;
③找到CE → B,置X(1)=ABCDE ∪ B=ABCDE;
第四次: ② X(0)=X(1),轉④;
④結束,即X(1)=(AB)+=ABCDE。
為了簡便,將各個屬性用字元表示,程式如下:
//求屬性集X(X∈U)對於U上的一個函式依賴集F的閉包X_F^+
//輸入:屬性全集U,U上的函式依賴F,屬性集X (X∈U)
//輸出:X關於F的閉包 X_F^+
#include <iostream>
#include <string>
using namespace std;
struct FunctionDependence//函式依賴
{
string X;//決定因素
string Y;
};
void Init (FunctionDependence FD[],int n)
{
//函式依賴關係初始化
int i;
string x,y;
cout<<"請輸入F中的函式依賴(決定因素在左,被決定因素在右)"<<endl;
//輸入函式依賴集合F
for (i=0;i<n;i++)
{
cin>>x>>y;
FD[i].X=x;
FD[i].Y=y;
}
cout<<"函式依賴集合F:";
cout<<"F={" ;
for (i=0;i<n;i++)
{
//顯示已知的函式依賴集合F
cout<<FD[i].X<<"->"<<FD[i].Y;
if (i<n-1)cout<<", ";
}
cout<<"}"<<endl;
}
bool IsIn(string f,string zz)//能夠判斷F中決定因素f裡所有的因素是否在X中,但這樣可能導致結果出現重複
{
bool flag1=false;
int len1=f.length();
int len2=zz.length();
int k=0,t=0,count1=0;
for (k=0;k<len1;k++)
{
for (t=0;t<len2;t++)
{
if (f[k]==zz[t])
{
//flag1=true;break;
count1++;
}
}
}
if (count1==len1)
{
flag1=true;
}
else flag1=false;
return flag1;
}
string CutAndSort(string mm)//將最終得到的閉包去除其中重複的元素,並且進行排序
{
int size=mm.length();
int kk=0,ii=0;;
string closure;
int a[200]={0};//用來記錄各個命題出現的次數
for(kk=0;kk<size;kk++)
{
a[(int)mm[kk]]++;//強制轉換型別,儲存各個因素的次數
}
for (ii=0;ii<200;ii++)
{
if (a[ii]>=1)//cout<<(char)ii;
closure+=(char)ii;
}
return closure;
}
string X_Fn(FunctionDependence FD[],int n,string &xx)
{
string yy=xx;
for (int i=0;i<n;i++)
{
if (IsIn(FD[i].X,yy)==true)
{
xx+=FD[i].Y;
}
}
yy=CutAndSort(yy);
xx=CutAndSort(xx);
//cout<<"yy="<<yy;
//cout<<" xx="<<xx<<endl;
if (xx!=yy)
{
X_Fn(FD,n,xx);//遞迴
}
return xx;
}
void FD(FunctionDependence FD[],int n)
{
//輸入 X
string xx;
int i;
cout<<"\n請輸入屬性集X:";
cin>> xx;
cout<<"\n"<<xx<<"關於F的閉包:{";
//求X關於F的閉包
cout<<X_Fn(FD,n,xx);
cout<<"}\n";
}
int main()
{
int N;
cout<<"請輸入F中函式依賴的組數:";
cin>>N;
FunctionDependence fd[N];
Init(fd,N);
FD(fd,N);
FD(fd,N);
FD(fd,N);
return 0;
}