Python实现分枝定界法求解整数规划问题

分枝定界法(Branch and Bound)是一种求解整数规划问题的常用算法,其既可以求解纯整数规划问题,也可以求解混合整数规划问题。这种方法的基本思想是对有约束条件的最优化问题的所有可行解空间进行搜索。

在分枝定界法中,全部可行解空间被反复地分割为越来越小的子集,这个过程称为“分支”。同时,对每个子集内的解集计算一个目标下界(对于最小值问题)或上界(对于最大值问题),这个过程称为“定界”。

在每次分枝后,凡是界限超出已知可行解集目标值的那些子集不再进一步分枝,这样可以节省计算资源,这个过程称为“剪枝”。通过这种方式,算法能够逐步缩小搜索范围,直至找到最优解。

分枝定界法的具体步骤包括:

  1. 问题建模:首先,将原问题转化为组合优化问题,并确定优化目标。这是将实际问题抽象化的过程,为后续的算法处理打下基础。
  2. 初始化树:将原问题构建成一个树状结构,其中每个节点代表问题的一个子问题。这个树状结构用于表示问题的所有可能解空间,并作为后续分枝和定界的基础。
  3. 选择分支:从树中选择一个节点进行分枝。通常,选择一个约束条件,将问题拆分成两个或多个子问题。这些子问题将作为新的节点添加到树中。
  4. 求解子问题:对每个子问题,使用适当的方法(如线性规划)求解,得到子问题的解以及对应的目标函数值。
  5. 定界:根据求解得到的子问题解,更新整个问题的上界和下界。对于最小化问题,下界是到目前为止找到的最小目标函数值,上界则是可能的最大目标函数值;对于最大化问题则相反。
  6. 剪枝:如果某个子问题的目标函数值已经超过当前已知的最优解(对于最小化问题)或者低于当前已知的最劣解(对于最大化问题),那么可以剪去这个子问题的整个树,因为它不可能包含更好的解。剪枝是分枝定界法中减少计算量的关键步骤。
  7. 重复过程:继续选择未处理的节点进行分枝、求解子问题、定界和剪枝,直到找到最优解或确定无解为止。

在整个过程中,分枝定界法通过不断地将问题分解为更小的子问题,并利用定界和剪枝技术来减少计算量,从而有效地求解整数规划问题。以下是Python实现分枝定界法:

import gurobipy as gp
from gurobipy import GRB

def heuristic_solve(problem):
    problem.Params.OutputFlag = 0
    problem.optimize()
    if problem.status == GRB.INFEASIBLE:
        return None, None
    return problem.ObjVal, problem.getVars()

def choice_node(condidate_node):
    node = condidate_node.pop(0)
    return node, condidate_node

class Node:
    def __init__(self, model, upper_bound, lower_bound, candidate_vars):
        self.upper_bound, self.lower_bound = upper_bound, lower_bound
        self.model = model
        self.candidate_vars = candidate_vars.copy()
        assert(upper_bound >= lower_bound), "upper bound is less than lower bound"

    def optimize(self, heuristic_solve):
        self.obj_values, self.solution = heuristic_solve(self.model)
        if self.obj_values == None:
            return "infeasible"
        return "feasible"

    def update_upper_bound(self):
        if self.upper_bound > self.obj_values:
            self.upper_bound = self.obj_values
            assert(self.lower_bound <= self.obj_values)
            assert(self.lower_bound <= self.upper_bound), "upper bound is less than lower bound"

    def update_lower_bound(self):
        self.lower_bound = self.obj_values
        assert(self.lower_bound <= self.obj_values)
        assert(self.lower_bound <= self.upper_bound), "upper bound is less than lower bound"

    def is_integer(self):
        for var in self.solution:
            if 0 < var.x and var.x < 1:
                return False
        return True

    def is_child_problem(self):
        if self.candidate_vars:
            return True

    def get_child_problem(self):
        self.child_left, self.child_right = self.model.copy(), self.model.copy()
        branch_index, self.condidate_child_vars = self.choice_branch(self.candidate_vars)
        self.child_left.addConstr(self.child_left.getVars()[branch_index] == 0)
        self.child_right.addConstr(self.child_right.getVars()[branch_index] == 1)
        node_left = Node(self.child_left, self.upper_bound, self.lower_bound, self.condidate_child_vars)
        node_right = Node(self.child_right, self.upper_bound, self.lower_bound, self.condidate_child_vars)
        return node_left, node_right

    def choice_branch(self, candidate_vars):
        self.condidate_child_vars = self.candidate_vars.copy()
        branch_index = self.condidate_child_vars.pop(0)
        return branch_index, self.condidate_child_vars

    def write(self):
        self.model.write("model.lp")


model = gp.Model("mip1")
x = model.addVars(10, name = 'x', vtype = GRB.BINARY)

model.setObjective(x[0] + x[1] + 2*x[2] + 2*x[8] + x[9], GRB.MAXIMIZE)
model.addConstr(x[0] + 2*x[1] + 3*x[2] + 5*x[3] + 3*x[4] <= 8, "c0")
model.addConstr(2*x[3] + 2*x[4] + 3*x[5] + 5*x[6] + 3*x[7] <= 10, "c1")
model.addConstr(x[7] + x[8] + 3*x[9] <= 4, "c2")
model.addConstr(2*x[0] + x[2] + 3*x[7] + 3*x[8] + 2*x[9] <= 8, "c3")
model.addConstr(x[7] + x[8] + 3*x[9] >= 1, "c4")
model.addConstr(2*x[4] + 2*x[5] + x[6] + 5*x[7] + 3*x[8] >= 4, "c5")
model.optimize()
model.write("model_integer.lp")

upper_bound, lower_bound = float('inf'), 0
model_relax = model.relax()
root_node = Node(model = model_relax, upper_bound = upper_bound, lower_bound = lower_bound, candidate_vars = [i for i in range(model.NumVars)])
candidate_node = [root_node]
current_optimum = None

while candidate_node:
    node, candidate_node = choice_node(candidate_node)
    if node.upper_bound <= lower_bound:
        print("prune by bound")
        continue
    model_status = node.optimize(heuristic_solve)
    if model_status == 'infeasible':
        print("prune by infeasiblity")
        continue
    node.update_upper_bound()
    if node.upper_bound <= lower_bound:
        print("prune by bound")
        continue
    if node.is_integer():
        node.update_lower_bound()
        if node.lower_bound > lower_bound:
            lower_bound = node.lower_bound
            current_optimum = node.solution
        continue
    if node.is_child_problem():
        child_node1, child_node2 = node.get_child_problem()
        candidate_node.append(child_node1)
        candidate_node.append(child_node2)

print("lower_bound: ", lower_bound)
print("optimum:", current_optimum)



运行结果如下:

  • 22
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
以下是使用分枝定界求解混合整数线性规划问题Python代码示例: ```python from scipy.optimize import linprog import math # 最大化目标函数的系数向量 c = [-3, -2, -4] # 不等式约束的系数矩阵 A = [[1, 1, 2], [2, 1, 1], [1, 3, 1]] # 不等式约束的右端常数向量 b = [5, 7, 3] # 变量的上下界 bounds = [(0, None), (0, None), (0, 1)] # 初始的松弛线性规划问题 res = linprog(c, A_ub=A, b_ub=b, bounds=bounds, method='simplex') # 分枝定界的主函数 def branch_and_bound(c, A, b, bounds, res): # 若当前的松弛线性规划问题已经没有整数解,则返回None if not all((map(lambda f: f.is_integer(), res.x))): return None # 若当前的松弛线性规划问题已经取得整数解,则返回该解 if all((map(lambda f: f.is_integer(), res.x))) and res.fun.is_integer(): return res # 否则,进行分枝操作 else: # 找到第一个非整数变量 for i, x in enumerate(res.x): if not x.is_integer(): break # 分别构造两个新的线性规划问题,一个限制x[i]的上界为整数部分,一个限制x[i]的下界为整数部分+1 bounds1 = bounds.copy() bounds1[i] = (0, math.floor(res.x[i])) res1 = linprog(c, A_ub=A, b_ub=b, bounds=bounds1, method='simplex') bounds2 = bounds.copy() bounds2[i] = (math.ceil(res.x[i]), 1) res2 = linprog(c, A_ub=A, b_ub=b, bounds=bounds2, method='simplex') # 分别对两个新问题进行递归调用 res1 = branch_and_bound(c, A, b, bounds1, res1) res2 = branch_and_bound(c, A, b, bounds2, res2) # 返回两个新问题的最优解 if res1 is None: return res2 elif res2 is None: return res1 elif res1.fun >= res2.fun: return res1 else: return res2 # 调用分枝定界函数 res = branch_and_bound(c, A, b, bounds, res) # 输出结果 print(res) ``` 这里的例子是一个混合整数线性规划问题,其中变量x1、x2和x3均有上下界限制,且x3是一个0-1变量。首先,使用线性规划库中的linprog函数求解问题的松弛线性规划问题。然后,定义一个分枝定界的主函数,该函数首先判断当前的松弛线性规划问题是否已经取得整数解,若是则返回该解,否则进行分枝操作。分枝操作中,找到第一个非整数变量,构造两个新的线性规划问题,一个限制该变量的上界为其整数部分,一个限制该变量的下界为其整数部分+1。然后,对两个新问题进行递归调用,最终返回两个新问题的最优解。最后,调用分枝定界函数,并输出结果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值