数据库求属性闭包和函数最小依赖集算法实现(附代码地址)

任务

数据库关系模式中函数依赖的理论涉及不少算法,此次根据课程需要将求属性闭包(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仓库

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