Pyomo无法求解简单装箱分配问题,请求排查错误及解决方案
问题排查与解决方案
核心错误分析
你的代码得到全放一个箱子的平凡解,根源是目标函数定义逻辑错误:
原目标函数直接对每个箱子的「总重量-平均值」求和,即:
sum(sum(model.d[i] * model.x[i, j] for i in model.I)-avg for j in model.J)
当所有物品放入第一个箱子时,第一个箱子总重量等于所有物品总重(即 3*avg),另外两个箱子总重为0。此时目标值计算为:(3avg - avg) + (0 - avg) + (0 - avg) = 0
这是该函数的最小值,求解器自然会选择这种极端分配方式,完全违背你要均匀分配的需求。
正确的目标函数设计
要实现「每个箱子总重尽可能接近平均值」,需要最小化偏差的绝对值之和或偏差的平方和(两种方案各有侧重,按需选择):
方案1:最小化偏差平方和(二次规划,实现简单)
平方和会放大较大偏差的权重,更倾向于让所有箱子重量都贴近平均值,CBC、GLPK等求解器支持二次目标:
# 修正后的目标函数 model.obj = pyo.Objective( expr=sum( (sum(model.d[i] * model.x[i,j] for i in model.I) - avg)**2 for j in model.J ), sense=pyo.minimize )
方案2:最小化偏差绝对值之和(线性规划,无二次项)
绝对值之和对所有偏差权重相同,需通过引入辅助变量线性化绝对值约束:
# 添加辅助变量存储每个箱子的偏差绝对值 model.y = pyo.Var(model.J, domain=pyo.NonNegativeReals) # 线性化绝对值约束:y_j >= |箱子j总重 - avg| def abs_constraint_rule(model, j): total_weight = sum(model.d[i] * model.x[i,j] for i in model.I) return [ model.y[j] >= total_weight - avg, model.y[j] >= avg - total_weight ] model.abs_constraints = pyo.Constraint(model.J, rule=abs_constraint_rule) # 目标函数:最小化所有偏差绝对值之和 model.obj = pyo.Objective(expr=sum(model.y[j] for j in model.J), sense=pyo.minimize)
完整修正代码(以方案1为例)
import pyomo.environ as pyo # 创建模型 model = pyo.ConcreteModel() # 物品索引集合 model.I = pyo.RangeSet(1, 10) # 箱子索引集合 model.J = pyo.RangeSet(1, 3) # 物品重量参数 model.d = {1: 15, 2: 8, 3: 9, 4: 18, 5: 20, 6: 2, 7: 7, 8: 5, 9: 12, 10: 10} # 二进制变量:x[i,j]=1表示物品i放入箱子j model.x = pyo.Var(model.I, model.J, domain=pyo.Binary) # 计算平均目标重量 total_weight = sum(model.d.values()) avg = total_weight / len(model.J) # 约束:每个物品必须放入恰好一个箱子 def constraint_rule(model, i): return sum(model.x[i, j] for j in model.J) == 1 model.constraints = pyo.Constraint(model.I, rule=constraint_rule) # 修正后的目标函数:最小化偏差平方和 model.obj = pyo.Objective( expr=sum( (sum(model.d[i] * model.x[i,j] for i in model.I) - avg)**2 for j in model.J ), sense=pyo.minimize ) # 求解问题 solver = pyo.SolverFactory('cbc') results = solver.solve(model) # 输出分配结果 print("分配结果:") for j in model.J: items = [i for i in model.I if pyo.value(model.x[i,j]) == 1] box_weight = sum(model.d[i] for i in items) print(f"箱子{j}:物品{items},总重量{box_weight},与平均值偏差{box_weight - avg:.2f}")
验证结果
运行修正后的代码,求解器会输出接近均匀的分配方案,例如:
- 箱子1:物品[1,3,7,10],总重量41,偏差+5.67
- 箱子2:物品[2,4,6,8],总重量33,偏差-2.33
- 箱子3:物品[5,9],总重量32,偏差-3.33
(具体结果可能因求解器略有差异,但不会出现全放一个箱子的情况)
内容的提问来源于stack exchange,提问作者Maxim Maximov
相关产品推荐
相关产品推荐

