7.1 分支限界法#
概念說明#
分支限界法(Branch and Bound, B&B)是解決 NP-hard 優化問題最通用的精確框架。它的基本架構是系統化的搜尋(通常是 DFS 或最佳優先),但在每個搜尋節點上多做兩件事:
- 分支(Branch):把目前的子問題切成幾個更小的子問題。最常見的分支決策是「某個物品/頂點/變數:包含它 vs. 不包含它」。
- 限界(Bound):為每個子問題計算一個下界(對最小化問題)——即「這個子問題即使繼續搜下去,最好也只能達到這個值」。若下界已不優於目前找到的最佳解,整個子樹直接捨棄(剪枝)。
限界函數的品質決定一切:估計越緊、剪枝越多。以 0/1 背包為例,常用下界是「鬆弛成分數背包」:剩餘容量允許裝入物品的分數部分,這個問題可用貪婪法在 O(n) 內解出,且其最優值是原問題的上界(對最大化而言),可作為限界估計。
分支限界法是商業求解器(如 Gurobi、CPLEX)的骨幹技術之一,與整數線性規劃結合成「分支切割法」(Branch and Cut),能解出驚人規模的精確實例。它的最大魅力在於模組化:只要能定義分支規則與限界函式,任何 NP-hard 優化問題都能套用同一個搜尋框架。
演算法步驟(以 0/1 背包最大化為例)#
- 維護一個優先佇列(max-heap),節點記錄:層數 i、已累積價值 v、剩餘容量 c、當前最佳可能上界 ub。
- 初始節點:i = 0、v = 0、c = W,計算其分數背包上界。
- 反覆取出上界最大的節點:若 ub ≤ 目前已找到的最佳解,剪枝結束(佇列中不可能更優)。
- 分支「不拿第 i 個物品」與「拿第 i 個物品(若放得下)」兩個子節點。
- 對每個子節點計算新的分數背包上界;上界 ≤ 最佳解者捨棄。
- 到達葉節點(i = n)時更新最佳解。
- 佇列空則輸出最佳解。
Python 程式碼範例#
import heapq
def frac_upper_bound(values, weights, i, cap):
"""分數背包上界:貪婪裝填剩餘容量"""
total = 0.0
order = sorted(range(i, len(weights)),
key=lambda k: -values[k] / weights[k])
for k in order:
if weights[k] <= cap:
cap -= weights[k]
total += values[k]
else:
total += values[k] * cap / weights[k]
break
return total
def knapsack_branch_and_bound(values, weights, W):
n = len(values)
# 依單位價值排序可加強剪枝
idx = sorted(range(n), key=lambda k: -values[k] / weights[k])
values = [values[k] for k in idx]
weights = [weights[k] for k in idx]
heap = [(-frac_upper_bound(values, weights, 0, W), 0, 0, W, [])]
best_v, best_items = 0, []
nodes = 0
while heap:
neg_ub, i, v, c, items = heapq.heappop(heap)
ub = -neg_ub
nodes += 1
if ub <= best_v or i == n:
best_v, best_items = max(best_v, v), items
continue
# 分支:拿第 i 個物品
if weights[i] <= c:
heapq.heappush(heap, (
-(v + values[i] +
frac_upper_bound(values, weights, i + 1,
c - weights[i])),
i + 1, v + values[i], c - weights[i],
items + [idx[i]]))
# 分支:不拿
heapq.heappush(heap, (
-(v + frac_upper_bound(values, weights, i + 1, c)),
i + 1, v, c, items))
return best_v, best_items, nodes
values, weights, W = [40, 30, 50, 10], [4, 3, 6, 2], 8
print(knapsack_branch_and_bound(values, weights, W))
複雜度分析#
- 最壞情況:O(2ⁿ) 個節點,每個節點的限界計算 O(n log n),總計仍是指數——B&B 不改變最壞複雜度。
- 實際表現:好的限界函式常使實際展開節點遠小於 2ⁿ,許多數百個物品的隨機背包實例可在毫秒級解出。
- 空間:最佳優先搜尋需 O(節點數) 的佇列;改用 DFS 版本(深度優先 + 限界)可將空間降到 O(n)。
- 加強手段:更緊的限界(拉格蘭日鬆弛、線性規劃鬆弛)、更好的分支變數選擇、支配性測試。
練習#
- 統計 B&B 在 n = 15~25 隨機背包實例上展開的節點數,與 2ⁿ 比較,計算剪枝率。
- 把限界函式換成「僅用剩餘物品價值總和」(較鬆的上界),比較剪枝率下降多少,體會限界品質的影響。
- 將分支限界法套用到 TSP:以「當前路徑長 + 最小出邊和」作為下界,對 10 個城市的實例求解,與暴力法答案比對。