老王在城西经营着一家快递分拣仓库。每天都有几百张快递单涌进来,每张单子上贴着一串商品标签,比如“牛奶—面包—鸡蛋”或“牛奶—面粉”。电商老板们总爱问:“老王,哪些货常常被同一张单子一起买走?”老王要是把每张单子都翻一遍,找到所有“经常结伴出现”的商品组合,那得翻到天亮。Apriori算法就像老王的笨办法——先列出所有可能组合,再一遍遍核对快递单。而FP-Growth呢?它像一位替老王重新设计了仓库布局的高人:第一遍扫单子,记下哪些商品是热门;第二遍,把每张单子的标签按热度排序后“挂”到一棵共享货架上。相同前缀的单子共用同一个架位,路径自然压缩,之后无论要挖什么组合,都不用再翻原始单子,只要顺着货架的藤蔓摸瓜就行。这就是FP-Growth——频繁模式增长算法,用一棵树替代了Apriori里令人头秃的候选集生成。

岗位关联

对AI从业者而言,FP-Growth是关联规则挖掘中最高效的经典算法之一,掌握它意味着你能在海量交易数据中快速找到有价值的共现模式,这是做用户画像、推荐系统和智能风控的硬技能。

目录
  1. 可视化演示
  2. 核心思想:从“一遍遍翻单”到“建好货架一次成型”
  3. 支持度统计:第一遍扫单,先给商品称重
  4. FP树构建:共享前缀,把重复路径压成一根
  5. 条件模式基:从树叶往根回溯“局部清单”
  6. 条件FP树:递归降维,挖出完整的频繁模式
  7. 性能优势:不猜组合,所以快得吓人
  8. 并行化扩展:把货架拆给多个仓库同时干
  9. AI 应用场景
  10. Python 演示
  11. 常见误区
  12. 面试题

可视化演示

可视化展示FP-Growth算法核心机制:FP树构建与路径压缩、条件FP树递归挖掘、FP-Growth与Apriori性能对比,交互切换理解频繁模式挖掘。

本节配有交互动效(进入视口后自动加载,约 3KB)。

FP树构建与路径压缩 事务按频次排序插入 共享前缀路径压缩 FP-Tree = insert(sort(T)) FP树通过路径压缩高效存储频繁项

核心思想:从“一遍遍翻单”到“建好货架一次成型”

老王过去应对电商老板的追问,靠的是最笨的办法:把所有可能一起买的商品组合都列出来,比如“牛奶+面包”“牛奶+鸡蛋”“面包+鸡蛋”……然后翻遍每一张快递单去数哪个组合出现得多。如果商品有几千种,组合数量会爆炸式增长,老王的仓库伙计们被累得直不起腰。Apriori算法就是这条路:先生成候选集,再反复扫描数据库验证,每多一件商品,候选组合就多一倍。

FP-Growth换了个思路。它把订单看成“标签串”,反正相同前缀的单子(比如都从“牛奶→面包”开头)可以在仓库里共用一个货架区,何必每次从头翻?于是算法先扫第一遍数据库,统计每个商品的“热度”(支持度),剔除冷门商品;扫第二遍时,把每张单子的标签按热度从高到低排序,再逐个挂到一棵树上——这棵树就是FP-Tree(频繁模式树)。树搭好了,所有频繁模式都藏在树枝上,顺着节点往下走就能找到,完全不需要再生成候选集。

说回老王:他现在的仓库不用再堆满候选组合清单,而是先造出一棵“共享货架树”,之后任何“哪些东西常一起卖”的问题,都变成在树上顺藤摸瓜。这正是FP-Growth名字里“频繁模式增长”的由来——频繁项集是从树的路径上“长”出来的。

support(X)=count(X)∣D∣\text{support}(X) = \frac{\text{count}(X)}{|D|}
支持度就是“包含商品组合 X 的订单数”占“全部订单数”的比例,热度越高越值得关注。
  1. count(X)统计同时包含组合 X(如“牛奶+面包”)的快递单张数,这是分子。
    count(X)\text{count}(X)
  2. |D|D 代表全部订单集合,|D| 是订单总数,作为分母做归一化。
    ∣D∣|D|
  3. support(X)两者相除得到组合 X 的支持度,值越接近 1 说明越常见。
    count(X)∣D∣\frac{\text{count}(X)}{|D|}

支持度统计:第一遍扫单,先给商品称重

建树之前,老王要做一件事:给仓库里所有商品“称重”。他让伙计把每一张快递单的商品标签抄下来,遇到一个商品就画一笔“正”字。几天下来,每个商品的累计出现次数就是它的支持度计数。那些只出现过一次两次的冷门商品,比如某个顾客偶然买的“老式收音机”,会被直接扔出局——因为它们几乎不可能和其他商品凑成“热卖组合”。

这一步对应算法中的第一遍数据库扫描:统计全部单项的频率,保留达到最小支持度阈值(min_sup)的项,并按频率从高到低排序。这一步至关重要,因为FP树之所以能共享路径、压缩体积,前提是所有订单都把高频项放在前面。如果第一个商品每次都不同,树的分支就会像孔雀开屏一样散开;而热门商品排前面,大量订单就有了相同前缀。

在老王仓库里,这一遍扫描像给商品“过秤”:不够分量的商品直接剔除,剩下的按重量排好队。这样一来,第二遍建树时每位顾客的订单都按“重货在前”的顺序挂上货架,树的分支自然紧凑,为后续的路径压缩打下了基础。

Frequent={X∣support(X)≥min_supp}\text{Frequent} = \{ X \mid \text{support}(X) \ge \text{min\_supp} \}
最小支持度是一道门槛,只有“热度”够高的商品或组合才有资格进入FP树。
  1. 支持度 requirement计算每个商品组合 X 的支持度,看它能否跨越阈值。
    support(X)\text{support}(X)
  2. min_suppmin_supp 是提前设好的最低热度门槛,比如 0.5 或出现次数≥2。
    min_supp\text{min\_supp}
  3. Frequent 集合把符合条件的 X 收进“频繁项集”清单,作为后续建树的原料。
    Frequent={X∣support(X)≥min_supp}\text{Frequent} = \{ X \mid \text{support}(X) \ge \text{min\_supp} \}

FP树构建:共享前缀,把重复路径压成一根

现在到了重头戏:老王要布置“共享货架”。规则很简单——所有订单的标签必须先按热度排序,然后从根节点开始,一个一个挂上树。如果某个标签在树上已经有同名的宝贝节点,就顺着它走,节点计数加一;如果树上还没有这个标签,就新建一个分支节点。比如三张订单分别是“牛奶-面包-鸡蛋”“牛奶-面包-黄油”“牛奶-面包”,它们的前缀“牛奶→面包”完全一样,树上只需保留一条路径,计数累加到3即可。

这就是FP树的精髓:路径压缩。FP树节点上存的不再是单件商品本身,而是商品的计数。根节点是空货架起点,每个子节点挂着一个商品标签和它的计数。同时,算法维护一张“项头表”,它就像仓库入口的索引目录,把所有频繁项按热度列出来,并链接到树上所有同名节点——之后挖掘时,想找哪个商品,直接通过索引跳到对应节点,不必全树遍历。

老王指着仓库里那排货架笑道:“以前四张单子里有三张都写着'牛奶→面包',我从头挂四个牛奶、四个面包;现在只挂一个牛奶、一个面包,计数写个3。共享货架一节省,整棵树就瘦了一大圈。”

ρ=N−KN=1−KN\rho = \frac{N - K}{N} = 1 - \frac{K}{N}
路径压缩率 ρ 表示省下来的节点比例:K 是树上的实际节点数,N 是所有订单的标签总数。
  1. N 与 KN 是原始所有订单标签的总数,K 是建树后实际产生的节点个数。
    N$$ 与 $$K
  2. N - K相减得到“被共享掉”的节点数,这就是路径压缩节省的空间。
    N−KN - K
  3. ρ 压缩率节省的节点数除以总标签数,得到 0~1 之间的压缩率,越高说明共享越多。
    ρ=1−KN\rho = 1 - \frac{K}{N}

条件模式基:从树叶往根回溯“局部清单”

货架建好了,接下来就是挖宝环节。FP-Growth 的挖掘是自底向上的——从支持度最低(也就是货架最底层)的节点开始。老王想查“鸡蛋”都和谁一起卖,他不需要再翻原始单据,只需要在项头表里找到“鸡蛋”的链接,顺着指针找到树上所有“鸡蛋”节点,然后从每个“鸡蛋”节点往父节点回溯,一直走到根,把沿途经过的节点路径记下来。

这条回溯出来的路径就叫做“前缀路径”(prefix path),所有前缀路径的集合就叫“条件模式基”(Conditional Pattern Base,CPB)。每条路径还要带上“鸡蛋”节点的计数,因为路径上的商品真正和“鸡蛋”共现的次数等于这个计数。老王把这些局部清单收集齐,再按支持度过滤,就得到了“鸡蛋的条件模式基”——只属于“鸡蛋”的小数据。

这就好比老王想查“鸡蛋的黄金搭档”,他不再翻整仓库的原始单子,而是顺着“鸡蛋”这条藤蔓,把每个挂着鸡蛋的货架支路单独拍个清单。接下来的工作,都在这叠局部清单里完成。

CPB(x)={prefix(p)∣p∈Tree,p 通向 x}\text{CPB}(x) = \{ \text{prefix}(p) \mid p \in \text{Tree}, p \text{ 通向 } x \}
条件模式基就是“所有包含 x 的完整路径,去掉 x 之后剩下的前缀部分”的集合。
  1. prefix(p)p 是树上从根到 x 的一条完整路径,prefix 表示去掉末端的 x 后保留的前缀路径。
    prefix(p)\text{prefix}(p)
  2. p 通向 x限定条件是路径 p 必须最终指向节点 x,这样才能保证 x 一定出现在路径里。
    p∈Tree,p 通向 xp \in \text{Tree}, p \text{ 通向 } x
  3. CPB(x)所有满足条件的前缀路径构成集合,这就是 x 的“小局部”数据。
    CPB(x)={prefix(p)∣…}\text{CPB}(x) = \{ \text{prefix}(p) \mid \ldots \}

条件FP树:递归降维,挖出完整的频繁模式

有了“鸡蛋”的条件模式基,老王就有了一个缩小版的数据库。他可以对这份局部清单再做一次FP树的构建:统计前缀路径中各商品的支持度,过滤掉不够格的,再挂成一棵小树——这就是“条件FP树”(Conditional FP-Tree)。在这棵小树上继续递归挖掘,每次递归都锁定一个新的后缀商品,模式就一层层“长”出来。

回到“鸡蛋”的例子:假设“鸡蛋”的支持度为2,向下递归时以“面包”为新的后缀,会发现“面包→鸡蛋”出现2次,于是频繁模式{面包,鸡蛋}诞生。再递归一层,可能得到{牛奶,面包,鸡蛋}。整个挖掘过程就像剥洋葱——每次递归剥掉一层,把后缀锚点不断加长,直到条件FP树只剩一个分支或为空为止。

老王把这套递归戏法称作“俄罗斯套娃”:每挖一层,货架就会变小一圈,但每次都会带出一个更长的组合。等所有套娃都打开,仓库里所有“热卖组合”清单就整整齐齐地摆在桌上了。

Patterns(x)={x∪Y∣Y∈FreqPatterns(CTreex)}\text{Patterns}(x) = \{ x \cup Y \mid Y \in \text{FreqPatterns}(\text{CTree}_x) \}
频繁模式就是“后缀项 x”与“条件FP树里挖出的子模式 Y”的拼接结果。
  1. CTree_x由 x 的条件模式基构建的“条件FP树”,这是一棵缩小版货架。
    CTreex\text{CTree}_x
  2. Y 来自递归在条件FP树上继续递归挖掘,得到更小的频繁项集 Y。
    Y∈FreqPatterns(CTreex)Y \in \text{FreqPatterns}(\text{CTree}_x)
  3. x ∪ Y 合成把每个 Y 与后缀 x 拼接,得到完整频繁模式。
    x∪Yx \cup Y

性能优势:不猜组合,所以快得吓人

Apriori之所以慢,是因为它要“猜组合”:先生成所有候选集,再一遍遍扫数据库验证。候选组合的数量随着商品种类呈指数级增长——10种商品就有1024种组合,20种商品就超过100万种。而FP-Growth只扫两遍数据库:第一遍统计单项频率,第二遍建树。之后所有挖掘都在内存中的FP树上完成,不再触碰原始数据库。

从复杂度上看,FP-Growth的代价主要取决于订单的平均长度和订单数量,呈线性关系;而Apriori的候选集生成阶段是组合数爆炸。树结构还天然做了路径压缩,内存占用也远低于Apriori反复装载的候选集。尤其是面对稀疏数据(每张订单商品少但种类多)时,FP-Growth的优势更加明显。

老王总结道:“Apriori 像一个个去敲门问'你们家有没有牛奶面包',FP-Growth 则是把全楼的邮箱钥匙全配好,一次进门,之后想找谁就找谁。”

OFP(D)=L×N,OApriori(D)=N×2kO_{\text{FP}}(D) = L \times N, \quad O_{\text{Apriori}}(D) = N \times 2^{k}
FP-Growth 的复杂度随订单长度 L 线性增长,而 Apriori 会随商品种类 k 指数爆炸。
  1. L × NN 是订单数,L 是每笔订单平均标签数,两者相乘就是建树的总工作量。
    L×NL \times N
  2. N × 2^kApriori 需要对 N 张订单反复扫描,去验证最多 2^k 个候选组合。
    N×2kN \times 2^{k}
  3. 对比L 通常很小(个位数),k 却可能上千,所以FP-Growth量级远小于Apriori。
    OFP≪OAprioriO_{\text{FP}} \ll O_{\text{Apriori}}

并行化扩展:把货架拆给多个仓库同时干

订单量太大怎么办?老王开了三家分店。他按快递单的编号把数据切成多份,每家分店领到一份订单子集,各自独立构建局部的FP树、独立挖掘频繁模式——这就是“数据划分并行”。因为FP树的构建是可以并行的,每台机器只需要处理自己的那部分订单,互不干扰。

但并行不是免费的午餐。不同的切分方式会导致各分店工作负载严重不均:如果某家分店恰好分到了大量“牛奶面包”订单,它的树必然又高又粗,构建时间远长于其他分店。所以并行化FP-Growth的关键在于负载均衡——要么按商品哈希分组让各节点数据更均匀,要么采用动态任务调度的方式让空闲节点主动领活。最后还需要一个结果合并阶段,把各分店挖出的局部频繁模式汇总,再做一次全局过滤,确保不遗漏全局频繁项。

老王看着三家分店的数据汇总说:“分店越多是越快,但最后合并数据、统一口径这一步省不掉。想要真正跑得快,就得让三家分店干得一样多,别让一家累死三家闲死。”

Speedup(p,k)=1(1−p)+pk\text{Speedup}(p, k) = \frac{1}{(1-p) + \frac{p}{k}}
阿姆达尔定律告诉我们:加速比取决于可并行部分的比例 p 和处理器数量 k,串行部分会拖后腿。
  1. (1-p)1-p 表示程序中不可并行的串行部分,比如最终合并结果,它始终占用1份时间。
    (1−p)(1-p)
  2. p/k可并行部分 p 被 k 个处理器分担,理想情况下耗时缩小为原来的 1/k。
    pk\frac{p}{k}
  3. Speedup总耗时是串行与并行之和,取倒数得到加速比,k 越大加速比越趋于 1/(1-p)。
    Speedup=1(1−p)+pk\text{Speedup} = \frac{1}{(1-p) + \frac{p}{k}}

AI 应用场景

电商平台的捆绑推荐

阶段:特征工程与模式挖掘

解决的问题:传统人工分析销售数据找“常被一起购买的商品组合”效率极低,且难以及时发现新组合。FP-Growth通过扫描历史订单快速构建FP树,自动挖掘全部高支持度组合,为商品捆绑推荐提供数据支撑。

具体案例:某生鲜电商每天产生50万笔订单,数据团队用FP-Growth对近30天订单做关联规则分析,设置最小支持度0.5%。算法在几分钟内找出“牛油果+全麦面包”“三文鱼+柠檬汁”等高频组合,运营部门据此设置搭配套餐,捆绑销量提升了23%。

银行反欺诈的关联特征识别

阶段:风险特征发现

解决的问题:欺诈交易往往有隐蔽的“组合套路”,比如短时间内“小额试探+大额转账+快速提现”。传统规则引擎靠专家手工编写组合规则,覆盖面小。FP-Growth能从海量历史交易中挖掘高频“特征组合”,辅助风控团队快速发现可疑模式,并作为特征喂给后续分类模型。

具体案例:某支付平台用FP-Growth分析已确认的诈骗交易流水,发现“夜间+新设备+改密+大额”的组合频繁出现在欺诈样本中。该组合被加入风控规则后,欺诈拦截率提升了12%,同时误报率没有明显上升。

医疗症状共现分析

阶段:医学知识发现

解决的问题:慢病管理需要了解哪些症状和指标往往同时出现,传统统计逐一核对组合非常耗时。FP-Growth把每位患者的症状清单视为一条“事务”来构建FP树,快速发现高频症状组合,辅助医生优化问诊流程和健康管理方案。

具体案例:某健康管理平台对2万名糖尿病患者的体征记录进行FP-Growth挖掘,发现“高血糖+高血脂+睡眠差”的支持度显著偏高。平台据此设计了糖尿病联合干预套餐,将相关患者的复诊依从率提升了18%。

Python 演示

python71 行
class FPNode:    def __init__(self, item, parent):        self.item = item        self.count = 1        self.parent = parent        self.children = {}        self.link = None  class FPGrowth:    def __init__(self, transactions, min_sup):        self.tx_list = transactions        self.min_sup = min_sup        self.root = FPNode(None, None)        self.header_map = {}        self.order_map = {}        self.link_map = {}     def build(self):        freq = {}        for tx in self.tx_list:            for it in tx:                freq[it] = freq.get(it, 0) + 1        self.header_map = {k: v for k, v in freq.items() if v >= self.min_sup}        self.order_map = {k: i for i, k in enumerate(            sorted(self.header_map, key=lambda x: -self.header_map[x]))}        self.link_map = {k: [] for k in self.header_map}        for tx in self.tx_list:            items = [it for it in tx if it in self.order_map]            items.sort(key=lambda x: self.order_map[x])            cur = self.root            for it in items:                if it not in cur.children:                    node = FPNode(it, cur)                    cur.children[it] = node                    self.link_map[it].append(node)                else:                    cur.children[it].count += 1                cur = cur.children[it]     def prefix_paths(self, item):        paths = []        for node in self.link_map[item]:            path = []            p = node.parent            while p and p.item is not None:                path.append(p.item)                p = p.parent            paths.append((path, node.count))        return paths     def mine(self):        patterns = []        for item in sorted(self.header_map, key=lambda x: -self.header_map[x]):            for path, count in self.prefix_paths(item):                patterns.append((tuple(path) + (item,), count))        return patterns  if __name__ == "__main__":    orders = [        ["牛奶", "面包", "鸡蛋", "果酱"],        ["牛奶", "面包", "鸡蛋"],        ["牛奶", "面包"],        ["牛奶", "果酱", "鸡蛋"],        ["面包", "果酱"]    ]    fp = FPGrowth(orders, min_sup=2)    fp.build()    for pat, cnt in fp.mine():        print(pat, cnt)
目的:实现了完整的FP-Growth双阶段流程——构建FP树与自底向上挖掘频繁模式,用于在订单数据中找出高频商品组合。
  1. self.header_map = {k: v for k, v in freq.items() if v >= self.min_sup}第一遍扫描后按最小支持度过滤冷门商品,相当于把不够分量的商品剔除出货架。
  2. self.order_map = {k: i for i, k in enumerate(sorted(self.header_map, key=lambda x: -self.header_map[x]))}按热度降序给频繁项编号,保证后续上树时高频项排在路径前面。
  3. items.sort(key=lambda x: self.order_map[x])每个事务先按热度排序再插入,这样不同订单才能共享前缀路径。
  4. if it not in cur.children: ... else: cur.children[it].count += 1树上已有同名节点就计数加一,否则新建节点,这是路径压缩的核心逻辑。
  5. self.link_map[it].append(node)维护项头表与树节点的链接,让后续挖掘能快速定位所有包含某商品的路径。
  6. while p and p.item is not None: path.append(p.item)从节点向上回溯父链,提取前缀路径形成条件模式基。

常见误区

误区“FP-Growth直接建树就能找到频繁项集,不需要像Apriori那样先算支持度,省了一道工序。”

正解FP-Growth依然要计算支持度,而且第一遍扫描数据库就是专门做单项支持度统计的,只有达到最小支持度阈值的商品才允许进入FP树。它省掉的不是“支持度统计”,而是“候选集的逐层生成与验证”。你可以把支持度统计理解为给商品称重,这步省了,货架就乱了。

为何常见:因为FP-Growth的宣传点集中在“不需要生成候选集”,初学者容易把候选集和支持度统计混为一谈,误以为连计数都省了。

误区“FP树就是把每张快递单原封不动挂上去,像文件夹一样存在树上。”

正解FP树存入的每个事务都经过了两次预处理:一是删掉支持度低于阈值的冷门项,二是按支持度从高到低重新排序。此外,多个事务若前缀相同,在树上只保留一条路径并用计数值表达重复次数,这才是“路径压缩”的真正含义。

为何常见:人脑中“树=数据结构”的直觉很自然,容易忽略算法在入树前对数据做过的排序与过滤,从而低估了树的压缩力度。

误区“每递归一层都要把对应的数据子集完整复制一份,内存会越来越大。”

正解条件模式基是由项头表链接回溯出来的“前缀路径集合”,本质上是FP树上指针的遍历结果,不是物理复制整张表。构建条件FP树时也只取过滤后的节点重新组织链接,数据规模随递归快速收缩,因此整体内存占用是受控的。

为何常见:递归算法容易让人联想到“一层层复制数据”,尤其当教材用表格展示CPB时,读者误以为这些表真的被完整存储。

误区“FP-Growth又快又省内存,以后所有关联规则任务都无脑用FP-Growth就好。”

正解FP-Growth在大多数场景下确实优于Apriori,但并非万能。当最小支持度阈值很高时,候选集本来就不多,Apriori的简单实现可能更易维护;当数据量小且需要频繁变更阈值时,Apriori重新扫库的代价反而更低。此外,Apriori天然生成候选项集的结构在某些增量学习场景仍有价值。

为何常见:看到性能对比图上一骑绝尘的FP-Growth,学习者容易忽略“性能优势的前提是低支持度+大数据量”这一适用边界。

面试题

Q1. FP-Growth 名称中的“频繁模式增长”最准确的含义是什么?

单选题 · easy

  • A.频繁项集直接从候选集增长
  • B.频繁项集从候选集逐步筛选
  • C.频繁项集从树路径递归生长← 正确答案
  • D.频繁项集从支持度增长推导

答案:C。FP-Growth 不生成候选集,而是通过FP树上的条件模式基递归挖掘,让频繁项集像树枝一样逐层“长”出来,故称“增长”(Growth)。A、B描述的是Apriori的思路,D是干扰项。

Q2. 在构建FP树之前,FP-Growth 对每个事务做了哪两步预处理?

单选题 · mid

  • A.去除重复项并按字典序排序
  • B.删除冷门项并按支持度降序排序← 正确答案
  • C.保留全部项并按出现顺序排列
  • D.合并同类项并打乱顺序

答案:B。第一步删除支持度低于最小阈值的项,第二步把保留项按支持度从高到低排序。这么做是为了让大量事务共享前缀路径,最大化路径压缩效果。A的字典序和D的打乱顺序都会破坏前缀共享的机会。

Q3. FP树上某个节点的 count 字段表示什么?

单选题 · mid

  • A.该商品在原始数据库中出现的总次数
  • B.从根到该节点的路径在所有事务中出现的次数← 正确答案
  • C.该节点子节点的数量
  • D.该商品在事务中出现的平均位置

答案:B。count 记录的是“以该节点为结尾的前缀路径”在已插入事务中的累计频次,也就是这条路径被多少张订单共享。它既不是单纯商品频次,也非子节点数量。

Q4. “条件模式基”的本质是什么?

单选题 · hard

  • A.原始事务中包含目标项的所有完整事务
  • B.目标项所有前缀路径及对应计数的集合← 正确答案
  • C.目标项在每笔订单中的排列位置
  • D.目标项的支持度大于阈值的证明

答案:B。条件模式基是“所有包含目标项 x 的前缀路径集合”,每条路径附上 x 节点的计数。它通过项头表链接回溯得到,而非重新扫描全部原始事务。A错在它只保留了前缀而不是完整事务,且还带局部计数。

Q5. 相比 Apriori,FP-Growth 的优势体现在哪些方面?

多选题 · mid

  • A.不需要扫描数据库统计支持度
  • B.避免了候选组合的指数级生成← 正确答案
  • C.只需要扫描数据库两次即可建树← 正确答案
  • D.支持内存中直接完成全部挖掘← 正确答案

答案:BCD。FP-Growth 依然要扫库统计支持度(A错)。真正优势在于不生成候选集(B对)、只扫描两遍(C对)、挖掘在内存FP树上完成(D对),这些特性共同带来了数量级的性能提升。

Q6. 最小支持度阈值调高之后,FP树会发生什么变化?

单选题 · easy

  • A.树会变得更高更宽
  • B.树会变小,被过滤的项更多← 正确答案
  • C.树保持不变,仅影响挖掘结果
  • D.树会变成多棵小树

答案:B。阈值调高意味着更多商品因达不到热度门槛被剔除,进入FP树的项更少,事务被过滤得更多,树整体变小。把阈值调低则树变大,可挖掘模式更多但噪音也更多。

Q7. 项头表(Header Table)在FP-Growth中的作用是什么?

单选题 · mid

  • A.存储每个商品的支持度并排序
  • B.建立商品与树节点的快速索引← 正确答案
  • C.记录树的深度和路径长度
  • D.保存所有候选频繁项集

答案:B。项头表不仅记录频繁项及支持度,更关键的是每个项都带有一串链接,指向FP树上所有同名节点。挖掘条件模式基时,通过这个索引能瞬间定位“该商品出现在哪些路径”,省去全树遍历。

Q8. FP-Growth 挖掘时为什么选择“自底向上”的顺序?

单选题 · hard

  • A.因为底部的节点支持度最低,先处理低频项可以快速收敛← 正确答案
  • B.因为底部节点离根最近,遍历代价最小
  • C.因为支持度最高的项一定在底部
  • D.因为FP树的根节点在底部

答案:A。FP树按支持度降序排列,最底部常是支持度较低的项。从低频项开始挖掘,其条件模式基规模小,能快速生成以它为后缀的模式,再逐层上推组合成更大模式。B、C、D在体系结构上不成立,根节点在顶部。

Q9. FP-Growth 并行化实现面临的挑战有哪些?

多选题 · hard

  • A.数据切分不均导致负载倾斜← 正确答案
  • B.各分区的局部频繁项合并后可能不满足全局阈值← 正确答案
  • C.共享前缀的历史路径无法简单分割← 正确答案
  • D.FP树本身不能并行构建

答案:ABC。A对,哈希切分可能让某节点分到大热业务的密集订单;B对,局部频繁但全局不频繁,需要合并后二次过滤;C对,一棵共享前缀树按订单划分后会变成多棵树,原本共享的路径被拆分,可能损失部分压缩效果;D错,FP树可以在各分区并行构建,这是并行化的基础。

Q10. 下列关于 FP-Growth 的说法正确的有哪些?

多选题 · mid

  • A.挖掘过程需要反复扫描原始数据库
  • B.条件FP树是递归挖掘的核心载体← 正确答案
  • C.支持度低于阈值的商品不会出现在FP树中← 正确答案
  • D.FP-Growth比Apriori一定快十倍以上

答案:BC。A错,建树完成后挖掘只在内存树上进行,原始库不再扫描;B对,条件FP树是逐层递归降维的关键;C对,入树前冷门项已被过滤;D错,“一定快十倍”是绝对化表述,性能取决于数据特征和阈值,不可一概而论。