python实现分支定界算法求解整数规划问题

 

一、构建函数

1.对整数规划模型松弛并求解,若解全部为整数,则直接返回,若不全为整数,则找到第一个不为整数的解及对应的变量;

2.对找到的解进行上下取整,分别作为索引对应的变量的新约束条件的上下界。

3.更新约束条件,并递归调用该求解整数规划模型的函数。得到较小区间和较大区间上的可行解。

4.比较两个区间的可行解得到的目标函数值的大小,返回所需可行解。

二、取一个包含7个变量10个约束条件的整数规划例子

目标函数:

Max z = x1 + 2x2 + 3x3 + x4 + 2x5 + x6 + 3x7

约束条件:

x1 + x2 + x3 <= 30

2x4 + x5 + x6 <= 20

x2 + 2x3 + x7 <= 25

x1 – x2 + x3 <= 10

x4 + x5 – 2x6 <= 5

x1 + x3 + x5 + x7 <=40

x2 + x4 + x6 <= 30

x3 + x5 + x7 <= 35

x1 + x4 + x7 <= 25

2x1 + x2 + x6 <= 15

所有变量都是非负的

以下为转换为矩阵形式:

三 、将分支定界算法求得的整数规划解与松弛线性规划的解进行比较。

可以得到整数规划的解相对于松弛线性规划的解要差一些或一样(当松弛线性规划求解直接得到整数解的情况下)的结论。

四、完整代码

可根据需要更改目标函数和约束条件,参数Aeq和beq是等式约束条件。值得一提的是linprog函数默认求解最小值,若要求解最大值则需将目标函数系数c乘以负一。分支定界算法原理可到其他文章学习。

from scipy.optimize import linprog
import numpy as np
#自定义分支定界算法函数
def branch_and_bound(c, A, b, Aeq=None, beq=None, bounds=None):
    # 默认变量非负
    if bounds is None:
        bounds = [(0, None) for _ in range(len(c))]

    # 调用函数求解线性规划问题
    res = linprog(c, A_ub=A, b_ub=b, A_eq=Aeq, b_eq=beq, bounds=bounds, method='highs')
    # 如果线性规划无解,则返回None
    if not res.success:
        return None

    # 检查是否所有变量都是整数
    if all(np.isclose(np.round(res.x), res.x)):
        return res.x  # 如果是,返回结果

    # 选择第一个非整数变量进行分支
    # 变量的索引,即位置
    non_int_index = next(i for i, x in enumerate(res.x) if not np.isclose(x, np.round(x)))
    # 变量的值
    non_int_value = res.x[non_int_index]

    # 创建新的约束条件
    # 初始化新的两个约束条件,这里需要使用copy方法复制原有约束条件,以便后面更新约束条件的时候不会互相影响
    lower_bound = bounds.copy()
    upper_bound = bounds.copy()
    # 通过遍历找到我们刚刚选择的第一个非整数变量,对它的约束条件进行更新,即将非整数解进行上下取整,分别作为两个新区间的上下界
    for i, (l,u) in enumerate(bounds):
        if i == non_int_index:
            #向下取整作为较小区间的上界
            u1 = np.floor(non_int_value)
            lower_bound[i] = (l,u1)
    for i, (l,u) in enumerate(bounds):
        if i == non_int_index:
            #向上取整作为较大区间的下界
            l1 = np.ceil(non_int_value)
            upper_bound[i] = (l1,u)

    # 对两个新区间分别递归调用分支定界算法
    lower_res = branch_and_bound(c, A, b, Aeq, beq, bounds=lower_bound)
    upper_res = branch_and_bound(c, A, b, Aeq, beq, bounds=upper_bound)

    # 选择最优解
    if lower_res is not None and upper_res is not None:
        #解与系数点乘,返回较小的解。由于在求解最大值时对系数乘了-1,所以这里选择较小值的解。
        return lower_res if c @ lower_res <= c @ upper_res else upper_res
    elif lower_res is not None:
        return lower_res
    elif upper_res is not None:
        return upper_res
    else:
        return None
# 以下为随机选取的一个可求解且松弛优化下解不全为整数的例子
# 目标函数系数
c = [-1, -2, -3, -1, -2, -1, -3]
# 不等式约束的系数矩阵
A = [
    [1, 1, 1, 0, 0, 0, 0],
    [0, 0, 0, 2, 1, 1, 0],
    [0, 1, 2, 0, 0, 0, 1],
    [1, -1, 1, 0, 0, 0, 0],
    [0, 0, 0, 1, 1, -2, 0],
    [1, 0, 1, 0, 1, 0, 1],
    [0, 1, 0, 1, 0, 1, 0],
    [0, 0, 1, 0, 1, 0, 1],
    [1, 0, 0, 1, 0, 0, 1],
    [2, 1, 0, 0, 0, 1, 0]
]
# 不等式约束的右侧值
b = [30, 20, 25, 10, 5, 40, 30, 35, 25, 15]
# 将分支定界算法求解的整数规划的解与松弛线性规划的解对比
# 调用分支定界算法并输出结果
solution = branch_and_bound(c, A, b)
print("整数规划最优解:{0},目标函数值:{1}".format(solution, -(c @ solution)))
# 求解松弛线性规划
result = linprog(c, A_ub=A, b_ub=b, bounds=None, method='highs')
# 输出结果
if result.success:
    print("松弛线性规划最优解:{0},目标函数值:{1}".format(result.x,-(c@result.x)) )
else:
    print("无解")

 

  • 9
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
分支法是求解整数规划问题的一种有效方法。下面是使用Python实现分支求解整数规划的步骤: 1. 整数规划问题,包括目标函数和约束条件。 2. 义一个节点类,用于记录每个节点的状态和上下。 3. 义一个分支函数,用于对当前节点进行分支,生成子节点。 4. 义一个函数,用于计算当前节点的上下。 5. 义一个搜索函数,用于搜索整个分支树,找到最优解。 6. 在搜索函数中使用优先队列,按照的优先级对节点进行排序。 以下是一个简单的代码示例: ```python from queue import PriorityQueue class Node: def __init__(self, lb, ub, value=None): self.lb = lb # 下 self.ub = ub # 上 self.value = value # 取值 def branch(node): # 对当前节点进行分支,生成子节点 nodes = [] if node.lb < node.ub: mid = (node.lb + node.ub) // 2 nodes.append(Node(node.lb, mid)) nodes.append(Node(mid+1, node.ub)) return nodes def bound(node): # 计算当前节点的上下 return node.lb, node.ub def search(): # 搜索整个分支树,找到最优解 root = Node(0, 10) q = PriorityQueue() q.put((0, root)) best_value = float('inf') while not q.empty(): _, node = q.get() lb, ub = bound(node) if lb >= best_value: continue if node.value is not None: # 计算当前节点的目标函数值 value = node.value if value < best_value: best_value = value else: # 对当前节点进行分支,生成子节点 nodes = branch(node) for child in nodes: if lb < best_value: q.put((bound(child)[1], child)) return best_value if __name__ == '__main__': # 整数规划问题 # min x + y # s.t. x + y >= 5, x <= 4, y <= 6 # x, y >= 0, x, y are integer # 目标函数最优解为 5,取值为 (1, 4) print(search()) ``` 这段代码实现了一个简单的分支求解整数规划问题算法。在搜索过程中,使用优先队列来对节点进行排序,按照的优先级进行排序。如果当前节点的下大于等于当前最优解,就不再继续搜索该节点。如果当前节点的上小于当前最优解,也不再继续搜索该节点。如果当前节点的取值不为空,就计算当前节点的目标函数值,并更新最优解。如果当前节点的取值为空,就对当前节点进行分支,生成子节点,并将子节点加入优先队列中。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值