數據庫求屬性閉包和函數最小依賴集算法實現(附代碼地址)

任務

數據庫關係模式中函數依賴的理論涉及不少算法,此次根據課程需要將求屬性閉包(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->A1X->A2 、… 、X->An
一:
將一個函數依賴X->Y除去,將其左部屬性X傳遞到閉包初始集closure中,求解左部屬性關於除去此依賴後的閉包結果closure(本函數結果集與初始集是一個數組)中是否存在右部屬性Y,若存在,證明可除去此依賴。
遍厲所有的函數依賴,則得到第一步處理的結果

 //依次刪除一個依賴X'->Y',尋找新依賴集中能否推出Y'
 //若能,則可被刪除;否則不能被刪除
 	//判斷 closure  輸出結果是否Y'爲1

由於遍歷的次序會導致不同的函數依賴結果,我通過設計算法得到所有的可能順序的結果,具體算法爲:

  1. 首先得到的一個最小函數依賴集, 結果儲存在basisDependencies中,0代表函數依賴可除去,例如{0,1,0,1,1}表示第一和第三個函數依賴可除去
  2. 我們將最後一個可除去的依賴(basisDependencies[j] == 0),如例中的第三個,通過控制數組controlDependencies 將代表其位置的值置爲-1(意味不可再被尋找最小函數依賴集時除去),此時再重新尋找最小函數依賴集
  3. 重複上述操作直至最後一個可除去的依賴已經是最後一個位置(沒有必要再將controlDependencies置爲-1 ,我們轉而操作倒數第二個可除去的依賴,如例中的第一個,將其置爲-1,再如第2步重新尋找最小函數依賴集
  4. 重複上述操作直至不存在倒數第二個可除去的依賴,此時獲得所有可能的結果.

二:
上一步得到消除了 冗餘函數依賴所有結果, 如下圖所示

在這裏插入圖片描述
在這裏插入圖片描述

但對於下面的例子得出的結果能看出還存在問題:
將上個例子中函數依賴中左部的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

解決方法:
增加函數消除完全相同的依賴函數

代碼地址

GitHub倉庫

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章