2023 国赛 C 题模拟赛完整复盘(蔬菜定价与补货)
时间:8/16–8/19
我应该没有摸鱼吧
仓库:https://github.com/Raisetsu41/MCM/
赛题与数据
题目:2023 高教社杯 C 题《蔬菜类商品的自动定价与补货决策》。
四问:
- 分析各蔬菜品类/单品销售量的分布规律及相互关系;
- 以品类为单位,分析销售总量与成本加成定价的关系,给出未来一周(2023-07-01~07)的日补货总量与定价策略;
- 单品层面:可售单品 27–33 个、最小陈列量 2.5 kg,根据 6/24–30 可售品种给出 7/1 的单品补货量与定价;
- 还需要采集哪些数据、对解决上述问题有何帮助。
附件:
| 附件 | 内容 | 规模 |
|---|---|---|
| 附件1 | 商品信息(6 品类 251 单品) | 251×4 |
| 附件2 | 销售流水 | 878,503 行,2020-07-01 ~ 2023-06-30(T=1095 天) |
| 附件3 | 批发价格 | 55,982 行 |
| 附件4 | 损耗率(单品级 + 分类级) | 251+6 条 |
清洗口径:退货按负销量并入当日净销量;单品日表 46,599 行、品类日表 6,474 行;有销售单品 246 个(5 个无销售记录)。
团队与分工
我以为”割裂分工”是比较糟糕的,所以约定:每个问题三人共同参与建模讨论,代码由我主要承担,论文统稿由 ljh 负责,lzy 主建模。实践下来效果不错,但后期统稿压力集中。
协作规范(COLLABORATION.md):
- 一人统稿:维护
main.tex、摘要、ref.bib、附录; - 一人主建模、一人主代码,每个问题设 lead,其他人交叉 review;
- 尽量只改自己负责的
.tex文件,main.tex/ref.bib由统稿人改; main分支只放能编译的稳定版本,大改动开feature/<名字>-<内容>;- 提交信息用动词开头;提交前
git pull --rebase origin main; - 提交前本地 xelatex 编译通过。
工作流(这次用的流程)
整体按”分析 → 建模 → 编码 → 绘图 → 写作 → 验收”的流水线推进,产物都落在 workspace/:
2analysis-modeling(建模稿)
→ 3coding-visual(代码 + 图表 + RESULTS_REPORT)
→ 4drawio(非数据图)
→ 5writing(论文章节)
→ 6verity(最终验收)- 建模稿里我习惯用
[cjg]补丁标注修正,保留队友原文、只追加说明; - 所有关键数字都留 CSV/JSON 证据,论文只引用 results/ 里的值。
问题一:分布、季节、关联、聚类
思路
拆成四个递进的子模型:
- 分布模型:品类日销量用 lognormal/gamma 拟合(AIC 选型、BIC 核对);单品大量零值 → 两阶段混合分布(售出概率 + 条件正销量分布);
- 季节模型:STL 分解周内季节(period=7, robust=True),年季节用月度聚合;
- 协同网络:星期/月份虚拟变量残差化后取 Spearman 秩相关,BH-FDR 校正,前后半样本稳健边建网;
- 聚类:六维时序特征(售出占比/有售日均/变异系数/周末偏置/节假日脉冲/销量占比)+ z-score + Ward 层次聚类。
关键结果
| 项 | 结果 |
|---|---|
| 品类分布 | gamma 4 个(花叶/花菜/水根/茄),lognorm 2 个(辣椒/食用菌),AIC=BIC 一致 |
| 单品分布 | 132 个进入参数拟合(gamma 81 / lognorm 51),售出概率 0.0557~0.9826 |
| STL | 季节强度 0.1729 |
| 旺季检验 | 茄类 1.5412、花叶 1.0945、花菜 1.0089 大于 1,其余品类淡季反而更高 |
| 协同网络 | 8,646 对中主边 1,062(正 820 / 负 242),稳健边 154、51 节点 |
| 品类相关 | 花叶-食用菌 0.651、花叶-辣椒 0.636、辣椒-食用菌 0.638 |
| 聚类 | k=5,silhouette 0.2583,80% 抽样 50 次 ARI 0.4567 |
插曲:伪篮子的证伪
一开始想用(日期, 扫码时间)构造伪交易 ID 跑 Apriori,结果在附件 2 上实测:同秒多件不同单品交易的占比极低,且无法排除多台收银机并发,属于伪回归。于是放弃关联规则,把颗粒度上卷到日频做协同网络——这个”先证伪再换方法”的过程最后也写进了建模稿。
问题二:弹性 → 加价率 → 预测 → 报童
模型链
最优加价率:常弹性下 求导得 ()。但实测六品类全是弱弹性(),利润在可行域内单调 → 历史加价率区间 网格搜索的边界解。
预测:先用弹性把历史销量折算到参考价的”去价格序列”,再做含趋势/星期/月份的回归外推(对数均值修正 ),最后按最优价乘性缩放。
报童:,分位数用 lognormal 矩匹配(与问题一分布口径衔接),补货量 。
关键结果
| 品类 | 弹性 | t 值 | 最优加价率 | 最优售价 |
|---|---|---|---|---|
| 花叶类 | -0.45 | -6.79 | 1.01 | 7.22 |
| 花菜类 | -0.50 | -5.45 | 0.71 | 14.00 |
| 水生根茎类 | -0.07 | -0.70(不显著) | 0.44 | 16.07 |
| 茄类 | -0.26 | -3.16 | 0.90 | 7.81 |
| 辣椒类 | -0.13 | -3.03 | 1.14 | 6.79 |
| 食用菌 | -0.31 | -5.28 | 0.82 | 4.53 |
- 未来一周:总补货 2607.89 kg,总期望利润 5305.19 元;
- 补货呈周末集中结构(7/1+7/2 占全周 36.2%);
- 水生根茎类弹性不显著,定价结论降级为”证据不足”;
- 报童 vs 确定性订货:5305.19 vs 4550.45 元(+754.74),报童更少订(负安全库存,)但期望利润更高;
- 回测:后 28 天 MAPE 20.6%~37.2%;2022-07 同期回测 23.9%~66.4%(跨年稳定性有限)。
纠结:Q-P 模型 vs k-π 推导
这轮里纠结最多的是:需求侧明明建的是 模型,定价策略那边为什么用加价率 推导?
理顺后的口径:顾客只能看到货架价 P,加价率 是派生输出,不是需求自变量。4.2 本来就是价格空间求导;4.3 的网格搜索也是”对夹逼后最终价格评估利润”。弱弹性下利润单调、最优恒在边界,所以可行域口径(加价率区间 vs 价格区间)直接决定数字——这也是为什么”中位价口径 +72.8%“这种看似离谱的结果其实是口径问题。
联合定价扩展:直接用 6×6 交叉弹性 + 差分进化,独立与联合同在价格可行域 下比较:独立 3265.50 vs 联合 3861.36 元/日(+18.2%);交叉项 15/30 显著(9 正 6 负),仅显著项 +15.6%、缩半 +5.4%。
问题三:单品级选品、定价与补货 MIP
模型
- 候选集:6/24–30 有售且周均净销量 ≥ 0.5 kg/天 → 45 个(有售 49 个剪枝后;花叶 16/辣椒 9/食用菌 8/水根 6/茄 4/花菜 2);
- 单品弹性:Log-Log 回归 + Shrinkage 收缩 (, 或无变异直接借品类弹性;12/45 借用品类弹性);
- 价格网格:历史 等距 11 档,参考价 显式并入网格;
- 0-1 MIP(HiGHS):同时决策上架/选档/订购,目标 = 收入 − 进货成本 − 缺货惩罚;
- 约束:、、定价唯一、、缺货量定义、品类覆盖。
关键结果(7/1)
| 指标 | 数值 |
|---|---|
| 选中单品 | 33(花叶 11/辣椒 8/食用菌 6/茄 3/水根 3/花菜 2) |
| 总订购量 | 296.83 kg |
| 总销量 = 总需求 | 270.14 kg,满足率 1.00 |
| 总收入 / 总成本 / 总利润 | 1915.69 / 1233.61 / 682.08 元 |
| 求解状态 | HiGHS optimal,gap=0,节点数 1,无降级 |
| 陈列刚性超订 | 1.16 kg(10.53 元,3 个低需求单品被最小陈列卡到 2.5 kg) |
收缩后单品弹性区间 ;小米椒(份) 单品毛利 109.49 元最高。
问题四:数据采集建议
五维方案,正文只留 2 页:
- 需求侧:会员 ID + 购物篮(Lift 识别互补/替代)+ 缺货登记(修正截断低估);
- 市场侧:货架拓扑 + 竞品实时均价 → 交叉价格弹性 ,替代效应按货架距离衰减;
- 运营侧:分时段库存 + 打折流水 → 日内动态定价(贝尔曼方程)叠在日级报童之上;
- 供给侧:分环节损耗率 + 供应商履约指数 → 时变损耗函数 + 提前期安全库存;
- 外部环境:天气/节假日/促销日历 → 回归外生变量,去混淆弹性。
优先级:外部环境(极低)> 需求侧 > 运营侧 > 供给侧 > 市场侧(高成本)。
模型检验与灵敏度
七项量化检验,阈值全部运行前声明,契约在 results/q2_robustness_summary.json:
| 检验项 | 观测值 | 状态 |
|---|---|---|
| 批发价 ±10% | 利润变化 <1% | PASS |
| 弹性 ±20% | 利润变化 <0.5% | PASS |
| 价格口径切换 | 中位价口径 +72.8%、弹性符号翻转 | CONDITIONAL |
| 报童 vs 确定性 | +754.74 元 | PASS |
| 损耗率替换 | 补货量 -1.0% | PASS |
| 2022-07 同期回测 | MAPE 最高 66.4%(>40%) | CONDITIONAL |
| 品类相关联合模拟 | 期望利润偏差 +0.11%,方差比 2.47 | PASS |
问题三灵敏度(14 个情景重解 MIP):批发价是最敏感输入(利润 -17.6%/+18.1%),选品数 27/30/33 → 657.54/673.60/682.08,网格离散化影响 ±4.5%,rho 和大 M 完全不变。
论文与页面控制
页码历程
初稿全篇(含附录代码)一度超过 100 页 → 通过压缩正文到 约 30 页(参考文献 4 页、附录约 65 页不计页数)。最终正文分布大致:封面+摘要 3、引言假设 1、Q1 7、Q2 10、Q3 4、Q4 3、检验+评价 4。
ljh 复盘里的页面控制经验
- 写作时给每个 section 定页数区间,并标注图表是否必须;
- 窄行多的表格改成并列/紧凑形式控制表高;
- 图表位置用
[H]固定,避免htbp乱跑留大片空白; - 每部分定稿后由论文手先做一轮页面控制,不要等到最后;
- 参考文献 58 条偏多,注意中文文献占比,避免”全是英文 → 像 AI 生成”的观感。
验收发现的坑
ref.bib出现过重复 key(silver2016),bibtex 直接报错;- 引用了不存在的
\ref{sec:sensitivity},编译出 ”??”; - 附录表格出现 ”—” 占位符没填;
- 硬编码”见第九章/9.1.1”这类章号,删改章节后全错,应该用
\ref;
踩坑与复盘汇总
Git / 协作
| 问题 | 解决 |
|---|---|
| push/pull 连接不稳定 | 改用 SSH 协议 |
| 提交后队友不知道 | 改完在群里说一声 |
| 冲突 | 提交前 git pull --rebase origin main;只改自己的文件 |
| 前期分工不均 | 每节定结果后立即排版引用,别把活堆给统稿人 |
建模 / 代码
- 伪关联要证伪:扫码时间构造伪篮子不可行,日频上卷才是对的;
- 价格口径决定一切:固定基期权重防”当期加权 → 机械负相关”;
- 弱弹性下”最优”是历史区间约束下的最优,论文必须写明边界;
- 缺货截断会让净销量低估真实需求(Q2/Q3 共享局限);
- 7/1 是周六但没做周末偏置,列为局限;
流程上的改进点(下次比赛)
- 定选题后尽快完善文档并标 deadline;
- 论文手前期就参与排版,别让最后 48 小时集中爆发;
- 图表移位/流程图问题提前准备替代方案(比如外部工具出图再插入);
- 参考文献从一开始就控制数量和质量。
仓库一览
MCM/
├── README.md / COLLABORATION.md / NOTES.md
├── materials/ # 模板与参考资料(只读)
├── Problem/ # 赛题 + 附件 1-4
└── workspace/
├── main.tex # 论文主文档
├── sections/ # 00 摘要 ~ 09 附录
├── draft/ # 建模稿 + RESULTS_REPORT
├── code/ # clean_data.py, question1/A-D.py,
│ # question2/nv.cpp+q2.py+q2_sensitivity.py,
│ # question3/q3.py
├── results/ # 清洗数据与全部结果 CSV/JSON
├── figures/ # 论文图表 PDF
└── fonts/ # 模板字体(勿动)编译:xelatex main.tex && bibtex main && xelatex main.tex && xelatex main.tex。
相关笔记
- xmu-math-modeling:暑期数学建模课程索引
- 2026-08-04-AI-Prompt-Note:AI 提示词在数模中的应用