任務
數據庫關係模式中函數依賴的理論涉及不少算法,此次根據課程需要將求屬性閉包(closure)和函數最小依賴集(basis)用代碼實現。
思路
兩個算法已有理論支撐,因此第一步是設計合適的數據結構,存儲算法過程需要的變量,核心如下:
- 屬性
- 函數依賴集
- 閉包集
- 最小函數依賴集
數據結構
全部用數組實現存儲
//屬性用數組儲存(存在置爲1)
int attributes[MAXSIZE];
//儲存依賴某一項的左部X,對應右部Y,有X->Y
int leftDependencies[MAXNUM][MAXSIZE];
//儲存依賴某一項的右部Y,對應左部,有X->Y
int rightDependencies[MAXNUM][MAXSIZE];
//儲存求得某函數依賴集關於某屬性集的閉包結果集
int closure[MAXSIZE];
//存儲獲取最小依賴集
int basisDependencies[MAXNUM];
//存儲設定依賴項以便得到所有可能的最小依賴集
int controlDependencies[MAXNUM];
//存儲可被刪去的左部單個屬性
int maskAttributes[MAXNUM][MAXSIZE];
考慮到這些數據需要頻繁調用,爲減少傳參麻煩,我採用C++ 創建對象 form 存儲變量與函數。
基本數據操作
爲實現靈活處理,通過命令行顯示菜單能夠實現:
i.initAttributes //初始化屬性個數
a.addDependency //新增函數依賴
s.setClosure //設定求屬性閉包所需的初始屬性
f.findClosure //尋找閉包
p.printInfo //打印基本信息
b.basisDependencies //打印所有的函數最小依賴集
算法實現
求屬性閉包
算法如下(課講來源,哈爾濱工業大學慕課 數據庫系統(中)戰德臣 教授):
通過觀察例子能更直觀的瞭解算法實現:
直白描述
核心:屬性閉包需要兩個必不可少的成分——屬性集和函數依賴集。
含義:屬性閉包是一個屬性集通過一個函數依賴集能導出的所有屬性(包括這個屬性集在內)
我們的算法這樣進行:
基礎:給定一個屬性集X和一組函數依賴F
將給定的屬性集直接添加到閉包內,(直接通過setClosure()
實現)
遍歷存儲函數依賴的左部leftDependencies
,如果某一個左部的所有屬性被提供的屬性集所覆蓋isConsistent == true
,說明右部的屬性可以被添加至閉包
遞歸 調用函數,直至無法增添新的屬性,返回。
主體函數
void form::findClosure()
{
bool isConsistent = true;
for (int i = 0; i < dependenciesNum;i++)
{
isConsistent = true;
for (int j = 0; j <attributesNum; j++)
{
if (leftDependencies[i][j]== 1 && closure[j] != 1)
{
isConsistent =false;
break;
}
}
if (isConsistent == true)
{
for (int j = 0; j <attributesNum; j++)
{
if(rightDependencies[i][j] == 1 && closure[j] != 1)
{
cout<< "add ";
printAttribute(j);
cout<< endl;
closure[j]= 1;
findClosure();
}
}
}
}
return;
}
求函數的最小依賴
相比於屬性閉包容易操作的算法,求函數的最小依賴很難通過如下算法進行設計:
最終通過這篇博文中提供的例子找到方法
直白描述
歸納出的算法如下:
對於提供的函數依賴集,簡化處理過程,輸入時直接按照分解律把依賴X->A1A2...An
分解爲X->A1
、 X->A2
、… 、X->An
一:
將一個函數依賴X->Y
除去,將其左部屬性X
傳遞到閉包初始集closure
中,求解左部屬性關於除去此依賴後的閉包結果closure
(本函數結果集與初始集是一個數組)中是否存在右部屬性Y
,若存在,證明可除去此依賴。
遍厲所有的函數依賴,則得到第一步處理的結果
//依次刪除一個依賴X'->Y',尋找新依賴集中能否推出Y'
//若能,則可被刪除;否則不能被刪除
//判斷 closure 輸出結果是否Y'爲1
由於遍歷的次序會導致不同的函數依賴結果,我通過設計算法得到所有的可能順序的結果,具體算法爲:
- 首先得到的一個最小函數依賴集, 結果儲存在
basisDependencies
中,0代表函數依賴可除去,例如{0,1,0,1,1}表示第一和第三個函數依賴可除去- 我們將最後一個可除去的依賴(
basisDependencies[j] == 0
),如例中的第三個,通過控制數組controlDependencies
將代表其位置的值置爲-1(意味不可再被尋找最小函數依賴集時除去),此時再重新尋找最小函數依賴集- 重複上述操作直至最後一個可除去的依賴已經是最後一個位置(沒有必要再將
controlDependencies
置爲-1 ,我們轉而操作倒數第二個可除去的依賴,如例中的第一個,將其置爲-1,再如第2步重新尋找最小函數依賴集- 重複上述操作直至不存在倒數第二個可除去的依賴,此時獲得所有可能的結果.
二:
上一步得到消除了 冗餘函數依賴的所有結果, 如下圖所示
但對於下面的例子得出的結果能看出還存在問題:
將上個例子中函數依賴中左部的A
替換爲AB
得到的結果如下所示:
因此下一步需要除去左部中屬性的冗餘,思路如下:
// 依次取上一步完成找到的basisDependency的左端的屬性
// 若刪去一個屬性後
// 能通過除該依賴以外剩下的依賴得到這個屬性,則可刪去
// 通過maskAttributes[][]實現
// 若maskAttributes[i][j] = -1(可被刪去)
// 則該屬性被刪去(不被計算閉包時考慮)
// 若maskAttributes[i][j] = 1(不可被刪去,被處理後證明)
// 若maskAttributes[i][j] = 0(初始值)
主體函數
void form::findBasis()
{
//依次刪除一個依賴X'->Y',尋找新依賴集中能否推出Y'
//若能,則可被刪除;否則不能被刪除
//需要函數:獲取一個依賴集所有左邊attributes
//closure 輸出結果是否含有Y'
bool basisFound = false;
initbasisDependencies();
for (int i = 0; i < dependenciesNum; i++)
{
int targetValue = 0;
//必須在可以除去的依賴中
if (controlDependencies[i] != 1)
{
continue;
}
//假設除去該依賴,檢查結果
basisDependencies[i] = 0;
targetValue = getRightDependency(i);
sendToClosure(i);//將除刪除的依賴的左邊的X'傳給closure
findClosure(targetValue);//將目標值傳入(meaningless)
//得到的閉包中不含Y'
if (closure[targetValue] != 1)
{
basisDependencies[i] = 1;//該依賴不可除去
}
else
{
basisFound = true;
}
}
if (basisFound == true)
{
//一旦找到一個,自身不再是最小的
ownIsBasis = false;
cout << "\r";
initMaskAttributes();
cout << "after step 1:" << endl;
printBasis();
//進一步對左邊的屬性進行處理
secondProcess();
cout << "after step 2:" << endl;
printBasis();
}
//若仍有可改變的值,遞歸獲取新的可能,否則return
if (changeControl() == true)
{
findBasis();
}
else
{
if (ownIsBasis == true)
{
initMaskAttributes();
cout << "after step 1:" << endl;
printBasis();
//進一步對左邊的屬性進行處理
secondProcess();
cout << "after step 2:" << endl;
printBasis();
}
return;
}
}
運行結果
對於博客中的示例,結果如下:
對於博客中的示例,結果如下:
皆正確。
其他
爲減少命令行輸入的繁瑣,增加接口函數void defaultSetting(form& sets)
可以直接設定屬性和函數依賴集。
不足&需要改進
- 輸入繁瑣:
當前需要分別輸入函數依賴集的左部和右部屬性集
解決方法:
可增加讀取函數切割輸入字符串X->Y
,用更簡潔的方式讀取函數依賴集 - 輸入受限
在輸入右部時,目前的處理手段只能處理單個屬性。
解決方法:
可以通過預留的split()
函數接口將右部的多個屬性直接分割爲單個屬性 - 最小依賴函數集輸出結果未處理完全
目前會存在這樣的結果:
after step 2:
basis:
B->B
B->C
B->C
C->A
C->B
解決方法:
增加函數消除完全相同的依賴函數