简介面向供应链管理研究者、政策制定者及循环经济企业管理者的一份PDF资源聚焦政府补贴下闭环供应链定价决策问题可用于政府补贴政策模拟、企业产品定价与回收策略制定。内容基于Stackelberg博弈模型系统构建无补贴、补贴制造商、补贴零售商三种场景分析新产品与再制品竞争强度、回收努力水平对定价和成员利润的影响并给出制造商提高回收努力可实现双赢、补贴零售商更利于供应链整体协同等关键结论。资源包共1个PDF文件约687KB目前已有71人学习。PDF中除完整理论推导外还包含Python复现源码及分步解释可自行调整竞争系数、回收敏感度等参数数值模拟验证不同补贴模式下的均衡差异文末指出模型局限并提出改进建议。整体兼具理论深度、代码可操作性和政策参考意义适合学术复现、政策评估及企业闭环供应链优化等场景。1. 政府补贴下的闭环供应链定价Stackelberg博弈模型到底能算出什么政府补贴下闭环供应链定价决策看起来是在算两个价格和一个回收价实际上是一个典型的Stackelberg博弈问题制造商先定批发价、回收转移价与回收努力零售商再据此定零售价和销售努力。这套资源把无补贴、补贴制造商、补贴零售商三种场景的模型与Python代码一起打包跑完能直接输出价格、努力水平与双方利润。做供应链管理、循环经济研究的人以及想用博弈论设计定价策略的从业者都适合拿它当复现底稿。我拆完这个包的第一感受是代码能跑通不算本事难的是看懂每个参数在博弈链条里的位置以及补贴加在哪一方会怎样改变利润分配。下面按模型的落地顺序来拆。2. 模型参数与利润函数的落地写法先看懂每一行代码在算什么2.1 参数初始化c1、c2与mu决定博弈格局代码包的起点是一个Parameters类所有经济参数都集中在构造函数里import numpy as np from scipy.optimize import minimize class Parameters: def __init__(self): # 生产成本 self.c1 100 # 新产品生产成本 self.c2 60 # 再制品生产成本 # 市场参数 self.theta 1000 # 市场潜在需求 self.mu 0.5 # 交叉价格弹性体现新产品与再制品竞争 # 回收相关 self.q0 50 # 基础回收量 self.beta 0.8 # 回收努力敏感系数 # 销售努力参数 self.alpha 0.6 # 销售努力敏感系数 self.kr 100 # 零售商销售努力成本系数 # 回收努力成本系数 self.km 80 # 制造商回收努力成本系数 self.kr_recycle 90 # 零售商回收努力成本系数 # 政府补贴 self.S 20 # 单位回收补贴这些参数不是随便填的。c1100、c260意味着再制造比新制造单件省40%成本这是闭环供应链里回收系统存在的根本动力。mu0.5是新产品与再制品之间的交叉价格弹性取值在0到1之间代表部分替代再制品降价会分流新产品的需求但不会完全取代。theta1000描述市场容量决定了价格项的标尺。beta0.8与q050组合起来看即使企业不做任何回收努力市场仍有50件基础回收量而每提高一单位努力能多回收0.8件。成本系数kr、km、kr_recycle对应二次型成本函数0.5*k*x^2系数越大同样的努力水平要付出更高成本最优努力会被自然压下来。S20表示政府按每单位回收量发放补贴。注意一个容易忽略的实现细节Parameters把所有变量都设成了类属性而不是实例属性。跑敏感性分析时直接改Parameters.mu x确实能跑通但设计上不够干净正式做研究建议在__init__里加参数注入。提示这套参数的量级是先定价格再定需求theta1000远大于c1100是保证p1、p2解出来为正的前提。自定义场景时theta不要改得比成本还小。2.2 需求函数与回收量函数线性建模背后是替代逻辑需求函数采用线性需求形式这是供应链定价文献里的标准做法。拆开看theta - p1是自价格弹性部分 mu * p2是替代品价格拉动 alpha * e是销售努力对需求的直接提升。回收量函数则是纯粹的努力驱动线性表达式def demand_function(p1, p2, e, model_typeGN): 计算新产品和再制品的市场需求 参数: p1: 新产品零售价 p2: 再制品零售价 e: 零售商销售努力程度 params Parameters() q1 params.theta - p1 params.mu * p2 params.alpha * e q2 params.theta - p2 params.mu * p1 return q1, q2 def recycling_function(em, er, model_typeGN): 计算制造商和零售商的回收量 params Parameters() qm params.q0 params.beta * em qr params.q0 params.beta * er return qm, qrq1的四个成分各有经济学含义theta是基准需求-p1表示新产品自身越贵需求越低mu*p2表示再制品越贵消费者越回流到新产品alpha*e说明销售努力能直接拉动新产品需求这也是零售商愿意承担努力成本的回报。q2与之对应但主循环代码里q2没有加销售努力项而后文论文假设段里写的q2 theta - p2 mu*p1 alpha*e是带alpha*e的。这里存在一个不一致我建议按论文假设段补上alpha*e因为再制品同样需要销售努力来推销不补会导致再制品的销量与努力水平脱节。回收量函数是独立的线性表达式不依赖价格。这意味着模型把回收端处理成靠努力驱动的物理过程这是论文简化的地方也是可扩展的点如果要做市场化回收竞争应当把回收价A、B也放进回收量函数形成价格与努力双驱动的闭环。2.3 利润函数补贴落点与回收成本的符号是命门利润函数是整个复现的核心制造商和零售商的利润构成完全不同。零售商赚的是零售价与批发价的差、回收业务价差再减去销售努力成本制造商赚的是批发毛利、再制造节省的成本再减去回收支付、转移支付和回收努力成本。补贴落点直接决定三模型差异def profit_function(p1, p2, w1, w2, e, em, er, A, B, C, model_typeGN): 计算制造商和零售商的利润 参数: p1,p2: 新产品和再制品零售价 w1,w2: 新产品和再制品批发价 e: 销售努力 em,er: 制造商与零售商回收努力 A: 零售商向消费者支付回收价 B: 制造商向消费者支付回收价 C: 制造商向零售商支付转移价 model_type: GN无补贴 / GM补贴制造商 / GR补贴零售商 params Parameters() q1, q2 demand_function(p1, p2, e, model_type) qm, qr recycling_function(em, er, model_type) # 零售商利润 销售毛利 回收价差 - 销售努力成本 retailer_profit (p1 - w1) * q1 (p2 - w2) * q2 \ (C - A) * qr - 0.5 * params.kr * e**2 # 制造商利润 批发毛利 再制造成本节约 - 回收成本 - 努力成本 manufacturer_profit (w1 - params.c1) * q1 (w2 - params.c2) * q2 \ (params.c1 - params.c2 - B) * qm \ (A - C) * qr - 0.5 * params.km * em**2 if model_type GM: # 政府补贴制造商 manufacturer_profit params.S * qm elif model_type GR: # 政府补贴零售商 retailer_profit params.S * qr return manufacturer_profit, retailer_profit制造商的利润项(c1 - c2 - B) * qm是再制造的核心收益每再造一件制造商省下c1-c2的新件成本但要向消费者支付B的回收价差值乘以制造商回收量就是回收再制造的真实获利。(A - C) * qr是制造商向零售商支付转移价的支出项注意零售商赚的价差C-A正好是制造商的成本双方在回收业务上是零和关系。最后减去二次型回收努力成本0.5*km*em^2。这里最值得注意的是A、B、C三个回收价的关系。论文第3节基本假设明确写了A C、c2 C c1、w2 w1、p2 p1但主代码的优化约束里并没有这些不等式。跑默认参数时关系基本能保持一旦把成本改成极端值很容易出现零售商回收业务亏损或再制造无利可图的局面。补贴落点方面GM把S*qm加进制造商利润GR把S*qr加进零售商利润同样是补贴20元一个按制造商回收量补一个按零售商回收量补最终改变的是回收业务在链条内部的定价和努力决策这就是三种模型结果差异的来源。3. Stackelberg博弈求解与三模型对比迭代优化的每一步都在模拟先后决策3.1 主从博弈的迭代求解先定批发价再定零售价Stackelberg博弈的核心是决策顺序制造商是领导者先定批发价、回收转移价与回收努力零售商是跟随者看到这些决策后再定零售价与销售努力。这套代码用交替迭代实现def solve_stackelberg(model_typeGN, max_iter100, tol1e-6): params Parameters() # 初始值 w1, w2 params.c1 * 1.2, params.c2 * 1.2 p1, p2 w1 * 1.2, w2 * 1.2 e, em, er 0.5, 0.5, 0.5 A, B, C 30, 35, 32 for i in range(max_iter): # 零售商反应给定批发价与回收参数优化零售价与销售努力 def retailer_obj(x): p1_r, p2_r, e_r x _, profit profit_function(p1_r, p2_r, w1, w2, e_r, em, er, A, B, C, model_type) return -profit res minimize(retailer_obj, [p1, p2, e], bounds[(w1, None), (w2, None), (0, None)]) p1, p2, e res.x # 制造商优化给定零售商反应优化批发价、回收参数 def manufacturer_obj(x): w1_m, w2_m, em_m, er_m, A_m, B_m, C_m x profit, _ profit_function(p1, p2, w1_m, w2_m, e, em_m, er_m, A_m, B_m, C_m, model_type) return -profit res minimize(manufacturer_obj, [w1, w2, em, er, A, B, C], bounds[(params.c1, None), (params.c2, None), (0, None), (0, None), (0, None), (0, None), (0, None)]) w1, w2, em, er, A, B, C res.x if i 0 and abs(res.fun - prev_profit) tol: break prev_profit res.fun manu_profit, retail_profit profit_function(p1, p2, w1, w2, e, em, er, A, B, C, model_type) return { p1: p1, p2: p2, w1: w1, w2: w2, e: e, em: em, er: er, A: A, B: B, C: C, manufacturer_profit: manu_profit, retailer_profit: retail_profit, total_profit: manu_profit retail_profit }每一轮循环先固定制造商决策用minimize求出零售商最优反应再把零售商反应作为已知求制造商最优决策。如此交替直到前后两轮目标函数值差小于tol。两个细节必须说明。第一minimize默认做最小化所以目标函数写成return -profit等价于最大化利润这是复现时最容易翻车的点。第二bounds的设计有经济学含义w1下限是c1保证批发价不会低于新成本w2下限是c2p1、p2下限分别是w1、w2保证零售商毛利为正回收价格A、B、C全部非负。这些边界条件与论文价格关系假设的方向是一致的。收敛性方面默认max_iter100、tol1e-6跑默认参数通常十几轮内收敛。但如果你把mu调到0.8以上或者把S调得很大目标函数等高线会变得很平坦利润差很小但价格还在漂移。我习惯把收敛判据改成同时检查价格变化量把本轮与上轮的(w1, p1, em)差的二范数也放进去防止出现利润相同、价格不同的两套均衡。提示scipy.optimize.minimize默认使用BFGS类拟牛顿方法对这类带边界的光滑问题通常够用。遇到不收敛时换成methodSLSQP并把max_iter提到300稳定性会更好。3.2 三种补贴模型的结果比较补贴对象不同利润分配完全不同运行compare_models可以一次性解出GN、GM、GR三个均衡def compare_models(): 比较三种政府补贴模式下的均衡结果 results {} results[GN] solve_stackelberg(GN) # 无政府补贴 results[GM] solve_stackelberg(GM) # 补贴制造商 results[GR] solve_stackelberg(GR) # 补贴零售商 return results if __name__ __main__: model_results compare_models() for model, res in model_results.items(): print(f\n{model}模型结果:) print(f新产品价格(p1): {res[p1]:.2f}) print(f再制品价格(p2): {res[p2]:.2f}) print(f新产品批发价(w1): {res[w1]:.2f}) print(f再制品批发价(w2): {res[w2]:.2f}) print(f制造商回收努力(em): {res[em]:.2f}) print(f零售商回收努力(er): {res[er]:.2f}) print(f制造商利润: {res[manufacturer_profit]:.2f}) print(f零售商利润: {res[retailer_profit]:.2f})理论上能看到的规律GM模式下S直接补贴制造商的单位回收量制造商会提高回收努力em批发价有下压空间制造商利润显著上升零售商利润变化有限。GR模式下补贴落在零售商零售商会提高回收努力er同时可能降低零售价需求的扩大让制造商也跟着受益最终呈现GM只利好制造商、GR是双赢的对比。注意这里的结果验证是方向性的不是精确预测。政策研究里更关心利润增量在制造商与零售商之间的分配方向而不是某个绝对值。真实落地时要把参数替换成企业实际成本、市场调研得到的交叉弹性补贴S按财政预算来设否则复现的结果只能停留在论文数值算例层面。3.3 论文结论的验证逻辑与双解交叉验证资源包后半段还给了用显式公式求均衡的solve_GN、solve_GM、solve_GR。以无补贴模型为例均衡批发价和零售价有闭式表达式def solve_GN(): 无补贴模型的闭式解用于与数值迭代交叉验证 params Parameters() # 论文推导出的显式最优价 w1_GN (params.alpha * (1 params.mu) params.theta params.c1) / 2 w2_GN (params.alpha * (1 params.mu) params.theta params.c2) / 2 p1_GN (3 * params.alpha * (1 params.mu) params.theta 3 * params.c1) / 4 p2_GN (3 * params.alpha * (1 params.mu) params.theta 3 * params.c2) / 4 return {w1: w1_GN, w2: w2_GN, p1: p1_GN, p2: p2_GN}闭式解和上面的数值迭代解互为校验。建议把两套结果都打印出来对比p1、p2、em、双方利润偏差在1e-2量级以内就说明迭代求解写对了。很多复现跑出来和论文对不上往往不是模型问题而是数值解没收敛或者符号写反了。验证代码的核心是比较三种模型的总利润变化率gn_profit model_results[GN][manufacturer_profit] model_results[GN][retailer_profit] gm_profit model_results[GM][manufacturer_profit] model_results[GM][retailer_profit] gr_profit model_results[GR][manufacturer_profit] model_results[GR][retailer_profit] print(fGN总利润: {gn_profit:.2f}) print(fGM总利润: {gm_profit:.2f} 变化率: {(gm_profit - gn_profit) / gn_profit * 100:.2f}%) print(fGR总利润: {gr_profit:.2f} 变化率: {(gr_profit - gn_profit) / gn_profit * 100:.2f}%)打印结果对应论文四个结论零售商需要考虑交叉价格弹性mu制造商提高回收努力em能实现双赢GM让制造商单方面受益GR让双方受益。我复现时建议把这些验证写成断言也就是验证GM下制造商利润大于GN、GR下零售商和制造商利润都大于GN。任何一条不满足都要回到利润函数和求解环节排查。4. 避坑指南复现这套Stackelberg代码时最常踩的五个坑4.1 目标函数没加负号优化结果全是最小利润现象跑出来的w1、p1全部落在下界利润输出是负值或明显不合理的小数。原因scipy.optimize.minimize默认做最小化把利润函数直接作为目标函数传入就是在找利润的最小值。解决在retailer_obj和manufacturer_obj里统一写成return -profit。检查方法很简单随便设一组变量手工算一遍利润再与minimize返回的fun对比符号。这是最基础的翻车点也是我第一次跑这个包时踩到的。4.2 初始值越界导致批发价低于生产成本现象w1、w2解出来等于下界值甚至无限逼近c1零售商毛利空间被压到几乎为零。原因初始值w1 c1 * 1.2通常没问题但把c2调高到接近c1或把mu改大后w2 * 1.2可能低于c2起点就违反约束。解决初始化时强制w1 max(c1 * 1.2, c1 10)w2同理同时bounds里w1下界设成c1 1e-6别让边界正好卡在成本线上。调试时可以加一行打印初始变量确认起点满足论文的价格关系假设。4.3 GM和GR的补贴落点搞反结论整个翻转现象GM模型跑出来零售商利润大幅上升GR模型跑出来制造商利润不变与论文结论正好相反。原因补贴加错了对象。GM应该把S * qm加进制造商利润GR应该把S * qr加进零售商利润两者只差一个字母但结果方向完全不同。解决在profit_function里用明确的if model_type GM分支并在返回值里带一个subsidy_effect字段。每次对比模型前先打印补贴额确认是20 * qm还是20 * qr。这个坑靠阅读代码时一眼扫过最容易漏掉算是我复现过程中的血泪经验。4.4 交叉价格弹性mu取值太大导致需求为负现象mu调到0.8以上时q1或q2出现负值优化结果里的价格变成负数或者干脆不收敛。原因线性需求函数在替代关系过强时会失去经济学意义mu * p2的拉动项可能超过theta - p1本身需求函数越过了零点。解决复现论文结论时先在mu0.1到0.9之间做扫描看需求函数是否始终为正记录需求转负的临界点。论文讨论产品竞争时通常只在mu in (0,1)区间内论证超出范围的结果只能当数值练习不能当政策结论。4.5 论文基本假设没有落到约束条件里现象最终结果出现p2 p1或A C零售商回收业务变成亏损但程序照样输出均衡解。原因主代码的bounds只约束了变量非负和批发价不低于成本没有把论文第3节的w2 w1、p2 p1、c2 C c1、A C作为约束传给minimize。解决用scipy的LinearConstraint补齐。以A C为例在manufacturer_obj的优化变量顺序[w1, w2, em, er, A, B, C]下from scipy.optimize import LinearConstraint # 令 A - C 0对应变量顺序中的第5个和第7个元素 linear_constraint LinearConstraint( [[0, 0, 0, 0, 1, 0, -1]], -np.inf, 0 ) # 在 manufacturer_obj 的 minimize 调用里加 constraints[linear_constraint]补上约束后重新求解结果往往更符合论文推导的结构。这也是我从能跑通到能用于研究的关键一步。5. 结果可信度检验参数敏感性扫描与闭式解互验对博弈模型只跑一组参数验证结论是不够的数值迭代可能收敛到局部均衡也可能因为参数区域不同而结论方向变化。我每次复现这类论文模型都会做两步检验先做参数敏感性扫描再用闭式解交叉验证。5.1 对交叉价格弹性mu做参数扫描mu直接刻画新产品与再制品的竞争程度是论文结论的枢纽。常见做法是等间距取0.1到0.9对每个mu求解一次GN模型def sensitivity_mu(mu_listnp.linspace(0.1, 0.9, 9)): records [] for mu in mu_list: # 临时替换类属性是偷懒但有效的做法 Parameters.mu mu res solve_stackelberg(GN) records.append({ mu: mu, p1: res[p1], p2: res[p2], profit_m: res[manufacturer_profit], profit_r: res[retailer_profit] }) return records records sensitivity_mu() for r in records: print(r)输出里重点看三个信号p1 - p2价差是否随mu增大而收窄双方利润是否单调变化是否存在拐点。如果出现p2 p1说明新产品与再制品的价格定位已经失去论文假设的层级关系这个参数区域应当排除。需要说明直接改类属性在这里是偷懒写法正式做研究建议把Parameters改成实例化传入参数否则并发跑多个mu时会互相覆盖。5.2 数值解与闭式解交叉验证把迭代数值解与闭式解solve_GN的结果对比价格偏差在1e-2以内确认没有把目标函数符号、回收成本项写反。之后再看论文结论的符号方向GM下制造商利润上升、零售商利润基本不变GR下双方利润共同上升。这两个方向性验证通过模型的可靠性才算真正立住。从那以后我每次复现博弈模型都强制自己把这三步走完跑通默认参数、扫描关键参数确认方向稳定、对照论文结论的符号方向做断言。这套顺序帮我省掉了大量看起来能跑、一深究就错的返工时间希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?