一、构建函数
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("无解")