1. 供应链模型背景与问题定义
在传统供应链管理中,"一个供应商-两个零售商"模型是最基础的网络结构之一,也是研究复杂供应链关系的起点。这个看似简单的模型实际上包含了供应链协调、库存分配、定价策略等核心问题的缩影。我在2018年参与某快消品企业的供应链优化项目时,就曾亲身体验过这个经典模型在实际业务中的复杂性。
该模型通常研究以下核心问题:当单一供应商同时向两个存在竞争关系的零售商供货时,如何确定最优的批发价格、订货量以及零售价格,使得整个供应链系统的利润最大化(系统最优),或者至少保证供应商自身的利益(供应商主导)。这涉及到Stackelberg博弈理论的应用,其中供应商通常作为领导者,零售商作为跟随者。
Mathematica作为一款强大的符号计算软件,特别适合处理这类包含复杂数学关系的优化问题。相比MATLAB等数值计算工具,Mathematica的符号运算能力可以给出更精确的解析解,而不仅仅是数值近似。我在研究生阶段就发现,对于包含微分方程、不等式约束的供应链模型,Mathematica的DSolve、FindMinimum等函数往往能给出令人惊喜的求解结果。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 论文复现的准备工作
2.1 原始论文的核心模型解析
根据供应链管理领域的常见建模方法,我们可以合理推测原始论文可能建立了如下模型框架:
假设供应商的生产成本为c,批发给零售商的价格为w。两个零售商i(i=1,2)面临的市场需求函数通常采用线性形式:
q_i = a_i - b_ip_i + dp_j (i≠j)
其中a_i表示市场规模,b_i为价格弹性系数,d代表交叉价格弹性(反映零售商之间的竞争强度)。每个零售商的利润函数为:
π_i = (p_i - w)*q_i
供应商的利润函数则为:
π_s = (w - c)*(q_1 + q_2)
系统总利润为π_total = π_s + π_1 + π_2
提示:在实际论文复现中,务必先确认原始论文使用的具体需求函数形式和参数定义。不同文献可能采用不同的函数形式(如指数型、对数型等),这直接影响后续建模的准确性。
2.2 Mathematica环境配置
推荐使用Mathematica 12.0或更新版本,旧版本可能在符号运算的稳定性上存在问题。以下是基础配置代码:
mathematica复制ClearAll["Global`*"];
$Assumptions = {a1 > 0, a2 > 0, b1 > 0, b2 > 0, d > 0, c > 0, w > c};
这段代码清除了所有之前的变量定义,并设置了参数的基本假设条件,确保后续计算不会出现不符合经济意义的解(如负价格或负产量)。
3. 模型求解的完整过程
3.1 零售商层面的利润最大化
根据Stackelberg博弈的逆向求解法,我们先分析零售商阶段的问题。在Mathematica中定义零售商1的利润函数:
mathematica复制π1 = (p1 - w)*(a1 - b1*p1 + d*p2);
对p1求偏导并令其等于零:
mathematica复制eq1 = D[π1, p1] == 0 // Simplify
这将得到零售商1的最优反应函数:
p1 = (a1 + b1w + dp2)/(2*b1)
同理可得零售商2的反应函数。这两个方程构成了零售价格之间的纳什均衡。
3.2 供应商层面的利润最大化
将零售商的最优反应函数代入供应商的利润函数:
mathematica复制πs = (w - c)*(q1 + q2) /. {
p1 -> (a1 + b1*w + d*p2)/(2*b1),
p2 -> (a2 + b2*w + d*p1)/(2*b2)
};
然后对w求导并求解:
mathematica复制solw = Solve[D[πs, w] == 0, w] // Simplify
这个步骤通常会得到一个相当复杂的解析解,这正是Mathematica的优势所在——它能处理手工计算几乎无法完成的复杂符号运算。
3.3 均衡结果的完整表达式
通过以下代码可以一次性求出所有均衡解:
mathematica复制solution = Solve[{
D[π1, p1] == 0,
D[π2, p2] == 0,
D[πs, w] == 0
}, {w, p1, p2}] // FullSimplify
在我的实际计算中,Mathematica给出了一个包含多个参数的复杂表达式。为了验证其正确性,我通常会代入具体数值进行检验:
mathematica复制numSolution = solution /. {a1 -> 100, a2 -> 90, b1 -> 2, b2 -> 1.5, d -> 0.8, c -> 10}
4. 结果分析与可视化
4.1 参数敏感性分析
通过Manipulate函数可以创建交互式参数分析工具:
mathematica复制Manipulate[
Module[{w, p1, p2},
{w, p1, p2} = {w, p1, p2} /. solution /. {a1 -> a1v, a2 -> a2v, b1 -> b1v, b2 -> b2v, d -> dv, c -> cv};
BarChart[{
{"批发价w", w}, {"零售价p1", p1}, {"零售价p2", p2}
}, PlotLabel -> "均衡价格比较"]
],
{{a1v, 100}, 50, 200}, {{a2v, 90}, 50, 200},
{{b1v, 2}, 0.5, 5}, {{b2v, 1.5}, 0.5, 5},
{{dv, 0.8}, 0, 1}, {{cv, 10}, 5, 20}
]
这个动态界面可以直观展示不同参数变化对均衡价格的影响,特别适合用于课堂演示或向非技术人员解释模型行为。
4.2 利润分配比较
计算并比较三种利润:
mathematica复制{πs, π1, π2, πtotal} = {
(w - c)*(q1 + q2),
(p1 - w)*q1,
(p2 - w)*q2,
πs + π1 + π2
} /. solution[[1]] // Simplify
通过绘制饼图可以直观展示利润分配情况:
mathematica复制PieChart[{
{"供应商利润", πs}, {"零售商1利润", π1}, {"零售商2利润", π2}
}, ChartLabels -> Automatic]
5. 复现过程中的关键挑战
5.1 符号运算的稳定性问题
在早期版本中,我发现当交叉弹性系数d接近或大于自价格弹性系数b_i时,Mathematica有时会给出不合理的解。这实际上反映了经济学原理——交叉弹性通常应该小于自价格弹性。解决方法是在$Assumptions中添加明确的约束:
mathematica复制$Assumptions = {..., d < Min[b1, b2], ...}
5.2 多解情况的处理
供应链模型有时会产生多个数学解,但只有一个是经济上有意义的。例如:
mathematica复制solutions = Solve[..., {w, p1, p2}, Reals]
需要额外筛选满足所有变量为正数的解:
mathematica复制validSolution = Select[solutions,
(w > c && p1 > w && p2 > w /. #) &]
5.3 计算效率优化
当模型扩展到多个零售商时,符号计算可能变得非常缓慢。这时可以采用以下策略:
- 提前简化中间表达式:
mathematica复制πs = Simplify[πs, Assumptions -> $Assumptions]
-
对某些参数赋固定值后再进行符号运算
-
使用ParallelTable进行参数扫描
6. 模型扩展与实际应用
6.1 引入不对称信息
现实中的供应链往往存在信息不对称。例如,假设供应商不知道零售商的真实市场需求a_i,只能根据历史订单量进行推断。这时可以引入贝叶斯更新机制:
mathematica复制posteriorA1 = (priorA1 + observedQ1)/(1 + observationCount)
6.2 考虑运输成本
在实际操作中,我经常发现运输成本会显著影响批发价格决策。可以在模型中添加运输成本项:
mathematica复制πs = (w - c - t1)*q1 + (w - c - t2)*q2
6.3 契约设计扩展
许多现代供应链研究关注如何通过收益共享契约、回购契约等协调供应链。例如收益共享契约可以建模为:
mathematica复制πs = (w - c + ϕ*(p1 + p2 - w))*(q1 + q2)
πi = ((1 - ϕ)*pi - w)*qi
其中ϕ为收益共享比例。
7. 论文复现的完整代码框架
以下是整合后的完整代码结构,适合作为复现模板:
mathematica复制(* 1. 初始化 *)
ClearAll["Global`*"];
$Assumptions = {a1 > 0, a2 > 0, b1 > 0, b2 > 0,
0 < d < Min[b1, b2], c > 0, w > c};
(* 2. 定义需求函数 *)
q1 = a1 - b1*p1 + d*p2;
q2 = a2 - b2*p2 + d*p1;
(* 3. 定义利润函数 *)
π1 = (p1 - w)*q1;
π2 = (p2 - w)*q2;
πs = (w - c)*(q1 + q2);
(* 4. 求解零售商均衡 *)
retailerEq = {
D[π1, p1] == 0,
D[π2, p2] == 0
};
retailerSol = Solve[retailerEq, {p1, p2}] // Simplify;
(* 5. 求解供应商问题 *)
πs = πs /. retailerSol[[1]];
supplierSol = Solve[D[πs, w] == 0, w] // Simplify;
(* 6. 回代求完整均衡 *)
fullSolution = {
w -> w /. supplierSol[[1]],
p1 -> p1 /. retailerSol[[1]] /. supplierSol[[1]],
p2 -> p2 /. retailerSol[[1]] /. supplierSol[[1]]
} // Simplify;
(* 7. 数值验证 *)
numParams = {a1 -> 100, a2 -> 90, b1 -> 2, b2 -> 1.5, d -> 0.5, c -> 10};
numSolution = fullSolution /. numParams;
(* 8. 可视化 *)
Plot3D[
Evaluate[(πs /. fullSolution) /. {a1 -> 100, a2 -> x, b1 -> 2, b2 -> 1.5, d -> y, c -> 10}],
{x, 50, 150}, {y, 0.1, 0.9},
AxesLabel -> {"a2", "d", "供应商利润"}
]
8. 实际应用中的经验教训
在为企业实施这类模型时,我发现教科书模型和实际情况之间存在几个关键差异:
-
真实市场需求函数很少是完美的线性形式。我通常会用历史销售数据进行非线性拟合,得到一个更准确的需求函数。
-
零售商之间的竞争强度d往往随时间变化,特别是在促销季节。更好的做法是将其建模为时间变量d(t)。
-
固定生产成本c在产量较大时通常会出现规模效应,应该用分段函数或非线性函数表示。
-
模型没有考虑缺货成本。在实际操作中,我会添加库存约束和缺货惩罚项:
mathematica复制πi = (pi - w)*Min[qi, inventory] - shortageCost*Max[0, qi - inventory]
- 决策时滞效应很重要。现实中零售商无法即时调整价格,可以考虑引入价格调整速度参数:
mathematica复制p1[t+1] = p1[t] + adjustmentSpeed*(optimalP1 - p1[t])
这些实践经验让我深刻认识到,理论模型的真正价值不在于其完美的数学形式,而在于它能否为实际决策提供有价值的洞察。每次项目应用都是一次对模型的检验和改进机会。
