本节我们就来讨论在运筹规划如何解决大规模的优化问题。一般会有以下一些方式,我分别进行理解。
- 列生成和分值定价算法
- 拉格朗日松弛算法
- Dantizig-Wolfe分解算法
- Benders分解算法。
列生成和分值定价算法
列生成算法(Column Generation)是一种用于高效求解大规模线性规划(LP)或整数规划(IP)问题的算法,尤其适用于变量数量庞大但大部分变量在最优解中为零的情况。
列生成算法的思想是是通过迭代求解限制型主问题实现全局最优。
求解流程
对偶变量反映了当前解中约束的边际成本。
- 步骤3:求解子问题(定价)
利用对偶变量构建子问题,目标是找到一个或多个能改善主问题目标的新列(即检验数为负的列)。
子问题通常是一个优化问题,例如:最短路径问题(车辆路径规划)。
若子问题目标值(检验数)为负,则将新列加入RMP;否则算法终止(当前解已最优)。
- 步骤4:迭代
重复步骤2-3,直到子问题无法生成改善的列。
完整的求解过程
需求描述:原料长度(L): 10,长度 3: 需求 4 根,长度 5: 需求 2 根,长度 7: 需求 3 根
步骤 1:初始化主问题(RMP)
- 模式 1: [3, 0, 0](切 3根长度3,余料1)
- 模式 2: [0, 2, 0](切 2根长度5,余料0)
- 模式 3: [0, 0, 1](切 1根长度7,余料3)
minx1+x2+x3s.t.3x1+0x2+0x3>=40x1+2x2+0x3>=4x1+0x2+x3>=4x1,x2,x3>0
步骤 2:第一次迭代
x1=34,x2=1,x3=3目标值:x1+x2+x3=5.3对偶变量:长度3的约束:π1=31长度5的约束:π2=21(需求2根,x2=1)长度7的约束:π3=1
求解子问题(定价问题),子问题目标: 找到新模式a=[a1,a2,a3,] 使得检测数<0.
检测数=(1−31a1+0.5a2+1a3)3a1+5a2+7a3<10
枚举可行模式,[1, 1, 0](切1根3和1根5,余料2),[0, 0, 1](已存在,检验数=0),[1, 0, 1](切1根3和1根7,余料0)
[1, 0, 1](加入主问题)
步骤 3:第二次迭代
更新主问题
minx1+x2+x3+x4s.t.3x1+0x2+0x3+x4>=40x1+2x2+0x3+0x4>=4x1+0x2+x3+x4>=4x1,x2,x3,x4>0
求解
x1=1,x2=1,x3=1,x4=1, 目标值为5, 对偶变量为
对偶变量:长度3的约束:π1=31长度5的约束:π2=21(需求2根,x2=1)长度7的约束:π3=32
求解子问题
模式 7: [0, 2, 0](切2根5,余料0)
模式 8: [2, 0, 0](切2根3,余料4
无负检验数模式 → 终止迭代。
步骤 4: 最终解
原料总数: 4 根
切割方案:
模式 1 [3,0,0]: 1 次 → 切出 3根3
模式 2 [0,2,0]: 1 次 → 切出 2根5
模式 3 [0,0,1]: 1 次 → 切出 1根7
模式 6 [1,0,1]: 1 次 → 切出 1根3和1根7
发现1和2是可能方案。
代码
while True:
# 求解当前RMP
cplex.solve()
duals = cplex.getDuals()
# 求解子问题生成新列
new_col, reduced_cost = solve_pricing(duals)
if reduced_cost >= -tol:
break # 最优解找到
else:
cplex.addCols([new_col]) # 添加新列
拉格朗日松弛
拉格朗日松弛是一种用于求解复杂优化问题(尤其是整数规划或混合整数规划)的技术,通过将难处理的约束松弛到目标函数中,转化为更易求解的子问题。其核心思想是:
- 松弛部分约束:将某些复杂约束移入目标函数,通过拉格朗日乘子(对偶变量)惩罚违反约束的情况。
- 分解问题:主问题通过对偶变量调整,子问题因约束减少更易求解。
- 迭代优化:通过次梯度法等更新拉格朗日乘子,逐步逼近原问题的最优解。
适用问题
列生成:动态添加变量(列),适合变量指数级增长的问题(如切割库存)。
拉格朗日松弛:松弛复杂约束,适合约束难以处理的问题(如旅行商问题中的子环消除约束)。
求解过程
资源分配问题, 目标最小化总成本, ∑cjxj
约束
- 资源需求约束:∑aijxj>bi(难处理,选择松弛)。
- xj∈[0,1]
拉格朗日松弛
松弛复杂约束:将资源需求约束放入目标函数,引入拉格朗日乘子 λi≥0
Lλ=min(j∑cjxj+i∑λi(bi−∑aijxj))
分解为子问题:对固定的 λ,子问题变为:
Lλ=i∑λibi+min j∑(cj−i∑λiaij)xj
子问题可独立求解(如贪心算法)。
更新乘子:用次梯度法调整 λ:
λik+1=max(0,λik+θk(bi−j∑aijxjk))
其中θk表示步长。
代码展示
import numpy as np
def lagrangian_relaxation():
# 问题数据
c = np.array([3, 5, 2]) # 成本系数
A = np.array([[2, 1, 4], # 资源消耗矩阵
[1, 3, 2]])
b = np.array([5, 4]) # 资源需求
x_bounds = [0, 1] # x_j ∈ {0,1}
# 拉格朗日参数初始化
lambda_ = np.zeros(len(b)) # 初始乘子
max_iter = 100 # 最大迭代次数
best_upper_bound = float('inf')
best_x = None
# 次梯度法迭代
for k in range(max_iter):
# 1. 求解子问题(松弛后的问题)
reduced_cost = c - np.dot(lambda_, A)
x = (reduced_cost < 0).astype(int) # 贪心选择:若减后成本<0则选1
# 2. 计算上界(原问题可行解,若存在)
if np.all(np.dot(A, x) >= b):
current_cost = np.dot(c, x)
if current_cost < best_upper_bound:
best_upper_bound = current_cost
best_x = x.copy()
# 3. 计算下界(拉格朗日松弛值)
L = np.dot(c, x) + np.dot(lambda_, b - np.dot(A, x))
# 4. 更新乘子(次梯度法)
subgradient = b - np.dot(A, x)
step_size = 1.0 / (k + 1) # 动态步长
lambda_ = np.maximum(0, lambda_ + step_size * subgradient)
print(f"Iter {k}: Lower Bound = {L:.2f}, Upper Bound = {best_upper_bound}, x = {x}")
return best_x, best_upper_bound
# 运行示例
solution, cost = lagrangian_relaxation()
print("\nFinal Solution:")
print(f"x = {solution}, Total Cost = {cost}")
总而言之
目前我们介绍了两种办法解决大规模的优化问题,后续还会继续补充另外两种方式。