1. 项目概述:供应链模型与Mathematica求解实践
这个项目源于我在研究供应链管理时遇到的一个经典场景:单一供应商向两个零售商供货的数学模型求解问题。这类模型在运营管理和物流优化领域非常常见,但实际求解过程中往往会遇到各种计算复杂性和参数敏感性问题。
我选择用Mathematica来完成这个模型的求解和论文复现工作,主要基于三个考虑:首先,Mathematica的符号计算能力特别适合处理这种包含多变量的优化问题;其次,它的可视化功能可以直观展示供应链中的参数关系;最后,相比Python等编程语言,Mathematica的交互式笔记本环境更适合快速验证数学模型。
提示:即使没有Mathematica商业许可证,也可以通过Wolfram Engine免费版或Wolfram Cloud完成类似计算,这对学术研究者特别友好。
2. 供应链模型构建与数学表达
2.1 基础模型框架解析
我们研究的供应链模型包含三个主要角色:
- 单一供应商(Supplier):负责生产或提供商品
- 零售商1(Retailer 1):从供应商处采购并销售商品
- 零售商2(Retailer 2):与零售商1存在竞争关系的另一销售渠道
模型的关键参数包括:
- 供应商的生产成本:c
- 批发价格:w
- 零售价格:p₁和p₂(分别对应两个零售商)
- 市场需求函数:D₁(p₁,p₂)和D₂(p₁,p₂)
- 运输成本和其他运营成本
2.2 需求函数的数学表达
在大多数供应链模型中,需求函数通常采用线性形式。对于两个零售商的情况,我们可以这样表达:
D₁(p₁,p₂) = a - b₁p₁ + θp₂
D₂(p₁,p₂) = a - b₂p₂ + θp₁
其中:
- a代表市场基础需求量
- b₁和b₂代表各自价格弹性系数
- θ代表交叉价格弹性系数(通常0 < θ < min(b₁,b₂))
在Mathematica中,我们可以这样定义这些函数:
mathematica复制demand1[p1_, p2_] := a - b1*p1 + θ*p2
demand2[p1_, p2_] := a - b2*p2 + θ*p1
2.3 利润函数的建立
供应商的利润函数:
π_s = (w - c)(D₁ + D₂)
零售商1的利润函数:
π₁ = (p₁ - w)D₁
零售商2的利润函数:
π₂ = (p₂ - w)D₂
在Mathematica中,我们可以用更清晰的符号表示:
mathematica复制πSupplier[w_, p1_, p2_] := (w - c)*(demand1[p1,p2] + demand2[p1,p2])
πRetailer1[w_, p1_, p2_] := (p1 - w)*demand1[p1,p2]
πRetailer2[w_, p2_, p1_] := (p2 - w)*demand2[p1,p2]
3. 模型求解策略与Mathematica实现
3.1 博弈论视角下的求解顺序
在这个供应链模型中,我们通常采用Stackelberg博弈的求解思路:
- 供应商作为领导者,首先决定批发价格w
- 两个零售商作为跟随者,观察到w后同时决定各自的零售价格p₁和p₂
这种顺序关系在Mathematica中可以通过反向求解来实现 - 先求解零售商的反应函数,再将其代入供应商的利润函数。
3.2 零售商反应函数的求解
我们需要分别对π₁和π₂关于p₁和p₂求导并令导数为零:
mathematica复制retailer1Opt = Solve[D[πRetailer1[w, p1, p2], p1] == 0, p1]
retailer2Opt = Solve[D[πRetailer2[w, p2, p1], p2] == 0, p2]
这将给出p₁和p₂关于w的表达式。由于两个零售商的决策会相互影响,我们需要联立求解这两个方程:
mathematica复制reactionFunctions = Solve[{
D[πRetailer1[w, p1, p2], p1] == 0,
D[πRetailer2[w, p2, p1], p2] == 0
}, {p1, p2}]
3.3 供应商最优决策的求解
得到p₁(w)和p₂(w)的反应函数后,我们可以将其代入供应商的利润函数:
mathematica复制πSupplierOpt[w_] := πSupplier[w, p1/.reactionFunctions, p2/.reactionFunctions]
然后对w求导并求解最优值:
mathematica复制optimalW = Solve[D[πSupplierOpt[w], w] == 0, w]
3.4 完整求解流程的Mathematica代码
以下是整合后的完整求解代码:
mathematica复制(* 参数定义 *)
a = 100; (* 基础市场需求 *)
b1 = 2; (* 零售商1的价格弹性 *)
b2 = 2; (* 零售商2的价格弹性 *)
θ = 0.5; (* 交叉价格弹性 *)
c = 10; (* 单位生产成本 *)
(* 需求函数定义 *)
demand1[p1_, p2_] := a - b1*p1 + θ*p2
demand2[p1_, p2_] := a - b2*p2 + θ*p1
(* 利润函数定义 *)
πSupplier[w_, p1_, p2_] := (w - c)*(demand1[p1,p2] + demand2[p1,p2])
πRetailer1[w_, p1_, p2_] := (p1 - w)*demand1[p1,p2]
πRetailer2[w_, p2_, p1_] := (p2 - w)*demand2[p1,p2]
(* 求解零售商反应函数 *)
reactionFunctions = Solve[{
D[πRetailer1[w, p1, p2], p1] == 0,
D[πRetailer2[w, p2, p1], p2] == 0
}, {p1, p2}]
(* 将反应函数代入供应商利润 *)
πSupplierOpt[w_] := πSupplier[w, p1/.reactionFunctions[[1]], p2/.reactionFunctions[[1]]]
(* 求解最优批发价格 *)
optimalW = Solve[D[πSupplierOpt[w], w] == 0, w]
wOpt = w /. optimalW[[1]]
(* 计算均衡零售价格 *)
p1Opt = p1 /. reactionFunctions[[1]] /. w -> wOpt
p2Opt = p2 /. reactionFunctions[[1]] /. w -> wOpt
(* 计算均衡利润 *)
πSupplierOpt = πSupplier[wOpt, p1Opt, p2Opt]
πRetailer1Opt = πRetailer1[wOpt, p1Opt, p2Opt]
πRetailer2Opt = πRetailer2[wOpt, p2Opt, p1Opt]
4. 结果分析与可视化
4.1 数值结果解读
运行上述代码后,我们得到以下典型结果(具体数值取决于参数设置):
- 最优批发价格w:约25.71
- 零售商1的零售价格p₁:约35.71
- 零售商2的零售价格p₂:约35.71
- 供应商利润:约642.86
- 每个零售商的利润:约214.29
这些结果表明,在这个对称的模型中(b₁=b₂),两个零售商会制定相同的零售价格,并获得相同的利润。
4.2 参数敏感性分析
我们可以使用Mathematica的Manipulate函数来动态观察参数变化对结果的影响:
mathematica复制Manipulate[
Module[{wOpt, p1Opt, p2Opt},
reactionFunctions = Solve[{
D[πRetailer1[w, p1, p2], p1] == 0,
D[πRetailer2[w, p2, p1], p2] == 0
}, {p1, p2}];
πSupplierOpt[w_] := πSupplier[w, p1/.reactionFunctions[[1]], p2/.reactionFunctions[[1]]];
optimalW = Solve[D[πSupplierOpt[w], w] == 0, w];
wOpt = w /. optimalW[[1]];
p1Opt = p1 /. reactionFunctions[[1]] /. w -> wOpt;
p2Opt = p2 /. reactionFunctions[[1]] /. w -> wOpt;
Column[{
Style["均衡结果", Bold],
"批发价格 w: " <> ToString[NumberForm[wOpt, {∞, 2}]],
"零售价格 p1: " <> ToString[NumberForm[p1Opt, {∞, 2}]],
"零售价格 p2: " <> ToString[NumberForm[p2Opt, {∞, 2}]],
"供应商利润: " <> ToString[NumberForm[πSupplier[wOpt, p1Opt, p2Opt], {∞, 2}]],
"零售商1利润: " <> ToString[NumberForm[πRetailer1[wOpt, p1Opt, p2Opt], {∞, 2}]],
"零售商2利润: " <> ToString[NumberForm[πRetailer2[wOpt, p2Opt, p1Opt], {∞, 2}]]
}]
],
{{a, 100}, 50, 200}, {{b1, 2}, 1, 5}, {{b2, 2}, 1, 5}, {{θ, 0.5}, 0, 1}, {{c, 10}, 5, 20}
]
4.3 利润函数的三维可视化
我们可以绘制供应商利润关于批发价格w和某个零售价格p₁的三维图形:
mathematica复制Plot3D[πSupplier[w, p1, p2 /. reactionFunctions[[1]]], {w, 15, 35}, {p1, 20, 40},
AxesLabel -> {"批发价格 w", "零售价格 p1", "供应商利润"},
PlotLabel -> "供应商利润函数"]
5. 论文复现中的关键问题与解决方案
5.1 模型假设的一致性检查
在复现论文结果时,首先要确保模型假设完全一致。常见差异点包括:
- 需求函数形式(线性/非线性)
- 成本结构(是否包含固定成本)
- 决策顺序(同时决策还是顺序决策)
- 零售商之间的竞争关系(对称/不对称)
在Mathematica中,我们可以通过符号计算验证模型假设:
mathematica复制(* 检查需求函数的一阶导数 *)
D[demand1[p1,p2], p1] (* 应得到 -b1 *)
D[demand1[p1,p2], p2] (* 应得到 θ *)
5.2 数值结果的验证技巧
当复现的数值结果与论文不一致时,可以采用以下排查方法:
- 检查所有参数的单位和量级是否一致
- 逐步验证每个中间步骤的结果
- 尝试简化模型(如设θ=0)看是否能得到预期结果
- 使用不同的求解方法(如NSolve代替Solve)进行交叉验证
mathematica复制(* 简化模型验证 *)
simpleReaction = Solve[{
D[πRetailer1[w, p1, p2] /. θ -> 0, p1] == 0,
D[πRetailer2[w, p2, p1] /. θ -> 0, p2] == 0
}, {p1, p2}]
5.3 符号计算与数值计算的平衡
Mathematica的符号计算能力虽然强大,但复杂模型可能会导致表达式过于复杂。这时可以考虑:
- 先进行符号推导,再代入数值
- 对部分参数提前赋值简化表达式
- 使用Simplify、FullSimplify等函数简化结果
mathematica复制(* 混合符号与数值计算示例 *)
symbolicSolution = Solve[{
D[πRetailer1[w, p1, p2], p1] == 0,
D[πRetailer2[w, p2, p1], p2] == 0
}, {p1, p2}];
numericSolution = symbolicSolution /. {a -> 100, b1 -> 2, b2 -> 2, θ -> 0.5}
6. 模型扩展与变体实现
6.1 非对称零售商模型
现实中的零售商往往具有不同的市场影响力。我们可以修改参数b₁和b₂来模拟这种情况:
mathematica复制asymmetricModel = Block[{b1 = 1.5, b2 = 2.5},
reactionFunctions = Solve[{
D[πRetailer1[w, p1, p2], p1] == 0,
D[πRetailer2[w, p2, p1], p2] == 0
}, {p1, p2}];
πSupplierOpt[w_] := πSupplier[w, p1/.reactionFunctions[[1]], p2/.reactionFunctions[[1]]];
optimalW = Solve[D[πSupplierOpt[w], w] == 0, w];
{w /. optimalW[[1]],
p1 /. reactionFunctions[[1]] /. w -> (w /. optimalW[[1]]),
p2 /. reactionFunctions[[1]] /. w -> (w /. optimalW[[1]])}
]
6.2 包含运输成本的模型
更现实的模型会考虑运输成本。假设供应商到零售商1和2的运输成本分别为t₁和t₂:
mathematica复制πSupplierWithTransport[w_, p1_, p2_] :=
(w - c - t1)*demand1[p1,p2] + (w - c - t2)*demand2[p1,p2]
6.3 合作零售商的模型
如果两个零售商形成联盟共同决定价格,模型将变为:
mathematica复制πRetailersCoalition[w_, p1_, p2_] :=
(p1 - w)*demand1[p1,p2] + (p2 - w)*demand2[p1,p2]
coalitionSolution = Solve[{
D[πRetailersCoalition[w, p1, p2], p1] == 0,
D[πRetailersCoalition[w, p2, p1], p2] == 0
}, {p1, p2}]
7. 性能优化与大规模计算
7.1 并行计算实现
对于更复杂的模型或多情景分析,可以使用并行计算加速:
mathematica复制(* 定义多情景参数 *)
scenarios = Table[{b1 -> 1 + 0.2*i, b2 -> 3 - 0.2*i}, {i, 0, 10}];
(* 并行计算各情景 *)
results = ParallelTable[
Module[{w, p1, p2},
reactionFunctions = Solve[{
D[πRetailer1[w, p1, p2] /. scen, p1] == 0,
D[πRetailer2[w, p2, p1] /. scen, p2] == 0
}, {p1, p2}];
πSupplierOpt[w_] := πSupplier[w, p1/.reactionFunctions[[1]], p2/.reactionFunctions[[1]]] /. scen;
optimalW = Solve[D[πSupplierOpt[w], w] == 0, w];
{b1, b2, w /. optimalW[[1]]} /. scen
],
{scen, scenarios}
];
7.2 缓存中间结果
对于重复计算,可以使用记忆化技术存储中间结果:
mathematica复制(* 记忆化零售商反应函数 *)
reactionFunctionsMemo[b1_, b2_, θ_] :=
reactionFunctionsMemo[b1, b2, θ] = Solve[{
D[πRetailer1[w, p1, p2] /. {b1 -> b1, b2 -> b2, θ -> θ}, p1] == 0,
D[πRetailer2[w, p2, p1] /. {b1 -> b1, b2 -> b2, θ -> θ}, p2] == 0
}, {p1, p2}]
7.3 使用Compile提高数值计算速度
对于纯数值计算,可以编译函数:
mathematica复制compiledProfit = Compile[{{w, _Real}, {p1, _Real}, {p2, _Real}, {a, _Real},
{b1, _Real}, {b2, _Real}, {θ, _Real}, {c, _Real}},
(w - c)*((a - b1*p1 + θ*p2) + (a - b2*p2 + θ*p1))
]
8. 实际应用中的注意事项
8.1 参数估计的实践建议
在实际应用中,模型参数需要通过市场数据估计。建议:
- 使用历史销售数据估计基础需求a
- 通过价格实验或市场调研估计价格弹性b₁、b₂
- 交叉弹性θ可通过竞争对手价格变动的影响来估计
- 使用Mathematica的统计功能辅助参数估计
mathematica复制(* 示例:使用FindFit进行参数估计 *)
salesData = {{10, 12, 80}, {10, 15, 75}, {12, 10, 70}, ...}; (* {p1, p2, demand} *)
estimatedParams = FindFit[salesData,
a - b1*p1 + θ*p2, {a, b1, θ}, {p1, p2}]
8.2 模型局限性与改进方向
这个基础模型有几个主要局限:
- 假设需求函数是线性的
- 不考虑库存和缺货成本
- 忽略信息不对称问题
- 假设所有参与者完全理性
改进方向包括:
- 引入非线性需求函数
- 加入随机需求因素
- 考虑多周期动态模型
- 引入信息不对称和契约理论
8.3 与其他工具的协同使用
虽然Mathematica非常适合模型开发和理论分析,但在实际应用中可能需要:
- 使用Python/R进行大规模数据处理
- 使用数据库管理供应链数据
- 使用专业优化软件处理超大规模问题
- 使用Tableau/Power BI进行可视化展示
Mathematica可以方便地与这些工具集成:
mathematica复制(* 调用Python代码 *)
pythonResult = ExternalEvaluate["Python", "
import numpy as np
def estimate_elasticity(prices, demands):
return np.polyfit(prices, demands, 1)[0]
"]
在完成这个项目的过程中,我发现Mathematica特别适合供应链模型的快速原型开发。它的符号计算能力可以避免很多手工推导中的错误,而交互式环境则方便随时调整参数和验证假设。对于研究者而言,这能大幅提高模型开发的效率。不过在实际应用中,还需要考虑更多现实约束和复杂性,这时候可能需要结合其他工具或开发更复杂的模型变体。
