“分支限界法 (Branch and Bound)” 处理 NP 完全问题中的 优化问题(比如最短路径、最小代价等),它与回溯法的核心区别在于多了一个估价函数(lower bound),提前判断某条路径“必定不会更优”,从而剪枝。
✅ 图1:如何判断某个部分解(subproblem)要不要继续
⭐ 场景设定:
你想从 s 到 t 找最短路径,现在有两个选择:
-
已知从
s → t
路径代价是 10。 -
某条路径从
s → u
的代价是 11,但你还没知道u → t
的代价。
❓问题:要不要继续尝试从 s → u → t
?
-
**11 已经比 10 大了!**即使
u → t
是免费的,整条路径也不可能更优。 -
所以,这种部分路径
s → u
就可以“提前剪枝”,不浪费时间。
💡 图中强调:
“我们用一个 快速计算的 lower bound(下界)来判断是否值得继续探索 subproblem。”
✅ 图2:分支限界算法框架(与回溯法的最大区别)
🧠 代码核心流程:
start with P0
while there are still subproblems:
选择一个 subproblem P
扩展出更小的 subproblems P1, P2, ..., Pk
对每个 Pi:
如果 Pi 是完整解 → 更新当前 best 答案
如果 Pi 的 lowerbound < bestsofar → 加入待扩展集合
否则 → 丢弃(因为已知一定不会更优)
🚩对比回溯法(Backtracking):
方法 | 是否估算最优性 | 剪枝依据 |
---|---|---|
Backtracking | ❌ 不估计未来 | 如果当前不合法 → 剪 |
Branch and Bound | ✅ 估计最优性 | 如果当前路径即使最乐观也不会更好 → 剪 |
📌 关键术语解释:
-
subproblem:部分解决的问题,比如路径只走了一半。
-
active subproblem:当前还没处理完,放在待扩展集合
S
中。 -
lowerbound(Pi):快速估计“这个分支最乐观能做到的最低成本”。
-
bestsofar:目前遇到的最优完整解的成本。
需要我把这个内容再举个现实生活的例子来解释一下吗?比如旅游路线、外卖送餐之类的?
用一个现实生活中的外卖送餐场景来帮你更好地理解 Branch and Bound(分支限界法) 的思路 👇
🍔 外卖送餐员找最优送餐路线(现实版 Branch and Bound)
🧩 场景设定:
你是一个外卖员,要从仓库出发,把餐送到 5 个客户家(A, B, C, D, E),然后回到仓库。
你希望找到一条送餐总路程最短的路线。
🚶 解法 1:暴力法(回溯)
你尝试所有排列,比如:
-
仓库 → A → B → C → D → E → 仓库
-
仓库 → B → A → C → D → E → 仓库
-
…总共 5!=1205! = 120 条路径要试!
很累!你会一条条尝试,每次走完一条才知道总距离。
🚀 解法 2:分支限界法(Branch and Bound)
你在尝试路径时,如果发现中途已经 “肯定不如目前最优”,你就不往下走了,提前剪掉!
✅ 举个具体例子:
你已经知道下面这条路线用了 20公里:
-
仓库 → A → B → C → D → E → 仓库:20 km
然后你尝试另一条路线:
-
仓库 → D → E → …
此时你只走了 “仓库 → D → E”,但这段已经花了 21公里。
你还没走完剩下 C、B、A,但就算剩下的都0公里,加起来也已经超过 20 了!
🔥 所以你立刻 放弃这个分支,不再往下扩展!
🌟 本质解释:
术语 | 现实中的含义 |
---|---|
Subproblem | 当前已走部分路径,比如“仓库→D→E” |
Active subproblem | 当前还没被剪掉,正在考虑的部分路径 |
Lower bound | 对当前路径最乐观的估计,比如“现在走了21公里,最乐观再加5公里” |
Best-so-far | 目前发现的最短路线(比如 20公里) |
剪枝依据 | 如果某条路径的 lower bound ≥ best-so-far,就直接跳过它! |
🧠 总结口诀:
“当前路径看起来都比我已知最优还差,那我就不走了!”
🌳 为啥叫 Tree Bound?
因为下界是靠 生成树 (MST) 来估算的,而不是完整路径。它能快速给你一个“不可能比这个更优”的基准,有助于剪枝。
🎯 用处总结:
-
在 TSP 的 branch and bound 过程中,你可以对当前路径计算下界。
-
如果这个下界已经比当前最优路径还差,那你就可以 安全地剪掉这个分支。
🧺 类比现实生活(快速举例):
你是一个快递员,要送 10 个包裹,现在已经送了 3 个。你不知道送完整 10 个最优路径是什么,但:
-
你可以算出剩下 7 个包裹用最短路线连接起来的距离(MST),
-
你也可以算出你现在已经走了多少路,
-
你还能估算你从当前位置连到剩下包裹的最短两条路。
这些加起来就是 你还没走完的下界 —— 如果这都已经超出你目前最佳方案,那说明这条路不值得走了,剪枝!
💡目标问题回顾:TSP 的本质
我们要解决的是:
在一个图中找一条最短的闭环路径,刚好访问每个城市一次。
✅ 分两部分理解图示内容
📌 第一页(第12页)关键解释:
1. 目标是什么?
我们正在尝试完成一个从 A
开始的 TSP 路径,并在探索从 A -> B -> C
这条路径(也叫 partial tour)。
2. 什么是 Lower Bound(LB)?
我们不知道最终的最短路径(OPT)是多少,但我们可以快速估计一个「下界」来告诉自己:
「这个分支至少要这么贵。」
3. 如何估这个下界?
看公式:
LB = 边AB的权重 + 从B到剩下城市中最便宜的边 + 剩下城市形成的最小生成树(MST)
举例说明:
-
A -> B = 2
-
B -> V−S
中最轻的是 2 -
剩下的城市构成的 MST 权重是 6
-
所以总下界 =
2 + 2 + 6 = 10
📌 第二页(第13页)只是换了路径
换成 A -> B -> C
,此时:
-
A -> B = 2
-
C -> V−S
最轻是 2 -
MST = 5
-
所以下界 =
2 + 2 + 5 = 9
这个路径比上一页的 LB = 10
好,所以它更有可能是我们要找的最优解。
🔁 第三页(第14页)展示「分支过程」的动态图
这一页展示的是分支过程的动画(如何一步一步扩展子问题):
-
黄色:当前选中的子问题
-
红色:还未扩展的子问题(还可能是解)
-
灰掉:已经被排除的子问题(因为下界太大)
-
每个节点底下的
Cost: xx
就是该分支的下界
你会看到,算法优先展开下界小的节点,来尽快找到好的解,同时丢弃一些注定不是最优的路径。
🧠 总结一句话:
Branch and Bound 就是:尝试所有可能的解,但用“下界”快速剪掉不值得继续探索的路径,从而避免爆炸性搜索。
第 15 页:Local Search 的基本思想
这一页解释的是局部搜索(Local Search),它是一种受“生物进化”启发的优化策略。
🔍 核心流程:
-
从任意初始解
s
开始。 -
在
s
的邻居解中,找出一个更好的解s'
(代价更小)。 -
用
s'
替换s
,重复上面过程。 -
如果找不到更好的邻居了,就停止,返回当前的
s
。
🧠 关键词解释:
-
邻居(neighborhood):对当前解
s
进行一次“轻微”修改后得到的解集合。 -
保持可行性(keeps feasibility):
s'
还是一个合法的解。 -
设计核心(central design decision):就是如何定义“邻居结构”——这是算法能不能做得好的关键。
📦 直观比喻:
就像你在爬山,但只能看到你脚下周围的高度(邻居),你一步步往高处走,直到你站在一个“山顶”,那就是局部最优。
第 16 页:应用到 TSP:2-change & 局部最小值
这一页将局部搜索用在了旅行商问题(TSP)上。
🛣️ 2-change 是什么?
-
选一个 TSP 路线
s
。 -
移除两条边,换入另外两条边,形成新路线。
-
这样生成的新路线就是
s
的一个邻居。
🧱 问题:局部最小值(Local Minima)
-
有些时候,所有 2-change 都不能改进当前路线,虽然它可能不是最优。
-
这叫做“陷入局部最优”。
🛠️ 怎么改进?
-
用 3-change,就是换 3 条边,但代价是:邻居数从 $O(n^2)$ 增加到 $O(n^3)$。
-
🧮 所以是个 效率 vs 精度的权衡(trade-off)。
✅ 总结:
局部搜索是一种渐进式优化算法,非常适合处理 NP 难问题。它灵活、简单,但可能会陷入局部最优。改进方式是增加邻居范围。
直观地解释 2-change 和 3-change 是在“换边”干什么,并且它们为什么能改进旅行路径(TSP tour)。
🛣️ 什么是 TSP 路径优化?
你在做旅行商问题(TSP):目标是走一圈回到起点,路径最短。
✅ 1. 什么是 2-change?
从当前路径中删除两个边,重新连两个边,把“路径扭一扭”成更短的路线。
举个图上例子(第 16 页中间图):
-
图左边:中间有两条红色对角线(交叉了),路径拐得很奇怪。
-
2-change操作:把这两条交叉边删掉 → 换成不交叉的新边。
-
得到右边图:整条路径更光滑、也更短了!
这就像原来你走了一段“交叉路线”,后来决定绕一下更顺了。
✅ 2. 那什么是 3-change?
删除三条边,再连回三条边,操作空间更大。
看你这张图第 17 页的 4 个步骤:
步骤 | 说明 |
---|---|
(i) | 随机起点,路径杂乱,红色三边即将被移除 |
(ii) | 换了三条边,路径更整齐了 |
(iii) | 再换三条边,继续变顺 |
(iv) | 已经很顺了,不再换了,局部最优完成 |
📌 关键:3-change 能跳出 2-change 无法优化的局部最小值,但代价是它考虑的邻居多,算法更慢($O(n^3)$)。
🧠 总结对比:
操作 | 删除几条边 | 优点 | 缺点 |
---|---|---|---|
2-change | 2 | 简单、快($O(n^2)$) | 容易卡在局部最优 |
3-change | 3 | 优化能力强 | 慢($O(n^3)$) |
这两页讲的是**图划分(Graph Partitioning)问题及其使用局部搜索(Local Search)**来求解的策略,以下是清晰、详细的解释:
🌐 图划分问题(Graph Partitioning)
❓问题定义:
-
输入:一个无向图 G=(V,E),每条边有非负权重。
-
要求:将顶点集合 V 分成两个部分A和 B,使得:
-
每部分至少有 α∣V∣个点(通常 \alpha = \frac{1}{2},即对半分)。
-
-
目标:最小化“割的容量”,也就是边从 A 到 B 的总权重。
🧠 直觉理解:
想象有一个公司组织重组,要把员工分成两组,但不希望打破太多重要的合作关系(即连接两边的“边”要尽量少,或者权重低)。
💡 局部搜索求解法(Local Search)
这是应对 NP-Hard 问题常见的启发式方法,思想如下:
🔁 解法步骤:
-
初始划分 A,B 随便选(但满足大小要求)。
-
迭代优化:在“邻居解”中搜索改进方案。
-
邻居解的定义:交换 A 中一个点 a和 B 中一个点 b。
-
如果交换后成本更小(割边的权重总和更低),就更新解。
-
不断重复直到找不到更好的交换(达到局部最优)。
-
📉 图示说明:
-
左下图表示某次交换 a,b 后,割边的总成本下降了 Δcost=−4。
-
右图展示了一个局部最优状态:已经找不到任何交换可以进一步减少成本。
🔄 画重点:
-
✅ 优点:算法简单,通常能迅速找到不错的解。
-
⚠️ 缺点:容易卡在局部最优,不是全局最优。
-
💡 提升策略:比如结合模拟退火、随机扰动、爬山算法等跳出局部最优。
✅ 第 20 页:Randomization(随机化)
核心思想:
不要只从一个初始解出发做局部搜索,因为容易卡在局部最优解(local optima)。所以——多次随机启动。
图中逻辑:
-
有多个局部最优解(比如 cost = 2 的有 4 个);
-
真正的最优解只有一个(cost = 0);
-
如果你随机从一个状态开始搜索,有机会遇到那个 cost = 0 的状态;
-
多试几次,总能遇到更好的解。
🔁 策略总结:
-
随机选初始状态;
-
随机选邻居来爬山;
-
反复尝试,提升找到好解的概率。
🔥 第 21 页:Simulated Annealing(模拟退火)
核心思想:
允许偶尔“走坏一步”,从而跳出局部最优解!
类比解释:
爬山法(local search)像是登山,一路往上,但容易卡在“小山丘”;而模拟退火就像有时候“先跳下去”,再去爬更高的山。
公式解读:
if 新解更好:接受
else:以一定概率接受坏解(这个概率会随“温度”T变小而越来越低)
图示说明:
右图展示了这过程:初始解在左边山谷,若允许偶尔走差的路径,就能跳出山谷,最终走向右边的“全局最优”。
✅ 第 22 页:总结(Conclusions)
总结整份讲义里讲的方法:
类型 | 方法 | 说明 |
---|---|---|
精确求解 | Backtracking、Branch & Bound | 时间指数级,但能找到最优解 |
近似求解 | Local Search、Randomization、Simulated Annealing | 找近似最优解,更快,但不一定最优 |
设计权衡 | 通过调参数控制“解的质量” vs “运行时间” | 比如 neighborhood 的大小、温度下降速度等 |
💡类比帮助理解(现实生活例子):
想象你是个美食探店达人:
-
Backtracking:穷尽全城每家餐厅,试完再回溯,非常累,但你一定能找出最棒的餐厅。
-
Local Search:你选了一家试吃,接着去附近再换一家“略好”的,最后找到“附近最好”的——但也许不是全城最棒的。
-
Simulated Annealing:你试了几家后,愿意有时“坐远一点的地铁”去尝另一家可能更差的店,但这可能意外帮你跳出“附近小圈子”。
-
Randomization:每次从地图上随机挑一家开始尝试,换换口味、碰碰运气。