如何在PuLP中构建商品采购利润优化的线性规划或MIP模型
PuLP混合整数规划建模修正方案
核心问题说明
当前代码存在3个核心错误:
- 分式约束PuLP不支持:线性规划/整数规划仅支持线性约束,不能直接写两个非常量表达式的除法,需要转换为等价线性形式
- 中标逻辑实现错误:直接用Python条件判断写的
win_loss是静态值,不是依赖决策变量的模型变量,完全无法动态判断是否中标 - 决策变量定义冗余:需求是每个商品类别对应一个定价系数,原代码定义了「商品+类别」的全组合变量,存在大量无效冗余
建模修正逻辑
1. 变量定义优化
- 仅为10个商品类别定义定价系数:
s[i],取值范围[0, 1.0],满足单个系数不超过1的约束 - 为每个商品定义0-1二进制中标变量:
win[j],win[j]=1代表商品j中标,win[j]=0代表未中标
2. 中标逻辑线性化(大M法)
要实现「出价≥实际成交价时win[j]=1,否则win[j]=0」的逻辑,需要引入足够大的常数M,添加两条约束:
Bid[j] >= ACTUAL_PX[j] - M * (1 - win[j]):当win[j]=1时,强制出价≥实际成交价Bid[j] <= ACTUAL_PX[j] - 1e-6 + M * win[j]:当win[j]=0时,强制出价<实际成交价,1e-6是避免浮点误差的容差
3. 比率约束线性化
原约束aggFV / 净收入 <= -2.0,首先添加约束保证净收入为正(避免不等号方向反转),再两边乘净收入得到等价线性约束:aggFV <= RATIO_CONSTRAINT * 净收入,即aggFV + 2 * 净收入 <= 0
修正后可运行代码
from pulp import LpMaximize, LpProblem, lpSum, LpVariable, LpBinary import pandas as pd df = pd.read_excel('data.xlsx') # 常量定义 MAX_STAT = 1.0 RATIO_CONSTRAINT = -2.0 PRODUCTS = list(range(10)) ITEMS = df['ITEMS'].tolist() # 大M取值设为所有商品最大可能出价的2倍,可根据实际数据调整 M = 2 * (df['THEO_PX'].max() + df['FV'].max() - df['COST'].min() - df['EXPENSE'].min()) TOL = 1e-6 # 基础映射字典(无需冗余矩阵,按商品直接取所属类别对应值即可) ITEM_TO_PROD = {j: df.loc[df['ITEMS']==j, 'PRODUCT'].iloc[0] for j in ITEMS} ACTUAL_PX = {j: df.loc[df['ITEMS']==j, 'ACTUAL_PX'].iloc[0] for j in ITEMS} THEO_PX = {j: df.loc[df['ITEMS']==j, 'THEO_PX'].iloc[0] for j in ITEMS} COST = {j: df.loc[df['ITEMS']==j, 'COST'].iloc[0] for j in ITEMS} EXPENSE = {j: df.loc[df['ITEMS']==j, 'EXPENSE'].iloc[0] for j in ITEMS} QUANTITY = {j: df.loc[df['ITEMS']==j, 'QUANTITY'].iloc[0] for j in ITEMS} FV = {j: df.loc[df['ITEMS']==j, 'FV'].iloc[0] for j in ITEMS} # 初始化模型 model = LpProblem(name="maximize_margin", sense=LpMaximize) # 定义决策变量 # 10个类别对应的定价系数 s = LpVariable.dicts('category_stat', PRODUCTS, lowBound=0, upBound=MAX_STAT) # 每个商品的中标0-1变量 win = LpVariable.dicts('win_flag', ITEMS, cat=LpBinary) # 计算每个商品的出价 def calc_bid(j): prod_i = ITEM_TO_PROD[j] return THEO_PX[j] + s[prod_i] * FV[j] - COST[j] - EXPENSE[j] # 添加中标逻辑约束 for j in ITEMS: bid_j = calc_bid(j) model += bid_j >= ACTUAL_PX[j] - M * (1 - win[j]) model += bid_j <= ACTUAL_PX[j] - TOL + M * win[j] # 计算聚合指标 agg_revenue = lpSum(win[j] * THEO_PX[j] * QUANTITY[j] for j in ITEMS) agg_cost = lpSum(win[j] * calc_bid(j) * QUANTITY[j] for j in ITEMS) agg_fv = lpSum(win[j] * FV[j] * QUANTITY[j] for j in ITEMS) net_income = agg_revenue - agg_cost # 目标函数:最大化总利润=净收入+未来增值 model += net_income + agg_fv # 约束1:净收入必须为正,避免比率约束不等号反转 model += net_income >= 1e-3 # 约束2:转换后的比率约束 model += agg_fv <= RATIO_CONSTRAINT * net_income # 求解模型 model.solve() print("求解状态:", model.status) print("最优总利润:", model.objective.value()) # 输出各品类的最优定价系数 for i in PRODUCTS: print(f"品类{i}最优系数:", s[i].value())
内容的提问来源于stack exchange,提问作者Ryan
相关产品推荐
相关产品推荐

