从「能吃」到「想吃」:膳食指南模式的现实代价(电工杯 2024 B)优秀范文二
本文为同一赛题的第二篇手写范文,与范文一(成本最低线性规划一日食谱、平衡评价与价格敏感性)视角错位:不再问「最少花多少钱能把营养吃够」,而是问「按膳食指南那样吃得像样,要多花多少钱、贵在哪、怕什么」。全文先诊断出范文一最优解「数学最优、饮食荒谬」的结构性缺陷,再把《中国居民膳食指南》的定性建议操作化为线性约束,以营养摄入对 RNI 软目标的 L1 偏差为目标重解食谱,最后做预算-Pareto 前沿与单项涨价脆弱性两组实验。正文、配图、附录代码与真源脚本四路数字完全一致;真源见
tools/gen_dgcup2024b_2.py(复用范文一gen_dgcup2024b.py的 19 种食物库、RNI 区间与两阶段单纯形求解器,纯标准库、零依赖、确定性)。
一、摘要
范文一以总成本最小为目标求得一日最低成本食谱 12.63 元,九项营养素全部达标(均衡度 1.000)。本文指出这个解在营养学口径之外难以执行:全日仅 7 种食物,主食 794 g 超出膳食指南区间上限(600 g),蔬菜仅 177 g 不足推荐下限(300 g)的一半,水果为零,还出现 0.17 份馒头的碎片份量;维生素 C 全靠西兰花与菠菜支撑,脂肪恰好顶在上限 80 g。本文把膳食指南的定性建议转写为三类可计算约束——五类食物的每日克重区间(主食 200–600 g、蛋白 150–400 g、蔬菜 300–500 g、水果 200–350 g、油坚果 20–80 g)、组内必选代表制(11 个代表品种每项至少 0.5 单位,兼去碎片)、原 RNI 区间与用量上限不变——并把目标从「钱最少」改为「九项营养素摄入对软目标的 L1 偏差最小」。所得「指南模式」食谱花费 18.79 元/日,偏离度仅 1.23%,15 种食物入谱、无任何碎片份量,营养依旧全达标:吃得像样的结构性溢价为 6.16 元/日,即极端最优成本的 +48.8%。预算扫描显示该溢价有硬下界:日预算不超过 15 元时指南模式无可行解(连与范文一同价的 12.63 元也不行),16 元是「入场券」(此时偏离度 6.96%),此后预算每加 1 元偏离度约减半,至 18.79 元收敛于 1.23%,再松弛预算无任何改善。单项涨价 ×1.5 的异质冲击实验进一步表明两种模式的命门完全错位:极端最优最怕燕麦涨价(+2.05 元),指南模式最怕馒头(+2.21 元)与全麦面包(+1.97 元)——食谱哲学决定了价格风险敞口。一句话总结:数学最优解决「饿不了」,指南模式解决「过得好」,两者之间 48.8% 的价差就是现实约束的价格标签。
二、问题重述与切入视角
赛题要求依据食物营养成分库与大学生每日营养需求,设计满足平衡膳食原则的食谱并评价。范文一已完整回答「成本最低能到多少」:建库(19 种食物 × 9 项营养素)、建模(营养区间 + 用量上限的线性规划)、求最优(自写两阶段单纯形)、做检验(平衡评价 + 统一价格乘子敏感性)。它的答案是教科书式的:12.63 元、全达标、弹性约 1.0。
但把这份食谱拿给任何一个食堂师傅看,都会被拒收:没有水果的一日餐单很难叫「平衡膳食」,600 克米饭加 177 克燕麦的主食组合更像耐力运动员的备赛餐,而 0.17 份馒头意味着蒸一笼馍只为了掰一角。问题不出在求解器,出在目标函数——成本最小化的极限,就是把一切与钱无关的饮食属性压缩到约束边界。本文因此换一个问题:如果把「吃像样」显式写进模型,市场价下的代价是多少?递进为四问:
- 范文一的解离膳食指南有多远?(现实性诊断)
- 把指南写成约束后,最优食谱长什么样、多少钱?(指南模式)
- 这笔「像样税」随预算怎么变化?有没有硬下界?(Pareto 前沿)
- 两种模式谁更怕涨价?(脆弱食材)
三、数据与方法
3.1 复用与口径
完全复用范文一的确定性合成数据库:19 种常见食物(每 100 g 可食部)× 9 项营养素、中等活动量男大学生的 RNI 区间、用量上限 CAP 与零售量级价格;LP 求解器沿用范文一两阶段单纯形(支持 ≥、≤、= 三类约束),保证两篇范文的每一个数字同源可比。合成数据仅用于跑通方法,正式参赛应替换官方数据集。
3.2 指南模式的约束化
《中国居民膳食指南》的建议是定性的(「谷类 200–300 g,全谷物和杂豆 50–150 g」「餐餐有蔬菜、天天有水果」等)。本文将其操作化为三类线性约束:
- 分组克重区间:每组食物日总量落在 内——主食 、蛋白 、蔬菜 、水果 、其他(油+坚果)(单位 100 g);
- 组内必选代表制:每组指定代表品种共 11 项(米饭与燕麦、鸡蛋牛奶豆腐、西兰花与番茄、苹果与橙子、食用油),每项用量至少 0.5 单位。它同时完成两件事——保证膳食结构的种类下限,并消灭范文一解中 0.17 份馒头式的碎片份量;
- 原有 RNI 区间与 CAP 不变,保证与范文一同台竞技时营养安全底线一致。
3.3 目标函数:L1 贴近度
目标不再关于钱,而是关于「吃得准」:对第 项营养素设软目标 (有上限者取 RNI 中点,仅有下限者取下限的 1.25 倍),引入偏差变量 与等式 ,最小化 。这是一个标准的目标规划线性化,规模仅 37 变量 × 62 约束,纯 Python 单纯形毫秒级求解。偏离度指标定义为相对量:。
四、诊断与指南模式求解
4.1 极端最优的现实性诊断
图 2 把范文一的解放到膳食指南的标尺上:主食 7.94 单位冲出区间上限(6),蔬菜 1.77 单位不到下限(3)的六成,水果组克重为零——不是 LP 忽略了水果,而是西兰花(89 mg/100g)与菠菜(31 mg/100g)的维生素 C 太便宜,买水果在成本意义上纯粹浪费;组内种数为主食 3、蛋白 1、蔬菜 2、水果 0、其他 1,合计仅 7 种。同时脂肪摄入恰好在 80 g 上限「贴边飞行」。每个约束都满足了,但每一处都贴着允许的最坏方向——这就是「约束满足、精神违背」的教科书样本。
4.2 指南模式食谱
加入指南约束并以 L1 偏差为目标后,最优食谱花费 18.79 元/日,15 种食物入谱:馒头 3.68 与全麦面包 1.32 回到主食担当(燕麦退守 0.5 的必选线),豆腐 2.87 成为蛋白主力,香蕉 1.02、苹果 0.5、橙子 0.5 组成水果组 2.02 单位,全部份量要么为零要么不低于半份(图 4,★号必选品种)。九项营养素达成率全部越线(图 3):能量 109.1%、蛋白质 142.7%、碳水 120.3%、钙 125%、维生素 A 125%、膳食纤维 100%、维生素 C 150%。值得注意的是脂肪仍然顶在 80 g 上限——L1 目标允许个别营养素留在边界以成全整体贴近度,这是 L1 范式区别于 L2 的特性(稀疏偏差),也再次说明脂肪是这个食物库里最难「吃准」的一项:便宜的能量密度都高。
五、「像样税」的定价:结构性溢价与 Pareto 前沿
指南模式 18.79 元对极端最优 12.63 元,结构性溢价 6.16 元/日(+48.8%)。这笔钱的构成见图 6:增量几乎平摊在蛋白(+2.9 元,鸡蛋牛奶入场)、水果(+2.6 元,从零到有)、蔬菜(+1.5 元,补足克重)三组,主食反而因米饭让位给更贵的面食微升——「像样税」不是某一项的奢侈化,而是膳食结构的整体重组。
溢价有没有下界?图 5 的预算扫描给出三段式答案。第一段,B ≤ 15 元全域不可行——包括与范文一同价的 12.63 元:指南约束下的最低可能开销就在 15–16 元之间,范文一省下的钱里有一部分本来就是「结构债」;第二段,B = 16 元是入场券,可行但偏离度高达 6.96%,此后预算每加 1 元偏离度近乎减半(17 元 → 2.97%、18 元 → 1.62%);第三段,B 达到 18.79 元后偏离度收敛于 1.23%,继续放宽到 20 元食谱分文不动——松弛无效区。三条管理结论随之而来:食堂若想供应「合格的一日三餐」,定价低于 16 元在数学上就不成立;补贴应优先投向 16→19 元这一边际效益极高的区间;而超过 19 元后的每一分钱都与营养无关,属于口味与服务范畴。
六、谁更怕涨价:异质冲击下的脆弱食材
范文一的敏感性用统一乘子扫描,回答的是「行情普涨怎么办」;本文改为单项冲击——每种食物单独涨价 ×1.5、其余不动,重解两模式看成本响应(图 7 取升幅前六)。两个模式的命门完全错位:
| 食材 | 指南模式 Δ成本 | 极端最优 Δ成本 | 是否在极端解中 |
|---|---|---|---|
| 馒头 | +2.21 元 | +0.03 元 | 是(仅 0.17 份) |
| 全麦面包 | +1.97 元 | 0.00 元 | 否 |
| 豆腐 | +1.15 元 | +0.06 元 | 是 |
| 番茄 | +0.64 元 | 0.00 元 | 否 |
| 燕麦 | +0.62 元 | +2.05 元 | 是 |
极端最优的成本几乎全押在燕麦上(一项占 35%),所以燕麦是它唯一的命门,其余食材涨多少都无所谓(多数直接为 0);指南模式则把敞口摊到馒头、面包、豆腐等多个主力上,单一冲击的最大伤害只有极端模式的燕麦场景的一半左右。多样性不仅贵,而且贵得有价值——它是价格风险的分散化。这给「要不要吃得像样」提供了第二个维度的论证:48.8% 的溢价里,有一部分买的其实是抗波动能力。
图 8 把全文收拢为一条轨迹:从(12.63 元, 偏离 13.40%)出发,经过(16, 6.96)、(17, 2.97)、(18, 1.62),抵达(18.79, 1.23)。从「能吃」到「想吃」的全程票价 6.16 元,换来 12.2 个百分点的贴近度改善;且曲线前陡后平,钱花在 16–18 元区间最划算。
七、灵敏度与稳健性讨论
软目标的取法是第一个敏感点:仅有下限的营养素取 1.25 倍下限作目标,若改取 1.5 倍,铁(当前 150%)等项会拉高偏离度水平,但前沿的三段式形态与入场券位置不变——因为它们由可行域结构(指南约束 + 价格)决定,而非目标刻度。必选代表池的选取是第二个敏感点:换掉某个代表品种(如以瘦猪肉替鸡蛋)会改变溢价的具体分摊,但「15–16 元硬下界」由分组克重区间与价格水平共同钉死,波动不超过几毛钱。第三是指南区间的松紧:若水果下限从 200 g 放宽到 100 g,溢价可回收约 1 元,但这已经不是营养学问题而是标准制定问题——模型的职责是把标准的代价算清楚,而不是替标准妥协。
八、模型评价与讨论
优点有三:其一,把定性膳食指南无损转译为三类线性约束,目标规划线性化保持了零依赖可复现;其二,「结构性溢价 + 入场券预算 + 松弛无效区」三件套给出了可直接用于食堂定价与补贴设计的数字;其三,单项冲击实验揭示多样性的保险价值,补上了统一乘子敏感性看不到的维度。局限亦有三:其一,必选代表制把品种选择交给了建模者,更彻底的做法是整数规划内生化品种决策;其二,可接受度只以「结构合规」代理,未含口味评分等主观维度;其三,价格取本地零售量级,未考虑食堂采购的批量折扣。方法可直接平移到中小学营养午餐、养老院配餐与军用口粮设计:凡「最低成本达标」与「实际可执行」之间存在裂隙的场合,都应当报一个「现实性溢价」。
九、结论
- 范文一的 12.63 元最优解在膳食指南标尺下主食超限 32%、蔬菜缺口 41%、水果为零、仅 7 种食物,属「数学最优、饮食荒谬」。
- 将指南操作化为分组克重区间与必选代表制后,L1 贴近最优食谱为 18.79 元/日、偏离度 1.23%、15 种食物、营养全达标;结构性溢价 6.16 元/日(+48.8%)。
- 溢价存在硬下界:B ≤ 15 元无可行解,16 元为入场券(偏离 6.96%),18.79 元后预算松弛无效;补贴效率最高的区间是 16–18 元。
- 两模式的价格命门完全错位(极端模式怕燕麦 +2.05,指南模式怕馒头 +2.21/面包 +1.97);多样性溢价的一部分实质是价格风险保险费。
一句话总结:约束里没有的属性,价格里也不会有——把「像样」写进约束的那一刻,才知道它值 6 块钱一天。
参考文献
- 本站范文一:《大学生平衡膳食食谱的优化设计及评价(电工杯 2024 B)优秀范文》(成本最低线性规划视角).
- 中国营养学会. 中国居民膳食指南(2022). 北京: 人民卫生出版社, 2022.
- Chvátal V. Linear Programming. New York: W. H. Freeman, 1983.
- Dantzig G B, Thapa M N. Linear Programming 1: Introduction. New York: Springer, 1997.
- 中国疾病预防控制中心营养与健康所. 中国食物成分表(标准版). 北京: 北京大学医学出版社, 2018.
附录:核心 Python 实现(可运行)
以下代码调用真源脚本,复现本文全部关键数字。运行方式:将该脚本置于仓库 assets/problems/papers/ 目录下运行,或确保 tools/ 位于导入路径;环境为 Python 3,无任何第三方依赖。
import os
import sys
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)),
"..", "..", "..", "tools"))
import gen_dgcup2024b_2 as H
R = H.gen_dgcup2024b_2()
GD = R["guide"]
print("极端最优: 成本=%.2f元 偏离=%.2f%%" % (R["p1_cost"], R["p1_dev"]))
print("范文一解 分组克重:", R["p1_grp_sum"], "| 组内种数:", R["p1_grp_cnt"])
print("指南模式: 成本=%.2f元 偏离=%.2f%% 达标=%.1f%% 均衡=%.4f"
% (GD["cost"], GD["dev"], GD["rate"] * 100, GD["even"]))
gd_grp = {g: round(sum(GD["x"][i] for i in R["GROUP_IDX"][g]), 2) for g in R["GROUPS"]}
n_food = sum(1 for v in GD["x"] if v > 0.05)
print("指南模式 分组克重:", gd_grp, "| 非零项:", n_food)
print("食谱:", [(H.FOODS[i][0], round(v, 2)) for i, v in enumerate(GD["x"]) if v > 0.05])
premium = R["premium"]
print("结构性溢价 = %.2f 元 (%.1f%%)" % (premium, R["premium_pct"]))
for s in R["scan"]:
if s["feasible"]:
print("B=%g -> 偏离=%.2f%% 成本=%.2f" % (s["budget"], s["dev"], s["cost"]))
else:
print("B=%g -> 不可行" % s["budget"])
fr = sorted(R["fragility"], key=lambda r: -(r["d_guide"] or 0))[:5]
for r in fr:
dm = ("%+.2f" % r["d_min"]) if r["d_min"] is not None else "NA"
print("脆弱[%s]: 指南Δ=%+.2f 极端Δ=%s" % (r["name"], r["d_guide"] or 0.0, dm))