FastFDs

137 阅读16分钟

作者:Catharine Wyss,Chris Giannella,Edward Robertson

会议/期刊:DBLP 2001

FastFDs算法

定义和例子

RR是关系模式,rrRR的一个实例。n=Rn=|R|是属性数量,m=rm=|r|是元组数。

定义1

函数依赖:XYX\rightarrow{Y}是关系rr的一个函数依赖当且仅当对任意元组t1,t2rt_1,t_2\in{r}t1[X]=t2[X]t1[Y]=t2[Y]t_1[X]=t_2[X]\Rightarrow{t_1[Y]=t_2[Y]}。写作rXYr\Vdash{X\rightarrow{Y}}

平凡函数依赖:XYX\rightarrow{Y}是平凡的当且仅当YXY\sube{X}

最小函数依赖:XYX\rightarrow{Y}是最小的当且仅当(i)rXY(i)r\Vdash{X\rightarrow{Y}},(ii)不存在ZX,rZY(ii)不存在Z\subsetneq{X},r\Vdash{Z\rightarrow{Y}}

定义2

函数依赖集合的规范覆盖:关系rr上的函数依赖集合的规范覆盖为Fr={XAXR,AR,rXA,AX,XA是最小的}\mathscr{F_{r}}=\{X\rightarrow{A}|X\subseteq{R},A\in{R},r\Vdash{X\rightarrow{A}},A\notin{X},X\rightarrow{A}是最小的\}

定义3

差集:对于t1,t2rt_1,t_2\in{r},元组t1t_1t2t_2的差集为D(t1,t2)={BRt1[B]t2[B]}D(t_1,t_2)=\{B\in{R}|t_1[B]\neq{t_2[B]}\}

关系rr的差集:Dr={D(t1,t2)t1,t2r,D(t1,t2)}D_r=\{D(t_1,t_2)|t_1,t_2\in{r},D(t_1,t_2)\ne{\empty}\}

关系rr的差集在AA上的投影:DrA={D{A}DDrAD}D^{A}_{r}=\{D-\{A\}|D\in{D_r}且A\in{D}\}

例2:

表1 关系表r0

表1是关系表r0r_0,有

  1. D(t0,t3)={ACF},D(t2,t3)={BCDF}D(t_0,t_3)=\{ACF\},D(t_2,t_3)=\{BCDF\}
  2. Dr0={ABC,ABD,ACF,ADE,ABCD,CD,BF,BCDE,D,BCDF,BE,C,CDEF,BDF,BCE}D_{r_0}=\{ABC,ABD,ACF,ADE,ABCD,CD,BF,BCDE,D,BCDF,BE,C,CDEF,BDF,BCE\}
  3. Dr0A={BC,BD,CF,DE,BCD}D^{A}_{r_0}=\{BC,BD,CF,DE,BCD\}

定义4

覆盖:设P(R)P(R)RR的幂集(即所有属性组合的集合),且XP(R)\mathscr{X}\sube{P(R)}。则XRX\sube{R}覆盖X\mathscr{X}当且仅当YX,YX\forall{Y}\in{\mathscr{X}},Y\cap{X}\ne{\empty}

最小覆盖:XXX\mathscr{X}的覆盖且不存在ZXZ\subsetneq{X}覆盖X\mathscr{X},则:XXX\mathscr{X}的最小覆盖。

考虑XR,AXX\subseteq{R},A\notin{X}XX覆盖DrAD_r^A。设DDrAD\in{D_r^A},则XDX\cap{D}\neq{\empty}。在AA上值不同的任意两个元组在XX上的值一定也不同,也就是说,XX可以区分任意两个在AA上值不同的元组,也就意味着rXAr\Vdash{X\rightarrow{A}}。因此,有:

引理1

XR,AXX\subseteq{R},A\notin{X},则rXAr\Vdash{X\rightarrow{A}}当且仅当XX覆盖DrAD_r^A

定理1

XR,ARXX\subseteq{R},A\in{R-X}rrRR上的一个关系实例。XAX\rightarrow{A}rr上的一个最小函数依赖当且仅当XXDrA(r)D_r^A(r)的一个最小覆盖。

定理1把原问题计算Fr\mathscr{F_r}转化为寻找DrA,ARD_r^A,A\in{R}的问题。令DrA={DDrADDrADDD=D}\underline{D_{r}^A}=\{D\in{D_r^A}|D'\in{D_r^A}且D'\subseteq{D}\Rightarrow{D'=D}\}DrA\underline{D_{r}^A}的覆盖一定也是DrAD_r^A的覆盖。因此,我们可以只保留最小差集并计算DrA\underline{D_{r}^A}的最小覆盖即可。

例3:在例2的关系表r0r_0中,Dr0A={BC,BD,CF,DE,BCD}D^A_{r_0}=\{BC,BD,CF,DE,BCD\}Dr0A={BC,BD,CF,DE}\underline{D_{r_0}^A}=\{BC,BD,CF,DE\}Dr0A\underline{D_{r_0}^A}的最小覆盖有CD,BCE,BDF,BEFCD,BCE,BDF,BEF。因此RHS为AA的最小函数依赖有:CDABCEABDFABEFACD\rightarrow{A},BCE\rightarrow{A},BDF\rightarrow{A},BEF\rightarrow{A}

更进一步优化,如果有两个元组只在AA上值不同,即DrA\empty\in{D_r^A},则XAX\rightarrow{A}一定是平凡的。在这种情况下,算法可以直接终止。

通过计算DrAD_r^A的最小覆盖来计算Fr\mathscr{F_{r}}的思路最早在Dependency Inference (vldb.org)里被提出。作者发现DrAD_r^A里的元组组成了超图里的边。因此,计算Fr\mathscr{F_{r}}的问题转化为计算超图的最小覆盖问题。SIAM95里对许多超图问题的时间复杂度进行了系统的研究。PODS97里研究了数据挖掘问题大框架(包括函数依赖发现问题)与超图遍历问题的关系。

这个方法也是Dep-Miner算法的基础。FastFDs算法和Dep-Miner算法的主要区别是:Dep-Miner采用层次搜索策略计算覆盖,FastFDs采用一种启发式的深度优先搜索策略。

计算差集

FastFDs首先计算基于关系rr和模式RR的差集DrD_r,计算过程如图1所示。然后,对于每一个属性AA,利用DrD_r计算DrA\underline{D_r^A}并计算DrA\underline{D_r^A}的最小覆盖,计算过程如图4所示。在这一部分,我们描述genDiffSets,下一部分我们描述findCovers。在计算

TANE算法的复杂度与r|r|成线性关系。另一方面,最多有r(r1)/2|r|(|r|-1)/2个可能的差集(元组对的个数),所以计算这些差集的时间复杂度为O(Rr2)O(|R||r|^2)。在中,提出了一个在某些情况下减少计算差集的时间复杂度的方法。该方法计算一致集agreesetsagree\,sets。一致集与差集互补。因此,差集可以定义在一致集上。

定义5

  1. 给定t1,t2rt_1,t_2\in{r}t1t_1t2t_2的一致集为A(t1,t2)={BRt1[B]=t2[B]}A(t_1,t_2)=\{B\in{R}|t_1[B]=t_2[B]\}
  2. 关系rr的一致集为Ar={A(t1,t2)t1,t2r}A_r=\{A(t_1,t_2)|t_1,t_2\in{r}\}

引理2将DrD_rArA_r联系起来了。给定XP(R)\mathscr{X}\subseteq{\mathscr{P}(R)},我们定义XC={RXXX}\mathscr{X}^C=\{{R-X|X\in{\mathscr{X}}}\}

引理2

给定模式RR上的关系实例rrARA\in{R}Dr=(Ar)CD_r=(A_r)^C

证明:设D(t1,t2)DrD(t_1,t_2)\in{D_r},根据定义,对于所有BRD(t1,t2)B\in{R-D(t_1,t_2)}t1[B]=t2[B]t_1[B]=t_2[B],因此RD(t1,t2)ArR-D(t_1,t_2)\in{A_r}D(t1,t2)(Ar)CD(t_1,t_2)\in{(A_r)^C}。设XArCX\in{A_r}^C,则RXArR-X\in{A_r}。所以存在t1,t2rt_1,t_2\in{r}使得RX=A(t1,t2)R-X=A(t_1,t_2)。因此根据定义,对于所有BXB\in{X}t1[B]t2[B]t_1[B]\neq{t_2[B]}XDrX\in{D_r}

为了计算DrD_r,我们首先计算关系rr的一致集ArA_r。然后计算ArA_r里每个成员的补集以得到DrD_r。这个看似拐弯抹角的方法的好处在于ArA_r里的成员不是从原始关系的元组对中产生,而是从精简划分里的元组对中产生。对于随机整数值关系表(?),精简划分中的元组对数量与原始关系中的元组对数量相比大大减少,加速了计算。

定义6

给定ARA\in{R},定义:

  1. t1,t2rt_1,t_2\in{r}关于属性AA等价t1At2t_1\sim{_A}t_2,当且仅当t1[A]=t2[A]t_1[A]=t_2[A]
  2. 对于trt\in{r},令[t]A[t]_Att关于属性AA的等价类,则AA的精简划分为
πA={[t]Atr[t]A>1}\pi{_A}=\{[t]_A|t\in{r}且|[t]_A|>1\}
  1. rr的精简划分为
Πr=AR  πA\Pi_r=\bigcup{_{A\in{R}}}\;\pi_A
  1. rr的最大精简划分为
Πr={πΠrπΠrπππ=π}\overline{\Pi_r}=\{\pi\in{\Pi_r}|\pi'\in{\Pi_r}且\pi\subseteq{\pi'}\Rightarrow{\pi'=\pi}\}。

引理3

t1,t2rt_1,t_2\in{r},如果t1t_1t2t_2没有出现在任意πΠr\pi\in{\overline{\Pi_r}},则t1t_1t2t_2在所有属性上的值都不同。

因此,只需要处理每一个πΠr\pi\in{\overline{\Pi_r}}中的元组对来计算ArA_r。图1展示了计算DrD_r的程序genDiffSets。

图1 genDiffSets:从一个关系实例中计算差集

发现DrA\underline{D_r^A}的最小覆盖

FastFDs使用图4中的findCovers程序寻找DrA\underline{D_r^A}的最小覆盖。每一个不包含AARR的子集都可能是DrA\underline{D_r^A}的最小覆盖。考虑一个简单的搜索树,暴力产生不包含AARR的子集。如图2所示,对于R={A,B,C,D,E,F}R=\{A,B,C,D,E,F\},从根节点到每个节点的路径代表了R{A}R-\{A\}的子集。共有2R12^{|R|-1}个节点。深度优先、从左到右的搜索过程可以无重复地按照字典序产生R{A}R-\{A\}的子集。注意,在图2中的字典序为B>C>D>E>FB>C>D>E>F

图2 生成R-{A}的子集

优化后的搜索方法findCovers构建一棵搜索树,同样地,属性是有序的,但是不像图2中的单纯按照字典序,这个属性顺序随着深度搜索的进行不断变化。如图3所示,是寻找DrA\underline{D_r^A}的最小覆盖的过程。树中的每个节点,我们对剩余的属性按照它们覆盖的差集(这些差集在处理当前节点之前还未被覆盖)的数量排序。当两个属性覆盖的差集数量相同时,则按照字典序排序。在每个节点,我们不断更新剩余的差集以及剩余属性的顺序。

图3所示的方法是findCovers所使用的搜索策略,支撑了FastFDs算法(图4)。图3中的叶子节点分为以下两种情况:

  1. 如果某个叶子节点还有剩余差集未被覆盖,但是已经没有剩余属性了,则为失败状态(此叶子节点不存在函数依赖)。如图3中的叶子⑤,⑦,⑧,⑨。

  2. 如果某个节点不存在还未被覆盖的差集,又分为两种情况:

    (a) 该叶子节点表示的函数依赖不是最小的(叶子①)。合法性检测包括测试当前叶子节点代表的LHS的直接子集是否能构成合法的LHS。

    (b) 否则,输出从根节点到当前叶子节点的路径作为当前属性的一个函数依赖的LHS。(叶子②,③,④,⑥)。 在每个节点,我们使用的是贪婪搜索策略:我们优先搜索覆盖剩余差集最多的属性(因为我们的目标是找到覆盖所有差集的属性组)。这个贪婪搜索策略表现良好(见实验部分),但是也会做一些无用功。如图3中的叶子①,函数依赖BCDABCD\rightarrow{A}不是最小的,因为CDACD\rightarrow{A}也是合法的函数依赖(叶子⑥)。这种情况的出现是由于当两个属性覆盖的差集数量相同时,我们按照字典序对它们排序,造成了多余的分支。多余的分支很难辨别,因此算法在输出它生成的函数依赖前必须进行最小性检测。

图3 搜索子集格以寻找最小覆盖

我们正式定义属性的顺序。XP(R)\mathscr{X}\sube{P(R)}为一些属性组合的集合。对于ARA\in{R}Cov(X,A)={ZXAZ}Cov(\mathscr{X},A)=|\{Z\in{\mathscr{X}}|A\in{Z}\}|X\mathscr{X}中包含AA的成员个数。对于A,BRA,B\in{R}

A>B  当且仅当  {Cov(X,A)>Cov(X,B)  Cov(X,A)=Cov(X,B)A字典序大于BA>B\;当且仅当\;\left\{ \begin{aligned} Cov(\mathscr{X},A)>Cov(\mathscr{X},B)\;或 \\ Cov(\mathscr{X},A)=Cov(\mathscr{X},B)且A字典序大于B \end{aligned} \right.

图3这个启发式的,深度优先搜索过程可以用递归方法findCovers表示(图4)。为了计算Fr\mathscr{F_{r}},我们对于每个属性ARA\in{R}搜索类似图3的树。注意若DrA=D^A_r=\empty(例如Dr={BC,D}D_r=\{BC,D\}),则不存在任意两个元组在AA上的值不同,即所有元组在AA上的取值相同,此时AA作为RHS的最小函数依赖只有A\empty\rightarrow{A}。上文提到过,若DrA\empty\in{D_r^A},则不存在属性AA作为RHS的非平凡函数依赖。图5所示的FastFDs算法先检查上述两种情况是否发生,未发生的情况下迭代地对ARA\in{R}执行findCovers。

图4 findCovers:寻找$\underline{D_r^A}$的最小覆盖

FastFDs的复杂度

前文定义过r=m,R=n|r|=m,|R|=n,这里设Fr=K|\mathscr{F_{r}}|=K,让我们复盘一下FastFDs的执行过程。

图5 FastFDs:计算一个关系实例的最小函数依赖

  1. 首先,我们必须计算关系rr的差集DrD_r,花费O(nm2)O(nm^2)时间;为了从DrD_r计算DrA\underline{D_r^A},我们需要最小化包含AA的集合XDrX\in{D_r}。设d=Drd=|D_r|,则最小化过程花费O(dlog(d))O(dlog(d))时间(why?);因此计算DrA\underline{D_r^A}的时间为O(nm2log(nm2))O(nm^2log(nm^2))

  2. 给定Fr=K|\mathscr{F_{r}}|=K,则findCovers的复杂度为O((1+w(n))K)O((1+w(n))K),其中w(n)w(n)是我们不完美的启发式搜素过程造成的多余的工作。

  3. 由于我们对每个属性ARA\in{R}调用findCovers函数,因此FastFDs中的主循环(2-8行)花费时间O(n(1+w(n))K)O(n(1+w(n))K)。 总的来说,我们算法的最差时间复杂度为

O(nm2+nm2log(nm2)+n(1+w(n))K)O(nm^2+nm^2log(nm^2)+n(1+w(n))K)

然而,FastFDs的空间复杂度比层级搜索的Dep-Miner和TANE降低了指数级。FastFDs只需要O(dn)O(dn)空间。dd的上限是m(m1)/2m(m-1)/2。TANE和Dep-Miner消耗了大量空间在候选集cmaxc_{max}上。因此,当nn很大时,FastFDs显著快于层级搜索方法,因为cmaxc_{max}的大小是nn的指数级。这个趋势在伯努利关系实例中更为明显,因为其函数依赖的平均长度为n/2n/2或更大。

实验结果

FastFDs已经用C++实现。测试环境:700MHz Athlon系统(128MV RAM),Red Hat Linux6.1。我们同样实现了我们自己版本的Dep-Miner,其使用和FastFDs相同的代码读取输入关系表并计算DrA\underline{D_r^A}。我们自己版本的Dep-Miner和FastFDs都完全在内存中运行。我们使用的TANE版本可以在网页上运行。

整数值关系实例

我们的第一个实验集包括一些随机生成的整数关系实例。表2展示了TANE,Dep-Miner,FastFDs在这些关系实例上的性能。每个关系实例根据相关系数(CF)生成。CF越高,每列中出现的不同值个数越少(每列的不同值个数范围为(1CF)num_tuples(1-CF)*num\_tuples)。随着CF增加,精简划分的长度也增加,差集计算消耗时间增加。

表2 随机整数值关系实例的结果;|R|=20

表2的定性分析

表2中的结果揭示了以下结论:

  • Dep-Miner和FastFDs中少于0.1%的计算时间花费在搜索函数依赖上;超过99.9%的时间用于计算差集。
  • 随着CF增加,TANE的性能不断接近Dep-Miner和FastFDs的性能(图6)。

表2中的结果表明了差集计算的最好情况(与R,r|R|,|r|成线性关系,每个实例只出现了一次)和TANE在计算过程的每个阶段都必须计算一个线性的划分的差别。

总而言之,在随机生成的有20个属性的整数实例上,当CF范围为0.0-0.9,Dep-Miner和FastFDs比TANE快2-3倍。

图6 随机整数值关系实例(表2)

表3的定量分析

表三对比了FastFDs和Dep-Miner在固定元组数(10,000),属性数量从10到60时的性能。注意到随着CF增加,FstFDs逐渐变得比Dep-Miner更高效。当CF为0.0时,Dep-Miner用时是FastFDs的53\frac{5}{3}倍,当CF为0.9时,Dep-Miner用时是FastFDs的2倍。注意到随着CF增加,计算的最小函数依赖的平均长度从2(CF=0.0)增加到3(CF=0.9)。这个趋势可以帮忙解释当CF增加时,为什么FastFDs更高效,且这个现象在伯努利关系实例中更明显,当最小函数依赖的平均长度增加到R/2|R|/2时,Dep-Miner变得非常低效。

表3 随机整数值关系实例的结果;|r|固定为10,000;删除了计算差集的时间

下一节中可以看出,低CF的随机整数关系实例不能很好地预测现实生活中的关系实例。伯努利关系实例在恰好这方面表现良好。

伯努利关系实例

我们的第二个实验集包括随机生成的伯努利关系实例。伯努利关系实例只包含两个值(通常是0和1)。在这方面,伯努利关系很像频繁项集问题中的“购物篮”关系。

随机伯努利关系实例是发现最小函数依赖的很好的测试。尽管随机整数值实例似乎很适合FastFDs,Dep-Miner,Tane这些搜索方法,但最小函数依赖数量很少,而在伯努利实例中则不同。随机伯努利实例中的最小函数依赖的平均长度已经被量化。对于nn个属性的伯努利实例,最小函数依赖的平均期望长度是n/2n/2,当元组数是m=2n/4m=2^{n/4}。这个长度代表了n个属性的幂集格中任意一层的可能的最大子集的数量,在实践中,当m=2n/4m=2^{n/4}随n呈指数增长时,表示返回的最小FDs的数量。

nn小于30时,平均长度为n/2n/2的最小函数依赖涉及到相对小的数据库,Dep-Miner和FastFDs计算差集的时间O(nm2)O(nm^2)被最小化。在随机整数数据库中,Dep-Miner和FastFDs在搜索函数依赖的时间少于0.1%,然而在m=2n/4m=2^{n/4}的伯努利关系实例中,超过99%的时间花费在搜索函数依赖上(而不是计算差集)。

另外一个有趣的地方在于,由于有许多大小为n/2n/2的子集,层级算法需要指数级的空间去存储中间层的候选集合。因此,需要查看层级搜索算法的指数级空间使用和FastFDs的O(nm2)O(nm^2)的空间使用的影响。

表4阐述了TANE,Dep-Miner和FastFDs在伯努利数据库上的相对性能,当m=2n/4m=2^{n/4},且nn范围为20至28。

表4 伯努利关系实例上的结果

表4的定量分析

三种算法的函数依赖的数量都随着nn成指数增长。然而,我们可以看到TANE和Dep-Miner的指数级空间使用的影响。当20<=n<=2320<=n<=23时,TANE的时间大约是FastFDs的两倍。在n=24n=24,由于大量使用磁盘交换空间,TANE的执行时间迅速增加。当n>=25n>=25,TANE运行超出内存大小。Dep-Miner的结果类似,尽管Dep-Miner的空间使用情况略好于TANE,其时间性能比FastFDs差了两个数量级。

FastFDs不会出现内存用尽的问题。这很重要,因为内存是比CPU更为稀缺的资源并且当内存交换被避免时,CPU会被使用的更高效。

备注1 在伯努利实验上的一个重要结论是,当mm越大时,相比随机整数值实例,伯努利实例上的差集计算效率越低。事实上,对于伯努利实例,计算差集的暴力搜索算法比优化算法更高效。因此,可能有更好的优化算法来计算kk个值的关系实例(k比较小)。

图7 表4的结果:右边y轴单位是对数

机器学习库关系实例

我们的第三个实验集涉及从机器学习仓库里提取的非合成的关系。表5展示了FastFDs,TANE,Dep-Miner三种算法在9个关系表上的性能。表6展示了在删除差集计算时间后Dep-Miner和FastFDs的性能。

表5 在机器学习关系实例上的结果

表6 表5中结果删除了差集计算时间

表5和表6的定量分析

首先比较FastFDs和Dep-Miner的运行时间。在所有情况下,FastFDs性能都不次于Dep-Miner。随着R|R|的增长,FastFDs比Dep-Miner快的越来越多。

接下来比较FastFDs和TANE的运行时间。注意当r|r|很大时,FastFDs的时间很长,这是由于r2|r|^2的差集计算时间。另一方面,随着R|R|增加,TANE的内存消耗迅速增加。对于Horse-Colic数据库(R=28|R|=28)FastFDs快速发现了最小函数依赖;而TANE超出内存限制。在现实情况中,发现函数依赖的最好方法可能是TANE(小R|R|和大r|r|)和FastFDs(小r|r|和大R|R|)的平衡。

结论和未来方向

本文提出了一个新的函数依赖发现方法FastFDs,在差集上利用启发式,深度优先搜索发现最小函数依赖。

我们的实验结果表明FastFDs在以下每种基准关系实例上都很有竞争力:(1)不同相关系数的随机整数实例,(2)随机伯努利实例,(3)现实生活中的机器学习库关系实例。事实上,我们的实验表明,对于宽的关系(大R|R|),在任何类型的关系实例上,FastFDs都表现非常好,原因在于深度优先搜索算法固有的空间高效性。对比较窄的关系,TANE可能更适用于种类(3)的关系实例。

有趣的是通常用来解决AI问题的启发式深度优先算法经常被数据挖掘界回避,因为它们不能处理数据挖掘程序需要的大量数据。然而,我们的实验表明,在计算DrAD_r^A的覆盖这一问题上,AI搜索策略比规范的层级搜索更好。事实上,在伯努利数据库中,当计算的函数依赖的平均长度>=R/2>=|R|/2,启发式搜索方法惊人得高效。

FastFDs的空间高效性使得它自然而然具有并行性。FastFDs的一个明显的多线程版本就是同时计算不同的属性AA作为RHS的函数依赖。

FastFDs的性能依赖于用以计算最小超图覆盖的简单的,贪婪的启发式算法。我们的启发式算法用来在一般的超图上发现最小覆盖;然而,并不是所有的超图都可以由关系实例产生。关系实例中蕴含着额外的限制还未被当前的启发式算法反映出来。在未来对这些限制进一步研究,以设计更好的启发式算法是一条有趣的道路。

另外一个有用的方向是考虑增量依赖推理问题:给定rr,函数依赖集合的规范覆盖Fr\mathscr{F_{r}},和一个元组tt,发现r{t}r\cup{\{t\}}r{t}r-{\{t\}}的规范覆盖。据我们所了解,这个问题目前还未被解决,并且看起来极具挑战性。

最后,非常合理的一点是,所有的函数依赖推理问题的应用都不需要所有的函数依赖,只需要“有趣的”函数依赖。举个例子,LHS比较小的函数依赖可能比LHS大的函数依赖更有用。刻画函数依赖的趣味性并设计算法发现这些依赖是未来的一个好的研究方向。