You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

请求scipy.optimize.linprog/milp调优提示:实现涂料罐组合成本优化

请求scipy.optimize.linprog/milp调优提示:实现涂料罐组合成本优化

看起来你已经用PuLP完美实现了涂料罐组合的成本最小化问题,现在想转用Scipy的linprog或milp工具复现结果,却遇到了整数约束失效、解不符合预期的问题对吧?我来帮你梳理下现有代码的问题,再给出修正后的实现方案~

先明确问题的核心约束与目标

我们要解决的是非负整数线性规划问题:

  • 目标:最小化总成本 sum(单罐价格 × 购买数量)
  • 约束:所有购买的涂料罐能覆盖的总面积 ≥ 目标面积(30㎡)
  • 变量:每种涂料罐的购买数量(必须是非负整数)

其中,每罐能覆盖的面积 = 罐容量 / 单位面积消耗量(也就是你代码里计算的exps数组),这个部分你是对的~

你的Scipy代码核心问题

你在构造约束矩阵的时候犯了关键错误:

  1. 完全偏离了「总覆盖面积≥目标面积」的核心约束,错误地用了(1/c[i])*exps[i]这种无关计算,导致约束逻辑完全失效
  2. 额外添加的上界约束(math.ceil(square/max(exps))*max(exps))其实是多余的——整数规划会自动选择成本最低的组合,不会无意义地多买涂料

接下来是修正后的代码:


方案1:用scipy.optimize.linprog实现

linprog默认处理A_ub @ x ≤ b_ub形式的约束,所以我们要把「总覆盖面积≥30」转换成-exps @ x ≤ -30的等价形式,同时明确设置变量为非负整数:

from scipy.optimize import linprog

pdict = {'Can_9ltr':[9, 0.17, 4870],
         'Can_4.5ltr':[4.5, 0.17, 2910],
         'Can_1ltr':[1, 0.17, 632],
         'Can_2.25ltr':[2.25, 0.17, 1790]}
square = 30

# 目标函数系数:各罐的价格(要最小化总成本)
c = [v[2] for v in pdict.values()]
# 每罐能覆盖的面积 = 罐容量 / 单位面积消耗量
exps = [v[0]/v[1] for v in pdict.values()]

# 构造约束:总覆盖面积 ≥ square → 转换为 -exps @ x ≤ -square
A_ub = [[-val for val in exps]]
b_ub = [-square]

# 变量边界:所有购买数量≥0
x_bounds = [(0, None) for _ in range(len(pdict))]

# 求解:integrality=1表示所有变量为整数,需Scipy 1.9.0+版本支持
res = linprog(
    c=c,
    A_ub=A_ub,
    b_ub=b_ub,
    bounds=x_bounds,
    integrality=[1]*len(pdict),
    method='highs'  # highs方法原生支持整数规划
)

# 整理为你需要的结果格式
can_names = list(pdict.keys())
optimal_set = {}
total_cost = 0.0
for name, cnt, price in zip(can_names, res.x, c):
    cost = cnt * price
    optimal_set[name] = {
        'CPU': price,
        'Amount': cnt,
        'Cost': cost
    }
    total_cost += cost

result_dict = {
    'Target_value': square,
    'Optimal_set': optimal_set,
    'Cost_total': total_cost
}

print(result_dict)

方案2:用scipy.optimize.milp实现

milp支持更直观的约束定义(直接设置下界和上界),不需要转换约束方向,代码逻辑更清晰:

from scipy.optimize import milp, LinearConstraint
import numpy as np

pdict = {'Can_9ltr':[9, 0.17, 4870],
         'Can_4.5ltr':[4.5, 0.17, 2910],
         'Can_1ltr':[1, 0.17, 632],
         'Can_2.25ltr':[2.25, 0.17, 1790]}
square = 30

# 目标函数系数:各罐的价格
c = np.array([v[2] for v in pdict.values()])
# 每罐能覆盖的面积
exps = np.array([v[0]/v[1] for v in pdict.values()])

# 约束:总覆盖面积 ≥ square,无上限
constraints = LinearConstraint(
    A=exps.reshape(1, -1),  # 约束矩阵(1行4列)
    lb=square,              # 下界:总覆盖面积≥30
    ub=np.inf               # 上界:无限制
)

# 变量边界:所有购买数量≥0
bounds = [(0, None) for _ in range(len(pdict))]

# 求解:integrality=1表示所有变量为整数
res = milp(
    c=c,
    constraints=constraints,
    bounds=bounds,
    integrality=[1]*len(pdict)
)

# 整理为你需要的结果格式
can_names = list(pdict.keys())
optimal_set = {}
total_cost = 0.0
for name, cnt, price in zip(can_names, res.x, c):
    cost = cnt * price
    optimal_set[name] = {
        'CPU': price,
        'Amount': cnt,
        'Cost': cost
    }
    total_cost += cost

result_dict = {
    'Target_value': square,
    'Optimal_set': optimal_set,
    'Cost_total': total_cost
}

print(result_dict)

验证结果

修正后的代码会输出和你PuLP完全一致的结果:

{
 'Target_value': 30,
 'Optimal_set': {
  'Can_9ltr': {'CPU': 4870, 'Amount': 0.0, 'Cost': 0.0},
  'Can_4.5ltr': {'CPU': 2910, 'Amount': 1.0, 'Cost': 2910.0},
  'Can_1ltr': {'CPU': 632, 'Amount': 1.0, 'Cost': 632.0},
  'Can_2.25ltr': {'CPU': 1790, 'Amount': 0.0, 'Cost': 0.0}
 },
 'Cost_total': 3542.0
}

关键注意点

  1. 确保你使用的是Scipy 1.9.0及以上版本:linprog的integrality参数和milp函数都是在这个版本之后正式支持的
  2. 约束构造一定要贴合实际业务逻辑:核心是总覆盖面积满足需求,不要添加无关的计算或约束
  3. 变量的整数约束必须明确设置,否则会得到非整数的解(也就是允许买半罐,不符合实际业务场景)

备注:内容来源于stack exchange,提问作者Артур Дутов

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.14 18:29:27