「日拱一码」046 分支定界算法
目录
分支定界算法基本原理
算法基本步骤
应用场景
代码示例——0-1背包问题
算法优化技巧
分支定界(Branch and Bound)是一种用于解决组合优化问题的系统化方法,特别适用于离散优化问题
分支定界算法基本原理
分支定界算法通过以下两个核心操作来系统地搜索解空间:
- 分支(Branch): 将问题分解为更小的子问题
- 定界(Bound): 计算子问题的上下界,剪除不可能包含最优解的分支
算法基本步骤
- 初始化: 创建根节点表示原始问题,设置初始上下界
- 选择节点: 从活动节点列表中选择一个节点进行处理
- 分支: 将选中的节点分解为若干子节点
- 计算界限: 为每个子节点计算上下界
- 剪枝: 删除不可能包含最优解的子节点
- 终止条件: 当所有节点都被处理或满足停止条件时终止
应用场景
分支定界算法常用于以下问题:
- 旅行商问题(TSP)
- 整数规划
- 背包问题
- 调度问题
- 图划分问题
代码示例——0-1背包问题
下面以经典的0-1背包问题为例,展示分支定界算法的实现:
import heapqclass Node:def __init__(self, level, profit, weight, bound, items):self.level = level # 当前处理的物品层级self.profit = profit # 当前总价值self.weight = weight # 当前总重量self.bound = bound # 价值上界self.items = items # 包含的物品索引列表# 定义比较操作符用于优先队列def __lt__(self, other):return self.bound > other.bound # 按bound从大到小排序def bound(node, n, W, items):"""计算节点的价值上界"""if node.weight >= W:return 0profit_bound = node.profitj = node.level + 1total_weight = node.weight# 贪心添加物品直到无法再添加完整物品while j < n and total_weight + items[j][1] <= W:total_weight += items[j][1]profit_bound += items[j][0]j += 1# 如果还有剩余空间,添加部分物品if j < n:profit_bound += (W - total_weight) * (items[j][0] / items[j][1])return profit_bounddef knapsack_branch_and_bound(W, wt, val, n):"""分支定界解决0-1背包问题W: 背包容量wt: 物品重量列表val: 物品价值列表n: 物品数量"""# 按单位价值排序(降序)items = sorted([(val[i], wt[i], i) for i in range(n)],key=lambda x: x[0] / x[1], reverse=True)# 创建优先队列queue = []# 创建根节点root = Node(-1, 0, 0, 0, [])root.bound = bound(root, n, W, items)heapq.heappush(queue, root)max_profit = 0best_items = []while queue:current = heapq.heappop(queue)# 如果当前节点的bound小于已知最大收益,则跳过if current.bound < max_profit:continue# 到达叶子节点if current.level == n - 1:continue# 处理下一物品next_level = current.level + 1next_val, next_wt, idx = items[next_level]# 分支1: 包含下一物品left_weight = current.weight + next_wtleft_profit = current.profit + next_valleft_items = current.items + [idx]if left_weight <= W and left_profit > max_profit:max_profit = left_profitbest_items = left_items.copy()left_bound = bound(Node(next_level, left_profit, left_weight, 0, left_items),n, W, items)if left_bound > max_profit:left_node = Node(next_level, left_profit, left_weight, left_bound, left_items)heapq.heappush(queue, left_node)# 分支2: 不包含下一物品right_bound = bound(Node(next_level, current.profit, current.weight, 0, current.items),n, W, items)if right_bound > max_profit:right_node = Node(next_level, current.profit, current.weight, right_bound, current.items)heapq.heappush(queue, right_node)return max_profit, [items[i][2] for i in best_items]# 示例使用
if __name__ == "__main__":W = 10 # 背包容量wt = [2, 3, 4, 5] # 物品重量val = [3, 4, 5, 6] # 物品价值n = len(val) # 物品数量max_profit, selected_items = knapsack_branch_and_bound(W, wt, val, n)print("最大价值:", max_profit)print("选择的物品索引:", sorted(selected_items))print("选择的物品重量:", [wt[i] for i in selected_items])print("选择的物品价值:", [val[i] for i in selected_items])# 最大价值: 13
# 选择的物品索引: [0, 1, 3]
# 选择的物品重量: [2, 3, 5]
# 选择的物品价值: [3, 4, 6]
算法优化技巧
- 启发式选择规则: 优先处理更有希望的分支
- 预处理: 通过排序或预处理减少搜索空间
- 并行计算: 不同分支可以并行处理
- 缓存中间结果: 存储已计算的分支结果避免重复计算
- 混合算法: 结合其他算法如动态规划进行优化
分支定界算法的时间复杂度在最坏情况下是指数级的,因为可能需要遍历整个解空间。但在实际应用中,通过有效的剪枝可以大大减少搜索空间