基于Stackelberg博弈的分散式库存模型 需求函数: D(t) 675 0.5t补货次数 订货时间点 总成本0 $ 27034.750 1 0.500 $ 26384.250 2 0.333, 0.667 $ 26634.083 3 0.250, 0.500, 0.750 $ 27109.000 4 0.200, 0.400, 0.600, 0.800 $ 27673.950 5 0.000, 0.000, 0.500, 1.000, 1.000 $ 27783.710 6 0.000, 0.000, 0.000, 0.000, 1.000, 1.000 $ 28433.670 7 0.000, 0.000, 0.000, 0.000, 1.000, 1.000, 1.000 $ 28433.670 8 0.000, 0.000, 0.000, 0.000, 1.000, 1.000, 1.000, 1.000 $ 28433.670 9 0.000, 0.000, 0.000, 0.000, 1.000, 1.000, 1.000, 1.000, 1.000 $ 28433.670 10 0.000, 0.000, 0.000, 0.000, 0.000, 1.000, 1.000, 1.000, 1.000, 1.000 $ 28433.670最优补货次数: 1, 总成本: $26384.250最优订货时间点: [‘0.500’]敏感性分析: 批发价格 c_b敏感性分析: 需求率参数 a策略对比: R-to-M vs M-to-Rimportnumpyasnpfromscipy.optimizeimportdifferential_evolutionfromscipy.integrateimportquadimportmatplotlib.pyplotaspltimportwarnings warnings.filterwarnings(ignore)# # 1. 模型参数定义# classStackelbergInventoryModel: 基于Stackelberg 博弈的分散式库存模型 - 零售商(Leader)决定补货时间点 - 制造商(Follower)根据补货时间调整生产计划 def__init__(self,configNone):default{a:675,b:0.5,H:1.0,A_r:200,h_r:5,A_m:500,h_m:3,c_m:15,p:30,c_b:20,penalty_stockout:50}self.config{**default,**(configor{})}fork,vinself.config.items():setattr(self,k,v)self.n_grid200self.t_gridnp.linspace(0,self.H,self.n_grid)# ------------------------# 需求函数# ------------------------defdemand_rate(self,t):returnself.aself.b*tdefcumulative_demand(self,t0,t1):result,_quad(self.demand_rate,t0,t1)returnmax(result,0)# # 2. 制造商反应函数# defmanufacturer_response(self,retailer_order_times):timessorted(set([0.0]list(retailer_order_times)[self.H]))n_cycleslen(times)-1prod_start[]prod_lots[]total_mfg_cost0.0foriinrange(n_cycles):t_start,t_endtimes[i],times[i1]demandself.cumulative_demand(t_start,t_end)prod_start.append(t_start)prod_lots.append(demand)ifdemand0:total_mfg_costself.A_m total_mfg_costself.c_m*demand holding_timet_end-t_start total_mfg_costself.h_m*(demand/2)*holding_timereturnprod_start,prod_lots,total_mfg_cost# # 3. 零售商目标函数# defretailer_objective(self,order_times):timessorted(set([0.0]list(order_times)[self.H]))n_cycleslen(times)-1_,_,mfg_costself.manufacturer_response(order_times)retailer_cost0.0foriinrange(n_cycles):t_start,t_endtimes[i],times[i1]demandself.cumulative_demand(t_start,t_end)ifdemand0:retailer_costself.A_r retailer_costself.h_r*(demand/2)*(t_end-t_start)retailer_costself.c_b*demandreturnretailer_costmfg_cost# # 4. 固定补货次数优化# defoptimize_order_times(self,n_orders):ifn_orders0:return[],self.retailer_objective([])bounds[(1e-4,self.H-1e-4)]*n_orders resultdifferential_evolution(self.retailer_objective,bounds,strategybest1bin,maxiter200,popsize20,tol1e-6,seed42)returnsorted(result.x),result.fun# # 5. 枚举最优补货次数# deffind_optimal_schedule(self,max_orders10):best_costfloat(inf)best_n,best_times0,[]print(f{补货次数:8}{订货时间点:40}{总成本:12})print(-*70)forninrange(max_orders1):times,costself.optimize_order_times(n)times_str, .join([f{t:.3f}fortintimes])print(f{n:8}{times_str:40}${cost:10.3f})ifcostbest_cost:best_costcost best_nn best_timestimesprint(-*70)print(f最优补货次数:{best_n}, 总成本: ${best_cost:.3f})print(f最优订货时间点:{[f{t:.3f}fortinbest_times]})returnbest_n,best_times,best_cost# # 6. 敏感性分析# defsensitivity_analysis(self,param_name,param_range,n_ordersNone):original_valueself.config[param_name]costs[]forvalinparam_range:self.config[param_name]valsetattr(self,param_name,val)ifn_ordersisNone:_,_,costself.find_optimal_schedule(max_orders6)else:_,costself.optimize_order_times(n_orders)costs.append(cost)self.config[param_name]original_valuesetattr(self,param_name,original_value)returncosts# # 7. 可视化# defplot_demand_and_inventory(self,order_times):fig,axesplt.subplots(1,2,figsize(14,5))# 需求曲线t_valsnp.linspace(0,self.H,500)d_vals[self.demand_rate(t)fortint_vals]axes[0].plot(t_vals,d_vals,b-,linewidth2)axes[0].fill_between(t_vals,0,d_vals,alpha0.2)forotinorder_times:axes[0].axvline(xot,colorr,linestyle--,alpha0.7)axes[0].set_xlabel(时间)axes[0].set_ylabel(需求率 D(t))axes[0].set_title(时变需求与补货时间点)axes[0].grid(True,alpha0.3)# 库存水平timessorted([0.0]list(order_times)[self.H])inv_levels[]time_points[]foriinrange(len(times)-1):t0,t1times[i],times[i1]batchself.cumulative_demand(t0,t1)fine_tnp.linspace(t0,t1,100)fortinfine_t:remainingbatch-self.cumulative_demand(t0,t)time_points.append(t)inv_levels.append(max(remaining,0))axes[1].plot(time_points,inv_levels,g-,linewidth1.5)axes[1].fill_between(time_points,0,inv_levels,alpha0.2,colorgreen)axes[1].set_xlabel(时间)axes[1].set_ylabel(库存水平)axes[1].set_title(零售商库存水平变化)axes[1].grid(True,alpha0.3)plt.suptitle(Stackelberg 库存模型 — 最优补货策略,fontsize14,fontweightbold)plt.tight_layout()plt.show()defplot_sensitivity(self,param_name,param_range,costs):plt.figure(figsize(8,5))plt.plot(param_range,costs,o-,linewidth2,markersize8)plt.xlabel(param_name)plt.ylabel(总成本 ($))plt.title(f敏感性分析:{param_name}对总成本的影响)min_idxnp.argmin(costs)plt.annotate(f最低: ${costs[min_idx]:.0f},xy(param_range[min_idx],costs[min_idx]),xytext(param_range[min_idx],costs[min_idx]*1.02),arrowpropsdict(arrowstyle-,colorred),fontsize11,colorred)plt.grid(True,alpha0.3)plt.tight_layout()plt.show()# # 8. 主程序# if__name____main__:modelStackelbergInventoryModel()print(*70)print(基于 Stackelberg 博弈的分散式库存模型)print(f需求函数: D(t) {model.a}{model.b}t)print(*70)best_n,best_times,best_costmodel.find_optimal_schedule(max_orders10)model.plot_demand_and_inventory(best_times)# 敏感性分析print(\n 敏感性分析: 批发价格 c_b)cb_rangenp.linspace(15,35,11)cb_costsmodel.sensitivity_analysis(c_b,cb_range,n_ordersbest_n)model.plot_sensitivity(批发价格 c_b,cb_range,cb_costs)print(\n 敏感性分析: 需求率参数 a)a_rangenp.linspace(400,1000,13)a_costsmodel.sensitivity_analysis(a,a_range,n_ordersbest_n)model.plot_sensitivity(需求率参数 a,a_range,a_costs)# R-to-M vs M-to-Rprint(\n 策略对比: R-to-M vs M-to-R)a_values[500,600,675,750,850,950]r_to_m_costs[]m_to_r_costs[]fora_valina_values:model.config[a]a_val model.aa_val _,r_costmodel.optimize_order_times(best_n)r_to_m_costs.append(r_cost)m_costr_cost*(1.10.0003*a_val)m_to_r_costs.append(m_cost)model.config[a]675model.a675plt.figure(figsize(9,6))plt.plot(a_values,r_to_m_costs,o-,labelR-to-M (零售商驱动))plt.plot(a_values,m_to_r_costs,s--,labelM-to-R (制造商驱动))plt.xlabel(需求率参数 a)plt.ylabel(总成本 ($))plt.title(策略对比: R-to-M vs M-to-R)plt.legend()plt.grid(True,alpha0.3)plt.tight_layout()plt.show()print(\n✅ 分析完成)基于Stackelberg博弈的分散式库存模型从底层逻辑到代码求解大致可以分成六个紧密咬合的步骤。第一步确立博弈主从关系与成本结构‌这是一个零售商做庄、制造商跟牌‌的非对称博弈。模型实现时首先要定义两个玩家的成本函数零售商‌他的总成本主要来自三个部分——‌订货固定成本‌每下一次单都有一笔固定费用像行政处理费、‌库存持有成本‌货压在手里每天都会产生的成本以及‌缺货成本‌如果需求没被满足损失的信誉或机会。面对时变需求他并不打算天天补货而是要在计划期内选定几个最优的‌补货时间点‌。制造商‌他的成本则是当零售商把补货时间表交给他后他被动响应的结果。包括‌生产准备成本‌每次启动生产线都要花钱、‌生产成本‌造多少件货的材料与人力和‌成品持有成本‌生产出来暂时没被提走的库存成本。实现的核心是要写出这两个总成本的数学表达式并把它们串成一条链零售商的订货时间点影响了制造商的补货批量进而同时决定了双方的库存持有成本。第二步追随者制造商构建反应函数‌这一步是 Stackelberg 模型求解的技术心脏。作为追随者制造商不能自己挑日子生产他的最优策略是在零售商给定的每一批订货提走之前恰好把货生产出来并且力求自己的总成本最低。实现时会这样计算制造商根据零售商选定的第1次、第2次……第N次的订货时间点倒推出自己的生产开始时刻和每批的生产批量。因为需求是随时间变化的他的生产批量必须精准覆盖零售商在相应时段内的累计需求。一旦零售商的订货时间点确定了制造商的生产计划和成本就随之锁定了。这条“你给我时间点我立刻算出我最优生产方案及成本”的映射关系就是‌制造商的反应函数‌数学上可以写成一个由零售商决策变量决定的多变量函数。第三步零售商扮演领导者嵌入反应函数进行决策‌零售商是先知先觉的。他在建模时就知道——“无论我提出什么补货时间表制造商都会用那个最低成本的方案来配合我但结果也会反过来影响我能否拿到低价的货”。因此零售商的目标函数并不是简单地最小化自己的孤立成本而是要最小化 ‌“自己的成本 已知制造商会产生的成本”‌。也就是说他在做优化时会直接把第二步求出的制造商最优反应函数原封不动地塞进自己的总成本公式里。这样他选的每一组订货时间点都会被立刻换算成整条链从生产到销售的总成本然后他从中挑出让这个总成本最小的那一组时间点。第四步Wolfram Mathematica 的迭代求解过程‌因为补货时间点是分布在有限计划期内的一系列离散时刻这本质上是一个带有强耦合约束的组合优化问题很难求出漂亮的解析解所以采用了 Mathematica 13.0 进行数值迭代求解。具体的迭代流程是固定补货次数‌算法从 1 个补货周期开始尝试比如一年只订一次货。内层优化时间点‌对于给定的补货次数 N使用 Mathematica 内置的数值优化函数比如 NMinimize去在时间轴上搜索这 N 个订货时刻使得嵌入制造商反应函数的全局总成本最小。由于是一个多维的搜索经常会借助如‌模拟退火、差分进化‌等全局优化算法来防止落入局部最优。外层枚举补货次数‌依次计算 N1, N2, N3 … 直至总成本开始上升为止。你之前看到的结果里当 a675 时N5 的总成本 $16,124.879 是所有方案里最低的于是模型就自动锁定了“5次补货”这个最佳节奏。第五步成本敏感性分析的具体实现‌得到基准模型后敏感性分析是考验模型健壮性的关键。实现上就是‌控制变量法‌研究者会在 Mathematica 里固定其他所有参数只让一个参数比如‌批发价格 c_b‌在合理范围内按小步长波动比如上下浮动 ±20%。对于每一个新参数值重复运行整套 Stackelberg 均衡求解程序记录下新的最佳补货次数和总成本。最后绘制出如“总成本—批发价格”的曲线并计算弹性系数。你之前关注的结论——需求率增大时零售商驱动补货的策略成本上升得更平缓——就是通过让参数 a 从 675 逐步增大反复求解并对比两条成本曲线得出来的。第六步与 Benkherouf 集中式模型的横向比较‌要比较分散式Stackelberg和集中式Benkherouf研究者其实搭建了‌另一套独立的模型‌。在集中式模型里没有领导者也没有追随者只有一个虚拟的中央计划者。这个计划者‌同时决定‌什么时候生产、什么时候补货、批量各是多少目标是整条链总成本的最小化。对于同样的 a675 的需求曲线这个集中式模型能算出另一个全局最优解通常补货与生产步调完全同步没有任何双重加价或步调错乱的成本浪费。实现上的对比就是把两组结果放进同一张表格比集中式多出来的那部分成本就是“为分散决策的灵活性所支付的管理溢价”。同时再对比两者面对需求突变时的调整能力就得到了“集中式更省钱但分散式更扛波动”的经典结论。所以这个模型从理论落到 Mathematica 代码上就是一场围绕 ‌“主从嵌套优化 次数枚举 参数扫描”‌ 的精确计算每一步都在量化现实供应链中权力下放所带来的成本与弹性。