分支定界算法的基本介绍:分支定界算法作为在解决整数规划问题的精确算法中最基本、最底层的算法,由伦敦政治经济学院的 Ailsa Land 和 Alison Doig 于1960年首次提出,是一种分而治之、先松后紧、张弛有度的算法。而后在1963年,由 John D.C. Little 在发表的关于TSP的研究中,明确使用“Branch and Bound“ 对该方法进行命名。
分支定界算法的一般步骤和主要特征梳理如下:
(1)松弛:线性松弛,将整数规划(IP)松弛为对应的整数规划线性松弛(IPr);
(2)分支:通过不断分支,将初始IPr的可行域连续划分为多个子区域;
(3)求解:求解每个子区域对应的子问题;
(4)更新:根据子问题的最优值、解的整数特性、目标函数的优化方向,来更新全局上下界
(5)终止:获得最优解,或上下界在一定容差界限内。
线性松弛
定义:将整数规划(IP)的整数约束和0-1约束全部去掉,会得到一个线性规划(LP),该线性规划(LP)被称为原来整数规划的线性松弛,本文用符号(IPr)来表示(IP)的线性松弛问题。
分支定界树
这一个不断二分的树形结构,也经常被称为分支定界树,Branch-and-Bound Tree(BB tree)
BB tree的一些重要的概念和特征:
(1)BB tree的根节点,无前驱节点,相当于IP对应的线性松弛问题IPr;
(2)BB tree是满二叉树结构,树中的节点要么是出度为2的节点,要么是叶子节点,出度为0,不存在出度为其他值的节点,相当于对于树中节点对应的任何一个问题,要么能分为两个子问题,要么不可再分;
(3)BB tree子节点的度等于2,其后有2个后继节点,相当于将当前问题分为两个可行域无交集的子问题;
(4)BB tree叶子节点的度等于0,其后有0个后继节点,当前节点已被剪枝,相当于当前问题已被查明/洞悉,不会再分;
(5)BB tree节点的查明/洞悉,被查明的节点无需再分支,我们可以将其进行剪枝,若节点对应的问题满足下面的任何一个条件,则可判断该节点已被查明;
-
该节点不可行。即可行性剪枝,节点对应的问题无意义
-
该节点得到了一个整数解。即最优性剪枝,节点对应的问题已经完成了使命
-
该节点的线性松弛问题的最优值,不超过全局下界(最大化问题)。即界限剪枝,节点松弛之后求得的解,还没有目前的全局下界高,可以预见到继续分支不会对问题全局下界有任何影响,无分支的必要了
(6)BB tree的终止条件,当所有的叶子节点均被查明,或全局上下界的在所给的容差界限内。
分支定界算法解析
分支定界算法始终围绕着一颗搜索树进行的,我们将原问题看作搜索树的根节点,从这里出发,分支的含义就是将大的问题分割成小的问题。
分支的过程就是不断给树增加子节点的过程。而定界就是在分支的过程中检查子问题的上下界,如果子问题不能产生一比当前最优解还要优的解,那么砍掉这一支。直到所有子问题都不能产生一个更优的解时,算法结束。
最大化问题的分支定界算法伪代码如下所示:
最大化问题的分支定界算法流程图:
分支、搜索与剪枝策略
在实际的操作过程中,仍有一些内容需要关注,主要包括分支、搜索与剪枝策略三部分,对于这几部分的设计与选择会直接影响算法的求解效率。
(1)分支策略
在选取分支变量对父节点的问题进行分支时,可能存在若干个非整数变量,那么如何选择具体的分支变量,下面给出了几种常见的可选分支策略:
-
最不可行分支:选择分数部分最接近0.5的(最偏离整数的)变量进行分支;
-
最大分数值分支:选择分数部分最大的变量进行分支;
-
伪检验数分支:在使用启发式方法近似估计约束对偶变量的基础上,得到变量检验数的估计值,进一步评估变量的分支对目标函数的影响,最终选择更有利的分支变量;
-
强分支:在分之前首先估计哪个分支会给目标函数带来最大改进,然后选择该变量进行分支。
(2)搜索策略
在节点队列中,对于不同子节点的选择会直接影响BB tree的搜索策略,一些常见的遍历策略如下所示。
-
深度优先搜索:在发生剪枝前,下一次搜索的节点一定是当前节点的后继,节点队列“先入后出”一般可保证深优;
-
广度优先搜索:优先遍历同一层次的节点,完毕后再遍历下一层次的节点,节点队列“先入先出”一般可保证宽优;
-
最好界限优先搜索:根据问题的优化方向,选择局部LB或者局部UB最好的节点首先被搜索。
(3)剪枝策略
-
查明一个节点:包含前述的3个查明条件(可行、最优、界限)之一,即可进行剪枝;
-
优超关系:更加灵活和动态的一种“界限剪枝”,如果基于一些信息,在任何时候都可以确定节点y的最佳子节点至少与节点x的最佳子节点一样好,那么就说y优超了x,那么x就失去了搜索的必要,将其剪枝。
案例分析一:利用分支定界算法求解最大化优化问题
代码逻辑描述:
1.求解线性松弛的原问题,判断是否可行
2.当未被探索的问题集不为空,且上下界的gap大于阈值,则根据搜索策略选择子问题进行探索
3.算法的终止条件 当未被探索的问题集不为空;如果上界等于下界,或者上界-下界的gap很小,则算法终止。
from gurobipy import *
import copy
import numpy as np
import matplotlib.pyplot as plt
RLP = Model("relaxed IP")
x = {}
# 设置决策变量
for i in range(2):
x[i] = RLP.addVar(lb=0, ub=GRB.INFINITY, vtype=GRB.CONTINUOUS, name = 'x_'+str(i))
# 设置目标函数和约束条件
RLP.setObjective(100*x[0]+150*x[1],GRB.MAXIMIZE)
RLP.addConstr(2*x[0]+x[1]<=10)
RLP.addConstr(3*x[0]+6*x[1]<=40)
# 求解解松弛问题
RLP.optimize()
class Node:
def __init__(self):
self.model = None
self.x_sol = {} #solution of sub-problem
self.x_int_sol = {} #round integer of solution
self.local_LB = 0 #local bound of node, sub-problem
self.local_UB = np.inf
self.is_integer = False #is integr solution
self.branch_var_list = [] #store branch variable
def deepcopy(node):
new_node = Node()
new_node.local_LB = 0
new_node.local_UB = np.inf
new_node.x_sol = copy.deepcopy(node.x_sol) #solution of sub-problem
new_node.x_int_sol = copy.deepcopy(node.x_int_sol) #round integer of solution
new_node.branch_var_list = [] # do not copy, or that always use the same branch_var_list in sub-problem
# deepcopy, or that the subproblem add all the new constraints sub-problem->infeasible
new_node.model = node.model.copy() # gurobi can deepcopy model
new_node.is_integer = node.is_integer
return new_node
def branch_and_bound(RLP):
# initialize the initial node
RLP.optimize()
global_UB = RLP.ObjVal
global_LB = 0
eps = 1e-3
incumbent_node = None
Gap = np.inf
'''
branch and bound
'''
# create initial node
Queue = []
node = Node()
node.local_LB = 0
node.local_UB = global_UB
node.model = RLP.copy()
node.model.setParam("OutputFlag",0)
node.cnt = 0
Queue.append(node)
cnt = 0
Global_UB_change = []
Global_LB_change = []
while(len(Queue) > 0 and global_UB - global_LB >