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

基于Pyomo实现含供应商依赖型运费的采购成本最小化优化

解决依赖采购总成本的分段运费建模问题

这个需求很常见,要处理这种和采购总成本挂钩的分段运费,我们得用二进制变量来标记是否触发运费条件,再配合大M法把逻辑转化为Pyomo能求解的线性约束。下面是具体的修改步骤和完整代码:

核心思路

每个供应商对应一个0-1变量:

  • 对于供应商A:y_A=1 表示从A采购的总成本≤100(需支付10运费),y_A=0 表示总成本>100(免运费)
  • 对于供应商B:y_B=1 表示从B采购的总成本≤150(需支付8运费),y_B=0 表示总成本>150(免运费)

然后用大M约束把“总成本是否超过阈值”的逻辑转化为线性约束,最后把运费加到目标函数里。

修改后的完整代码

from pyomo.environ import ConcreteModel, Var, Objective, Constraint, SolverFactory, Binary

model = ConcreteModel(name="(MN_2)")
# products
N = ['prod1', 'prod2', 'prod3']
# suppliers
M = ['A', 'B']
# price
p = {('prod1', 'A'): 10, ('prod2', 'A'): 9, ('prod3', 'A'): 50, 
     ('prod1', 'B'): 16, ('prod2', 'B'): 20, ('prod3', 'B'): 35}
# user quantity constraint (minimum purchase)
q_u = {('prod1', 'A'): 2, ('prod2', 'A'): 1, ('prod3', 'A'): 1, 
       ('prod1', 'B'): 1, ('prod2', 'B'): 1, ('prod3', 'B'): 1}
# seller quantity constraint (maximum supply)
q_s = {('prod1', 'A'): 20, ('prod2', 'A'): 10, ('prod3', 'A'): 10, 
       ('prod1', 'B'): 10, ('prod2', 'B'): 10, ('prod3', 'B'): 10}

# 1. 定义决策变量:采购量x,以及标记运费是否触发的二进制变量y
model.x = Var(N, M, bounds=(0,10))
# 二进制变量:y[m]=1表示从供应商m采购的总成本≤阈值,需付运费;y[m]=0表示免运费
model.y = Var(M, domain=Binary)

# 2. 定义大M值:每个供应商的最大可能总成本(确保约束生效)
# 供应商A的最大总成本:sum(p[n,A] * q_s[n,A]) = 10*20 +9*10 +50*10=790,取800
M_A = 800
# 供应商B的最大总成本:16*10 +20*10 +35*10=710,取750
M_B = 750
# 运费阈值
threshold_A = 100
threshold_B = 150
# 运费金额
freight_A = 10
freight_B = 8

# 3. 修改目标函数:采购成本 + 运费
def obj_rule(model):
    purchase_cost = sum(p[n,m]*model.x[n,m] for n in N for m in M)
    freight_cost = freight_A * model.y['A'] + freight_B * model.y['B']
    return purchase_cost + freight_cost
model.obj = Objective(rule=obj_rule, sense=minimize)

# 4. 原有约束:用户最小采购量、供应商最大供应量
def user_quantity(model, n, m):
    return model.x[n,m] >= q_u[n,m]
model.user_quantity = Constraint(N, M, rule=user_quantity)

def seller_quantity(model, n, m):
    return model.x[n,m] <= q_s[n,m]
model.seller_quantity = Constraint(N, M, rule=seller_quantity)

# 5. 添加运费相关的大M约束
# 供应商A的约束:
def freight_constraint_A_upper(model):
    # 当y[A]=1时,总成本≤threshold_A;y[A]=0时,约束自动满足
    return sum(p[n,'A']*model.x[n,'A'] for n in N) <= threshold_A + M_A*(1 - model.y['A'])
model.freight_A_upper = Constraint(rule=freight_constraint_A_upper)

def freight_constraint_A_lower(model):
    # 当y[A]=0时,总成本>threshold_A(加1e-6避免浮点精度问题);y[A]=1时约束自动满足
    return sum(p[n,'A']*model.x[n,'A'] for n in N) >= threshold_A + 1e-6 - M_A*model.y['A']
model.freight_A_lower = Constraint(rule=freight_constraint_A_lower)

# 供应商B的约束:
def freight_constraint_B_upper(model):
    return sum(p[n,'B']*model.x[n,'B'] for n in N) <= threshold_B + M_B*(1 - model.y['B'])
model.freight_B_upper = Constraint(rule=freight_constraint_B_upper)

def freight_constraint_B_lower(model):
    return sum(p[n,'B']*model.x[n,'B'] for n in N) >= threshold_B + 1e-6 - M_B*model.y['B']
model.freight_B_lower = Constraint(rule=freight_constraint_B_lower)

# 求解并打印结果
solver = SolverFactory('glpk')
solver.solve(model)

print("采购量:")
model.x.pprint()
print("\n运费触发标记(1=需付运费,0=免运费):")
model.y.pprint()
print("\n总成本(采购+运费):", model.obj())

关键部分解释

  • 二进制变量y:用来把“是否触发运费”的离散逻辑转化为线性变量,Pyomo的Binary域确保变量只能取0或1。
  • 大M约束:
    • 上界约束:当y[m]=1时,强制总成本≤阈值;y[m]=0时,大M项会让约束变得宽松,不会限制总成本。
    • 下界约束:当y[m]=0时,强制总成本略大于阈值(加1e-6是为了避免浮点精度问题,防止出现总成本刚好等于阈值却触发免运费的情况);y[m]=1时,约束自动满足。
  • 目标函数:把运费和二进制变量绑定,只有当触发运费时(y[m]=1)才会把对应运费加入总成本。

这样修改后,模型就能自动计算出包含最优运费策略的采购方案了。

内容的提问来源于stack exchange,提问作者Malcolm

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.11 08:07:01