1. 程式人生 > >關係規範化之函式依賴集閉包和屬性集X對於函式依賴集F的閉包

關係規範化之函式依賴集閉包和屬性集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+中的元素也可

能很多。此外,閉包F+中也存在許多冗餘資訊。其實,判斷一個函式依賴X->Y是否在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;
}