Keyboard shortcuts

Press or to navigate between chapters

Press ? to show this help

Press Esc to hide this help

剪枝

复习

  • 回溯与搜索树:把选择展开成树并 DFS
  • 大 O 记号:衡量搜索规模的增长
  • 算法与正确性:剪枝不能误删可行解

TL;DR

  • 剪枝提前放弃不可能产生答案的分支
  • 它能大幅缩小搜索树的规模
  • 常用约束判断和上下界来剪枝
  • 剪枝必须保证不会误删可行解

正文

  回溯能把所有解都找出来,可搜索树常常大得吓人。如果能让它在搜索过程中早点放弃那些显然没希望的分支,速度就能大幅提升。这个动作,就是剪枝(pruning)。

把死路提前砍掉

  搜索树里,很多分支其实从一开始就注定失败。剪枝的思想是:在深入之前就判断出来,直接跳过整棵子树,不去走。

  比如摆皇后的问题:每放一个皇后,都要满足“不同行、不同列、不同斜线”。如果某一列已经冲突了,那这一整条分支再怎么往下放都不可能成立——于是立刻回头,省下后续所有徒劳的尝试。

两类常见的剪枝

  剪枝的依据,大致有两类:

  • 可行性剪枝:根据问题的约束,判断“当前状态已经违反规则”,那就没必要继续
  • 最优性剪枝:如果是求最优解,当“当前部分解已经比已知的最好解更差”时,就没必要往下走了

  第二类很像我们平时的思考:如果一条路走到一半,花的力气已经超过手头最好的方案,那再往下也没意义,及时掉头。

别把正确答案剪掉

  剪枝威力很大,但有一条铁律:绝不能剪掉任何可能产生正确答案的分支。

  如果约束判断写得过严、或者上下界估计得不准,就可能把真正的解也一并砍掉,导致算法给出错误结果。“剪得狠”和“剪得对”是两回事——宁可保守一点,也不能误伤。

  你会发现,剪枝背后的思路,和前面许多优化如出一辙:用更多的信息(约束、界限)换更少的搜索。 信息越准,越能精准地砍掉死路,又不伤及无辜。

思考题 1

剪枝为什么会显著加快回溯搜索?

思考题 2

设计剪枝时,最需要避免什么错误?

小结

知识点

  • 剪枝提前放弃不可能产生答案的分支
  • 分为可行性剪枝与最优性剪枝
  • 它可大幅缩小搜索树规模
  • 剪枝不能误删任何可行解

参考资料

  1. Wikipedia(zh):回溯法:常与剪枝配合使用
  2. Wikipedia(zh):分支定界:利用上下界进行剪枝的搜索方法

思考题答案(仅供参考)

思考题 1

  因为搜索树中很多分支注定无法产生答案,剪枝能在深入之前就识别并跳过整棵子树,避免了大量无谓的尝试,从而把搜索规模显著缩小,速度大幅提升。

思考题 2

  最需要避免的是“误剪”——把可能产生正确答案的分支也剪掉,导致结果错误。约束判断和界估计必须保证不排除任何可行解,必要时宁可保守一些。

协议

  本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。

封面图

设计师 | 南国微雪