极小函数依赖集求解算法:拆、删、消三步与手算例题
发布时间:2026/10/19 20:54:26 锦皓数字建站

函数依赖的极小化大概是数据库原理课里最让人“背得下定义却下不了笔”的考点了。提到极小函数依赖集多数人都能背出三条右部单属性、没有冗余依赖、没有冗余属性。可真给你一张函数依赖集F让你求Fmin很多人第一步就卡住先拆先删用谁的闭包删完还能不能删这篇就是冲着这些问题来的。我会把极小函数依赖集的求解算法拆成一套可以直接照做的流程配合两道手算例题把闭包计算、覆盖判定、删依赖、消属性这些动作全部过一遍顺便把考试里最容易扣分的细节都指出来。这篇文章适合正在准备数据库期末考试、考研初试的同学也适合自学数据库原理但被教材例题绕晕的开发者。你不需要多深的基础只要知道函数依赖是“属性之间的决定关系”就能跟着走下来。先说明一点我不只给步骤我会反复讲“为什么这一步要这么干”因为考试改步骤分的时候理由往往比最终答案更重要。1. 先读懂考纲极小函数依赖集要消灭的到底是什么冗余1.1 三条定义的直白翻译教材上函数依赖集F的极小化也叫最小覆盖Fmin一般要求满足三个条件F中每个函数依赖的右部是单属性F中不存在冗余依赖F中不存在冗余属性。这些定义很多同学背得滚瓜烂熟却不理解。“右部单属性”好办就是把X→AB这种依赖替换成X→A和X→B两条。“无冗余依赖”的意思是删掉某一条依赖之后剩下的依赖集仍然能推出原来那条依赖那这条依赖就是多余的。“无冗余属性”指的是一条依赖左侧如果带着A、B两个属性而去掉B之后只剩A也能推出右边的结论那么B就是多余的。这三条读完你再回头看题目会发现极小化其实根本不是让你去“背算法”而是让你反复回答三个问题这条依赖能不能删删了之后推理能力还在吗左部这些属性是不是有凑数的回答清楚答案自然出来了。1.2 极小化的本质推理能力不变的“钥匙串整理”用一句话归纳Fmin是F的“等价但最小”版本。所谓等价是说F能推出的每一个函数依赖Fmin也能推出反过来Fmin能推出的F也必然能推出。瘦身就是在保持这种推理能力不变的前提下尽量删除和压缩。我给学生讲的时候喜欢打一个比方你有一串钥匙每把钥匙都能开某一扇门但有一把钥匙能开的门别的钥匙全能开那这把就是冗余依赖有的钥匙上挂了一个装饰环去掉环完全不影响开门这个环就是冗余属性。题目要你做的就是把钥匙串整理到最精简但每一扇原来看得上的门都还必须开得了。这个比方看着简单其实非常贴近算法本质。尤其是后面做删依赖和消左部判断时你只要反复问自己一句“去掉它别的钥匙还能开这扇门吗”思路就不会乱。1.3 极小集不唯一先给自己松个绑教材里常说“函数依赖集的极小化”但很少强调一个关键事实Fmin不是唯一的。删除冗余依赖的顺序不同、消左部属性的先后不同很可能得到不同的极小集。比如A→B和B→A两条能互相推出的依赖你先删A→B和后删B→A留下来的就不一样。考试里只要最终结果满足三条条件且与原集等价都应该算对。这一点非常容易让考生怀疑自己算错先打一个预防针不要因为答案和同学不一样就立刻觉得自己错了先检查每一逻辑链是否成立。2. 前置能力闭包与覆盖整套算法的手感来源极小化的每一步其实都在做“比较”删掉一条依赖后还能不能推出原来那条左部去掉一个属性后还能不能推出右部这些话落到计算上全靠“属性闭包”和“依赖覆盖”两件工具。不把这两个技能练熟背再多步骤也是空中楼阁。2.1 属性闭包的手工算法给定属性集X和依赖集FX关于F的闭包记作X(F)含义是从X出发用F里的依赖反复推导最终能得到的所有属性集合。手工计算很机械但必须养成固定节奏把闭包集合初始化为X本身循环扫描F任何一条依赖左侧已经全部出现在当前闭包中就把它的右侧属性并入闭包重复扫描直到一整轮下来闭包没有新增属性判断某条依赖X→A是否成立就看A是否落在X(F)里。我用一个生活类比辅助理解闭包就是“你认识的人加上你认识的人的朋友”。假设你认识AA认识BB认识C那么你通过A能认识B再通过B能认识C扩散到没有人再提供新面孔为止。谁进了这个圈子谁就是闭包成员。举个微型例子。设F{A→B, B→C, C→A}让你算B的闭包。初始是{B}B→C把C带进来闭包变成{B,C}接着C→A把A带进来闭包变成{A,B,C}再扫一轮A→B、B→C、C→A都没有新属性了所以B(F){A,B,C}。2.2 覆盖与等价“谁更能推”的判断标尺接下来是覆盖。如果对于G中的每一条依赖X→A都能从F推出即A∈X(F)我们就说F覆盖G。如果F覆盖G且G覆盖F那么F和G等价。等价说白了就是“推理能力完全一样”。这在极小化里有什么用作用大了。极小化的每次删除操作都要验证“删完之后的新集合与原集合等价”。一个依赖X→A想从F里删掉只需要验证A属于X在“去掉它之后的集合”下的闭包。因为这时新的依赖集仍能推出X→A而原来能推出的其他依赖没有减少等价关系就保住了。这个判定式是全篇最核心的公式你最好把它抄在笔记第一行想删X→A就算A是否在X(F-{X→A})里。后面所有的删依赖动作都是在重复套这条公式。再补充一个覆盖判断的小例子。假设F{A→B, B→C, C→A}G{A→C}。判断F是否覆盖G算A在F下的闭包得到{A,B,C}C在里面所以成立。反过来G当然覆盖不了F因为G连B都推不出来所以两者不等价。这种来回验证的思路就是做题时的“体检”。2.3 考试中提高闭包速度的小规矩闭包计算看起来简单但依赖一多就容易漏。实际做题建议每一轮扫描时在纸上把新加入的属性用下划线标出来避免重复判定一条依赖左侧属性已经全部出现时立刻并入右侧并划掉那条依赖已经用过的依赖如果右侧没有带来新属性下一轮就不必再看。很多同学做题慢不是不会而是每次都从头扫到尾导致闭包反复空转。把“扫描标记归并”做成肌肉记忆闭包一分钟内可以算完一组小型依赖集。闭包速度上来之后极小化题目基本就不再受时间困扰了。3. 求解算法三阶段拆、删、消的完整逻辑进入核心环节。给定一张函数依赖集F按以下三个阶段的顺序执行就能得到Fmin第一阶段拆右部第二阶段删冗余依赖第三阶段消左部冗余属性。下面逐一拆开讲。3.1 阶段一右部属性逐条拆开对F中每一条右部含多个属性的依赖例如X→AB替换成两条X→A和X→B。这个动作没有任何推导技巧唯一注意点是不要顺手在拆右部时就把左部拆开。左部属性是否冗余是后面第三阶段的活儿拆右部阶段要保持左部原样否则会让后面的等价性判定变混乱。为什么右部必须先拆第一是因为极小集第一条硬性要求就是右部单属性第二是因为把右部分开后每条依赖的冗余性、左部冗余性都能独立判断不会被“半冗余”状态干扰。比如X→AB里可能A冗余但B不冗余整体依赖没法简单删必须先拆开才能分别处理。3.2 阶段二删除冗余依赖对当前依赖集中的每一条依赖X→A做一次“试删”临时把它从F中拿走得到F′在F′下计算X的闭包X(F′)如果A仍然在闭包里说明这条依赖是多余的正式删除如果A不在闭包里说明这条依赖必不可少放回。这里必须特别强调判断用的是“去掉这条依赖之后的集合”而不是原始F。更关键的是一旦前面有依赖被删除后续判断全部基于删完之后的当前集合。很多同学用原始F一口气算到底遇到带传递环的题就会把本不该删的删掉或该删的删不掉。考试时建议在草稿纸上把“当前F”单独写成一行每删一条就更新一次别怕麻烦。这行字既是你的计算依据也是阅卷老师看步骤分的线索。3.3 阶段三消左部冗余属性经过阶段二剩下的依赖都“不可或缺”了但左部还可能带着多余属性。以AB→C为例要判断A是否多余就是把A临时拿掉看剩下的B在当前F下能不能推出C。如果能A就是冗余的把AB→C换成B→C如果不能保留A。注意这里的闭包计算对象是去掉属性后的左部Z而不是原来的X。很多同学在AB→C里判断B是否冗余下意识就去算AB的闭包这是错的。正确的问法是如果左部只剩A还能不能推出C所以你要算的是A在当前F下的闭包看C在不在里面。左部有多余属性的依赖每消掉一个属性后要回头重新检查剩下的左部。比如ABC→D先发现B冗余变成AC→D这时还要再检查A和C直到没有属性可以再减。这个“连环消元”的动作在综合题里非常常见。3.4 三个阶段能不能换顺序教材上有两种流派有的先删冗余依赖再消左部属性有的先消左部属性再删冗余依赖。考试我强烈建议用“拆→删→消”的标准顺序。原因是冗余依赖如果还留在集合里计算闭包时会把结果“撑大”导致左部属性是否冗余的判断失真。先删干净再做左部消元每一步闭包计算的对象都是干净集合判定更可靠。顺序本身不一定严格影响最终正确性但按固定流程能避免大量低级失误。答题时在开头写一句“按右部拆分、冗余依赖删除、左部属性消除的顺序处理”阅卷老师一眼就能看出你思路清晰。4. 两道高频考题的完整手算推演前面讲的是套路下面完整算两道题。题目难度都不高但包含了极小化几乎全部关键动作。4.1 例题A一个纯粹的冗余依赖删除过程设F{A→B, B→A, B→C, A→C, C→A}求Fmin。右部本来就是单属性跳过拆分直接进入删依赖阶段。当前F就是原F。先看A→B。临时去掉它在F-{A→B}下计算A的闭包。A→C把C带进来C→A形成闭环闭包停在{A,C}始终没有B所以A→B不能删。再看B→A。去掉它在剩余集合中计算B的闭包B→C得到CC→A得到AA→B又带回B闭包扩张到{A,B,C}。A在闭包内说明B→A可以由其余依赖推出删除B→A。当前F更新为{A→B, B→C, A→C, C→A}。接着看B→C。去掉后当前集合是{A→B, A→C, C→A}B从这里出发没有任何以B开头的依赖闭包只有{B}所以B→C保留。再看A→C。去掉后在{A→B, B→C, C→A}下算A的闭包A→B得到BB→C得到CC→A闭环闭包包含CA→C冗余删除。当前F变为{A→B, B→C, C→A}。最后看C→A。去掉它后{A→B, B→C}中C没有可推出的属性闭包只有{C}不含AC→A保留。阶段二结束当前F{A→B, B→C, C→A}。所有依赖左部都是单属性第三阶段无需操作最终Fmin就是{A→B, B→C, C→A}。这个例子的重点在于删除B→A之后判断A→C用的已经是更新后的集合。如果你始终用原始F算这一步闭包也会包含C结果碰巧一样但一旦遇到更绕的环形依赖差别就会立刻显现。养成“删一条、更新一条”的习惯永远不会错。4.2 例题B同时存在冗余依赖与冗余左部的综合题设F{AB→C, C→A, A→D, AD→E, E→A, D→B, B→C}求Fmin。先拆右部所有依赖的右部已经是单属性跳过。阶段二删冗余依赖。先看AB→C去掉它在剩余集合{C→A, A→D, AD→E, E→A, D→B, B→C}下计算AB的闭包。初始是{A,B}B→C直接把C带进来AB的闭包已经包含C所以AB→C是冗余的正式删除。当前F更新为{C→A, A→D, AD→E, E→A, D→B, B→C}。接下来逐条检查剩余的依赖。C→A去掉后C的闭包只有{C}不含AA→D去掉后A的闭包只有{A}不含DAD→E去掉后AD的闭包从{A,D}出发经D→B得到BB→C得到CC→A得到A最后停在{A,B,C,D}始终没有EE→A去掉后E的闭包只有{E}D→B去掉后D的闭包只有{D}B→C去掉后B的闭包只有{B}。这些依赖右部都不在对应闭包内所以全部保留。阶段三消左部。当前F{C→A, A→D, AD→E, E→A, D→B, B→C}只有AD→E的左部有多个属性。先判A是否冗余。临时去掉A问题变成D在当前F下能不能推出E算D的闭包D→B得到BB→C得到CC→A得到AA→D闭环此时闭包里已经有A、B、C、D而AD→E的左侧A和D都被推出了所以E也能进入闭包。D的闭包包含E说明A冗余把AD→E替换成D→E。当前F更新为{C→A, A→D, D→E, E→A, D→B, B→C}。现在所有依赖左部都是单属性算法结束。最终Fmin{C→A, A→D, D→E, E→A, D→B, B→C}。这道题同时体现了删依赖和消左部两个动作。注意消左部时我计算的是D的闭包而不是AD的闭包这一步对应“去掉A后D还能不能推出E”的问题。D能推出EA就是多余的这个依赖就可以从AD→E瘦身成D→E。4.3 结果的自我校验算出结果不是终点考试时间充裕时务必做等价性验证。理论上最终G要满足对G中每条依赖Y→BB在Y关于原始F的闭包中对F中每条依赖X→AA在X关于G的闭包中。完整验证比较费时间但快速校验至少可以抽查几条。拿例题B的结果来说G{C→A, A→D, D→E, E→A, D→B, B→C}还原回原F的推理关系原F里的AB→C在G下由B→C直接推出原F里的AD→E在G下由D→E推出。反过来G里每一条都能从原F推出比如D→E在原F下需要走D→B、B→C、C→A、A→D凑齐AD后再用AD→E。这种互相覆盖的检查做完你会非常踏实考试时就不怕“算对了但怀疑自己”。5. 考场中最容易翻车的细节与检查清单5.1 高频错误对照我观察到的错题前四个翻车点最值得总结。第一删除依赖时用原始F算闭包而不是去掉该依赖后的当前集。第二消左部属性时算错对象比如AB→C判断B是否冗余应该算A的闭包很多人却去算AB的闭包。第三拆右部之后把左部也顺手拆掉或者右部只拆了一半。第四每一步删除后不更新集合导致后面对同一依赖反复判断越算越乱。这四个错误有一个共同根源没有时刻盯着“当前依赖集”。我建议做题时把当前F单独写一行每删一次就划掉更新一次闭包计算的每一轮都对照这行最新的集合来写推导。从答题角度讲这行“当前F”也是重要的得分依据写清楚不仅能防自己出错还能让老师顺藤摸瓜给步骤分。5.2 考场快速检查模板进入考场前把下面这份模板刻在脑子里。拿到题目第一步检查F中是否有右部多属性的依赖有则拆拆完重新整理。第二步对每条依赖做“试删”临时删掉它算左侧闭包看右部是否还在删除后立刻更新集合一条一条来。第三步对每个左部多属性的依赖逐个属性做减法测试算出“去掉该属性后的集合”的闭包看能否推出右部能推就删属性删完回头重新检查剩下属性。第四步把最终结果与原F互相覆盖验证至少抽查原F里的每条依赖是否都能由结果推出。最后检查结果格式右部全部单属性没有哪条依赖删掉后仍可被推出没有哪个左部属性删掉后仍不影响结论。四条全过这题基本就稳了。如果题目明确要求“求极小函数依赖集”过程的书写比最终集合更重要。极小集不唯一但每一步的推理理由可以统一为同一套闭包判定你只要把这套判定写清楚结果不同也没关系。5.3 考前复习节奏建议我在复习备考数据库时最后一周每天只做两件事上午练五道闭包计算小题下午做两道极小化综合题。闭包是极小化的基本功练到闭包手算不再出错极小化题目就只是套流程。遇到算出来和参考答案不一样时不要急着改答案先检查自己“当前F”每一步更新是否正确、闭包有没有漏推新属性再检查是不是删了本不该删的依赖。多半是这两个原因。另外一个实用教训不要试图背题。我见过不少同学背了大量答案考场换一组依赖就懵了。极小化的核心其实就是一个循环——试删、算闭包、看结果、更新集合。把这个循环理解透题怎么变都只是同一套动作的重复。先把闭包练稳再谈极小化顺序千万别反。
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。