如何在Pyomo优化过程中使用变量结果从列表插值实现阶梯定价
实现方案
我们可以通过二进制指示变量标记每日销量所属的档位区间,同时新增每日销量变量适配整数/浮点场景,完整实现阶梯定价的最优销量求解逻辑如下:
前置逻辑说明
首先需将每日的档位按销量阈值从小到大排序,保证阶梯顺序正确。对第i天第j个档位,记q[i][j]为档位销量上限,price[i][j]为对应档位的定价(支持固定总价/单位价两种模式)。
完整实现代码
from pyomo.environ import * # 输入参数 # 每日档位格式:(销量阈值, 档位定价),已按销量阈值升序排序 daily_price_offer = [ [(5, 8), (10, 4)], # 第1天档位 [(7, 6), (10, 4)], # 第2天档位 [(4, 6), (10, 3)] # 第3天档位 ] max_apples_total = 14 options_per_day = len(daily_price_offer[0]) day_cnt = len(daily_price_offer) # 最小销量默认从0开始,可根据业务调整 min_sale = 0 # 初始化模型和求解器 model = ConcreteModel() solver = SolverFactory('gurobi') # 无gurobi可替换为glpk # 定义集合 model.DAYS = RangeSet(0, day_cnt-1) model.OPTIONS = RangeSet(0, options_per_day-1) # 定义变量 model.daily_sale = Var(model.DAYS, within=NonNegativeReals) # 每日销量,改为Integer即可限定整数销量 model.active_bundle = Var(model.DAYS, model.OPTIONS, within=Binary) # 当日生效档位标记 model.daily_revenue = Var(model.DAYS, within=NonNegativeReals) # 每日收入 # 约束1:每日最多只能有一个生效档位 def single_bundle_rule(model, d): return sum(model.active_bundle[d, j] for j in model.OPTIONS) == 1 model.single_bundle_con = Constraint(model.DAYS, rule=single_bundle_rule) # 约束2:销量匹配生效档位区间 def sale_range_rule(model, d, j): # 取当前档位的上下限阈值 lower_q = min_sale if j == 0 else daily_price_offer[d][j-1][0] upper_q = daily_price_offer[d][j][0] # 大M约束:只有当前档位激活时,销量才会落在对应区间 return ( lower_q * model.active_bundle[d,j], model.daily_sale[d], upper_q * model.active_bundle[d,j] + 1e6 * (1 - model.active_bundle[d,j]) ) model.sale_range_con = Constraint(model.DAYS, model.OPTIONS, rule=sale_range_rule) # 约束3:每日收入匹配档位定价(默认按你示例的档位固定总价规则) def revenue_rule(model, d): return model.daily_revenue[d] == sum( daily_price_offer[d][j][1] * model.active_bundle[d,j] for j in model.OPTIONS ) model.revenue_con = Constraint(model.DAYS, rule=revenue_rule) # 约束4:总销量不超过上限 def total_sale_rule(model): return sum(model.daily_sale[d] for d in model.DAYS) <= max_apples_total model.total_sale_con = Constraint(rule=total_sale_rule) # 目标函数:最大化总营收 model.total_profit = Objective(expr=sum(model.daily_revenue[d] for d in model.DAYS), sense=maximize) # 求解 res = solver.solve(model) # 输出结果 print("===== 求解结果 =====") total_sale = 0 for d in model.DAYS: active_j = [j for j in model.OPTIONS if value(model.active_bundle[d,j]) > 0.5][0] sale_cnt = round(value(model.daily_sale[d]), 2) revenue = round(value(model.daily_revenue[d]), 2) total_sale += sale_cnt print(f"第{d+1}天:销量={sale_cnt}个,生效档位阈值={daily_price_offer[d][active_j][0]}个,当日收入=${revenue}") print(f"总销量:{round(total_sale, 2)}个") print(f"总营收:${round(value(model.total_profit), 2)}")
关键逻辑说明
- 场景适配:将
model.daily_sale的定义域改为Integer即可实现整数销量,默认NonNegativeReals支持浮点量级场景 - 定价规则调整:如果需要使用档位内统一单位价(比如5个以内每单位8美元,5-10个每单位4美元),只需将
revenue_rule替换为下方代码即可:def revenue_rule(model, d, j): return model.daily_revenue[d] >= daily_price_offer[d][j][1] * model.daily_sale[d] - 1e6 * (1 - model.active_bundle[d,j]) model.revenue_con = Constraint(model.DAYS, model.OPTIONS, rule=revenue_rule) - 大M参数:代码中的
1e6为大M常量,可根据业务的最大可能销量调整为合适值,避免数值求解误差
内容的提问来源于stack exchange,提问作者Jwem93
相关产品推荐
相关产品推荐

