ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

用Mathematica复现竞争零售商渠道策略论文的完整指南

用Mathematica复现竞争零售商渠道策略论文的完整指南 复现这篇竞争零售商渠道策略的论文项目标题里写的是mathematic实际指的就是Wolfram Mathematica学术界一般直接叫Mathematica。这篇博文把整个复现过程从头捋一遍从模型设定、符号推导到均衡判定、数值画图再包括我踩进去又爬出来的几个坑。如果你正在复现运营管理、产业组织、供应链方向的理论模型或者刚拿到一篇渠道策略论文想用Mathematica验证结论这篇内容应该能帮你少走好几天弯路。先说清楚论文复现不是把作者公式抄进notebook就完事。真正的复现要把决策逻辑完整还原消费者需求怎么定义、零售商之间怎么竞争、渠道选择博弈时序是什么、用的是纳什均衡还是子博弈精炼均衡这些底层问题理不清楚代码写得再漂亮也对不上结果。1. 复现项目到底在做什么1.1 论文复现的三层目标复现一篇论文表面上是重算一遍结果实际是在做三件事。第一是验证。作者在论文里声称某些参数条件下双渠道是均衡某些条件下传统渠道是均衡我们得独立算一遍确认这些结论站得住脚。独立这个关键词很重要最好不直接参考作者给的中间推导而是从需求函数开始用另一套工具链重推。这样能避免被作者的计算错误带偏也能发现自己理解上的盲区。第二是精细理解模型。论文里到处是“经计算可得”背后可能省掉了好几页代数。复现时要把那些省略的步骤补回来补的过程中你对模型机制的理解会完全不一样。比如竞争强度对线下价格的影响看公式结论感受不深自己推导时才会意识到渠道结构变了价格反应函数的斜率也会变。第三是为扩展研究打基础。很多人不是从零建模型而是在已有模型上加设定比如多一个渠道、换一种需求函数、引入不对称成本。复现一遍相当于把地基打牢后面扩展才敢放开手脚。1.2 竞争零售商渠道策略的典型设定这类论文的模型框架高度相似。市场上有两个竞争零售商记为A和B销售可替代产品价格竞争。每个零售商可以选两种渠道策略只做线下传统渠道简写为T或者线上线下双渠道简写为D。关键在决策时序。第一阶段两个零售商同时决定是否开通线上渠道。第二阶段给定渠道结构双方做价格竞争。求解用逆向归纳先解第二阶段的价格均衡再回到第一阶段比较利润判断什么渠道组合能成为纳什均衡。需求侧通常用线性需求函数因为有良好的解析性质能推出闭合解。竞争强度用交叉价格系数表示系数越大产品替代性越强价格竞争越激烈。消费者渠道偏好用线上渠道的天然需求占比表示其余比例归线下。线上渠道本身有权衡它有覆盖增量市场的优势但要付出单位配送成本还要付开通固定成本。论文关心的核心问题是双渠道均衡在什么条件下出现双渠道一定占优吗竞争强度、消费者渠道偏好、线上成本与固定成本如何影响均衡渠道结构最终答案都落在参数空间的分区上所以我们复现的最后一步几乎总是画区域图。1.3 复现流程拆解我把复现流程固定成五步。第一步把模型设定翻译成Mathematica符号表达式包括参数假设、需求函数、利润函数。第二步对每种渠道组合求解第二阶段价格均衡写成反应函数再联立。第三步把均衡价格代回利润函数得到第一阶段各策略组合下每个零售商的利润。第四步根据利润比较构造纳什均衡条件通常是一组不等式。第五步在参数空间上绘制均衡区域图和论文结果对照。流程听起来简单实际每一步都有隐藏陷阱。符号解太长、多解筛选、不等式化简不干净这些后面单独讲。先说说为什么要用Mathematica而不是Python或者Matlab。2. 为什么选Mathematica做这种复现2.1 符号推导和规则求解是核心能力复现这类模型真正的难点不在编程而在符号推导。要解联立方程、化简带参数的高次表达式、判断不等式成立条件这套需求几乎是为Mathematica量身定做的。求解方程用Solve求偏导用D解不等式系统用Reduce化简用Simplify和FullSimplify替换表达式用斜杠点。这套语法看起来有些怪但组合起来非常顺手。举个例子求解混合渠道结构下的价格均衡手算可能要一整页纸Mathematica里就是几行代码而且符号推导不出错不会因为手算漏项导致后续结果漂移。更重要的是不等式处理。复现中要判断一个零售商有没有动机偏离当前渠道策略本质是判断一组带参数的不等式是否成立。Reduce可以在给定参数范围内输出完整的条件分支这部分Sympy目前还做不到这么干净。2.2 符号、数值、可视化在一个环境里完成我坚持用Mathematica的另一个原因是流程完整。复现过程中经常需要从符号推导直接跳到数值验证再跳到画图。比如先符号解出均衡价格马上代入一组具体参数看数值再画RegionPlot看区域变化整个过程在同一个notebook里连续完成。Manipulate这个交互式控件对理解模型帮助很大。拖动竞争强度或者固定成本的滑块均衡区域实时变化模型的比较静态一目了然。给导师或者合作者展示的时候这种动态演示也比静态图片有说服力。2.3 需要提前接受的几个缺点Mathematica也不是没有毛病。第一语法和主流语言差异大函数名首字母大写、方括号传参、用规则替换数据新手适应期不短。第二全符号计算容易失控尤其是不加约束直接对高次系统求解跑几个钟头出不来是常事。第三表达式过长时输出可读性差满屏都是带一堆参数的分式需要及时Simplify或封装成函数。但从复现论文这个场景看这些缺点都可以接受。Python加上Sympy虽然开源免费复杂不等式化简时会明显吃力而这类模型最后的均衡条件恰恰就是一堆不等式。3. 核心模型搭建与符号推导实操3.1 从需求函数开始参数、变量与假设我习惯把模型先完整写在纸上再翻译成代码避免边写边改逻辑混乱。参数方面用a表示基础市场规模用θ表示产品替代系数用λ表示消费者天然倾向线上渠道的比例用c表示线上渠道单位配送服务成本用F表示开通线上渠道的固定成本。开头的清理操作也很重要。每次新建notebook第一行执行ClearAll[Global*]把之前遗留的变量全部清掉。这个习惯救了我很多次不然上次定义的pA会在下一次运行中悄悄混进当前计算导致看似无法解释的错误。然后是需求函数。我采用一个经典的线性需求框架。当零售商A只做线下渠道时它的需求是DA[PA_, PB_] : a - PA theta * PB这里的含义是基础市场规模减去自身价格再加对手价格乘以替代系数。如果零售商开通双渠道需求按消费者渠道偏好拆成线下和线上两部分。A线下需求为(1 - lambda)(a - PAr theta * PBeff)线上需求为lambda (a - PAo theta * PBeff)其中PBeff是B在两个渠道上的加权平均价格PBeff[pBr_, pBo_] : (1 - lambda) pBr lambda pBo这个加权平均设定背后的直觉是一个双渠道零售商对竞争对手形成的价格压力等于它在两个渠道价格的平均水平。这样做能保持需求函数的线性结构适合做解析推导。参数约束我会显式写出来assume {a 0, theta 0, theta 1, lambda 0, lambda 1, c 0, F 0};后面做Reduce或者Simplify时把这些假设条件作为约束传进去能大大减少无意义解的出现。3.2 四种渠道结构下的利润函数与一阶条件四种渠道结构分别是TT、DD、TD和DT其中TD和DT在对称设定下结论相同所以实际只需要处理TT、DD、TD三类。先看最简单的TT结构。零售商A的利润函数是价格乘以需求ProfitA_T[pA_, pB_] : pA * DA[pA, pB];一阶条件就是利润对自身价格求导等于零focTT D[ProfitA_T[pA, pB], pA] 0;解出反应函数为pA (a theta * pB)/2。由于对称性令pA pB p得到均衡价格p a/(2 - theta)均衡利润为profitTT p * (a - p theta * p) /. p - a/(2 - theta)结果化简后是a^2/(2 - theta)^2。这个过程本身就展示了复现论文时常用的小技巧不要急着把两个零售商都写进去利用对称性先减少变量计算量会小很多。再看DD结构。由于完全对称可以假设线下价格相同线上价格也相同分别记为pr和po。A线下利润和线上利润在价格上恰好是可分的所以可以分开优化ProfitA_DD[pr_, po_] : pr * (1 - lambda) (a - pr theta * pr) (po - c) * lambda (a - po theta * po) - F;对pr求导得到pr a/(2(1 - theta))对po求导得到po a/(2(1 - theta)) c/2。注意线下价格和线上配送成本无关这个反直觉的结论来自需求函数中线下、线上市场彼此独立线上成本只会完全转嫁到线上价格。最难的是TD混合结构。A只做线下B做双渠道。这时A的需求会受B加权平均价格影响而B的线下和线上价格又分别受A价格影响形成一个三方程联立系统。我在代码里这样写ProfitA_TinMixed[pA_, pBr_, pBo_] : pA * (a - pA theta * ( (1 - lambda) pBr lambda pBo)); ProfitB_DinMixed[pA_, pBr_, pBo_] : pBr * (1 - lambda) (a - pBr theta * pA) (pBo - c) * lambda (a - pBo theta * pA) - F; eqMixed { D[ProfitA_TinMixed[pA, pBr, pBo], pA] 0, D[ProfitB_DinMixed[pA, pBr, pBo], pBr] 0, D[ProfitB_DinMixed[pA, pBr, pBo], pBo] 0 }; solMixed Solve[eqMixed, {pA, pBr, pBo}] // Simplify;运行后输出的表达式会很长但这正是复现的价值所在。混合结构下A作为单渠道零售商面对双渠道对手时定价逻辑会发生明显变化后面画均衡区域图时这就是划分混合均衡区域的关键素材。3.3 均衡渠道结构的判定与参数区域划分第二步解出价格均衡后把均衡价格代回利润函数得到每种渠道结构下的利润值。然后进入两阶段博弈的均衡判定。以TT均衡为例。A不偏离到D的条件是B选择T时A在T下的利润不低于A单方面切换到D下的利润。也就是profitA_TT profitA_DT。这里profitA_DT表示B做T、A做D时A的利润。同理B也要满足对称条件。DD均衡的条件则是profitA_DD profitA_TD即B做D时A不要想退回T。注意这个条件方向很容易写反我每次都要对着博弈树重新捋一遍。代码层面我建议把这个条件定义为布尔表达式condDD (profitA_DD profitA_TD) (profitB_DD profitB_TD); condTT (profitA_TT profitA_DT) (profitB_TT profitB_TD);然后可以用Reduce求解参数条件Reduce[condDD (assume /. List - And), {theta, lambda}] // FullSimplify不过要注意直接对包含F的不等式做全符号求解会非常慢甚至跑不完。我的经验是分两步走。第一步保留少数参数符号比如固定a 1把c和F赋值这样Reduce只在二维参数空间上做不等式化简速度快很多。第二步用数值扫描验证符号区域边界。实际操作中论文里的参数分区图大都是固定其他参数后在二维平面画的所以这种降维处理并不会丢失核心信息。4. 数值模拟与论文图形复现4.1 参数设定与代码组织数值模拟之前我习惯把均衡利润表达式封装成只依赖参数的函数而不是每次复制粘贴一大段结果。比如ClearAll[evalProfit]; evalProfit[structure_, aVal_, thetaVal_, lambdaVal_, cVal_, FVal_] : Module[ {a aVal, theta thetaVal, lambda lambdaVal, c cVal, F FVal}, Switch[structure, TT, profitTT, DD, profitDD, TD, profitTD ] ];这样组织代码的好处是后期画图时不需要重复推导直接调用即可。参数取a1c0.2F0.1θ范围取0到0.8λ范围取0到1。4.2 用RegionPlot还原均衡区域图RegionPlot是还原参数分区图的主力函数。你只需要把均衡条件写进第一个参数指定坐标范围图形就出来了RegionPlot[ {condDD, condTT, condMixed}, {theta, 0, 0.8}, {lambda, 0, 1}, PlotLegends - {双渠道均衡, 传统渠道均衡, 混合渠道均衡}, BoundaryStyle - Directive[Thick, Gray], PlotStyle - {Opacity[0.3], Opacity[0.3], Opacity[0.3]} ]注意condMixed要仔细定义。混合结构有两种取向A为T和B为D或者反过来。对称设定下两者条件相同画图时合并成一个区域。如果坐标系里的区域边界和论文不一致不要急着改代码先回到均衡条件确认不等式方向是否写反。实际复现中我不建议一次性把所有条件丢进RegionPlot。先单个画condDD再画condTT最后叠加混合区域这样出错时能立刻定位是哪一层的逻辑问题。4.3 用Manipulate做动态参数探索静态图只能反映一组参数的结果我更喜欢用Manipulate交互式探索Manipulate[ RegionPlot[ {condDD, condTT, condMixed}, {theta, 0, 0.8}, {lambda, 0, 1}, PlotLegends - {DD, TT, Mixed} ], {c, 0, 0.5}, {F, 0, 0.2} ]拖动滑块的时候能非常直观地看到固定成本F上升双渠道均衡区域缩小传统渠道均衡区域扩大。这种动态演示对理解模型机制很有帮助也适合在组会或者答辩时使用。5. 复现过程中的常见问题与排查技巧5.1 一阶条件符号求解卡住怎么办Solve输入进去Mathematica转圈几小时不出结果这个问题几乎每个人都会碰到。我的排查顺序固定如下。先检查方程数量是否等于变量数量。少一个条件就会让符号求解陷入长时间计算。再检查变量之间是否存在可分离结构比如DD结构下线下和线上价格互不耦合就应该分开解不要强行整个方程组一起Solve。然后是加假设条件。Solve本身不直接利用Assumptions但Reduce可以利用。如果Solve跑不动改成Reduce把参数范围约束写进不等式系统通常能更快得到结果。最后的大招是数值探路。先给参数赋一组具体数值用FindRoot求数值均衡确认解的存在性和唯一性再回头推符号解。数值能算出来但符号跑不动的时候基本可以确定是表达式复杂度过高这时需要人工分析替换变量。5.2 多解、增根和边界解筛选符号求解经常返回多组解其中绝大多数没有经济学意义。我的筛选步骤是两步。第一步做经济意义筛选要求价格为正、需求为正、利润为正validSols Select[sol, (pA 0 pB 0) /. # ];第二步做稳定性筛选即二阶条件。价格竞争均衡需要在利润函数的严格凹点取得。用Hessian矩阵判断hessian D[ProfitA_TT[pA, pB], {{pA, pB}, 2}]; NegativeDefiniteMatrixQ[hessian]线性需求模型下这个条件相对简单但混合结构里还是有必要确认一次。5.3 数值结果和论文对不上排查方向这是最打击人的环节。我的排查顺序是固定的符号定义、参数假设、均衡条件、坐标范围。先核对需求函数形式。有些作者用的是q a - p θ(p_j - p_i)这样的相对价格形式而我这里用的是q a - p_i θ p_j的绝对价格形式。两者模型本质不同结论也不同复现时必须有意识地对照原论文。再核对成本处理位置。有的论文把线上单位成本放在价格里记为po - c有的把它折进渠道效用变成需求函数里的项。放错位置结果会完全对不上。然后是均衡判定方向。混合结构均衡的偏离方向特别容易搞反。最后看画图范围论文可能用了归一化参数比如令a1θ的定义域上限可能取1而不是0.8坐标范围不对也会让图形看起来不一致。5.4 性能优化技巧能用数值的地方尽量别走符号。想验证某个命题在某个参数点是否成立直接用具体数值算不要每一次都从符号解开始。批量扫描参数空间时用ParallelTable替换Table多核并行能明显提速。曾经画一次均衡分区图需要扫密集网格换并行后耗时从十几分钟降到两分钟。如果要对同一个大表达式反复代入不同参数求值可以考虑Compile生成更快的数值函数。线性需求模型一般用不上但模型一旦加入消费者效用函数之类的高复杂度项性能差距会非常大。6. 几点实操心得与扩展方向6.1 复现论文的正确打开方式经过这次复现我最大的体会是复现一篇论文大部分时间花在理解模型和调试边界条件上真正写代码的时间只占一小部分。拿到论文后我先不急着打开软件而是花半天把模型结构吃透把变量表、参数表、均衡条件全部手写一遍。这一遍认真写完后面代码基本就是按图索骥。还有一个小技巧给每个渠道结构单独建一个代码块命名清楚比如“3.1 TT结构一阶条件”“3.2 DD结构一阶条件”。等所有结构都建完再统一做利润比较和画图。这样即使后面发现某个结构推导错了修改范围也很局部不会把其他部分连带搞乱。6.2 这个模型还能往哪些方向扩展复现完成后这个模型的可扩展性很强。第一成本不对称。假设A的线上成本cA和B的线上成本cB不同均衡区域会从对称结构变成更丰富的分区可能推出更有意思的结论。第二渠道偏好内生。消费者不是固定比例偏好线上而是基于价格和体验自行选择渠道。这会引入渠道层面的交叉价格弹性模型复杂度会上一个台阶。第三订单履约方式。线上订单可以选择配送到家或者到店自提固定成本从一次性投入变成分阶段开通成本这也是近几年文献里比较活跃的方向。顺带说一句复现过程中我还养成了一个习惯每算完一个结构就把均衡价格的表达式用传统数学记号写在笔记本上再和代码输出对照。这看起来笨但确实帮我逮住了好几处手写推导和代码定义不一致的问题。别太相信自己脑子里的记号写出来才靠得住。
返回列表