案例 159 一年存 6 万:先把这道题判成不可行
案例 159 一年存 6 万:先把这道题判成不可行
摘要:小哥想在一年里存 6 万元——寄回老家 4.8 万、留 1.2 万应急。把 12 个月的现金流写成目标规划:单量 \(n_m\) 决定跑单净收入 \(5.6n_m\),月支出 2449 元、寄回 4000 元共 6449 元刚性,月储蓄 \(s_m\) 与目标之间的差用偏差 \(u^+,u^-\) 吸收。求解结果是一次"判死":即便把单量顶到能力上限 32.6 单/天、每月 847.6 单,月储蓄仍是 -1,702 元,缺口 \(u^+\) 高达 80,429 元/年(6,702 元/月),总偏差等于缺口的 12 倍加年缺口。要真存到 6 万,需日均 78.6 单(2,044 单/月),是能力上限的 2.41 倍,换算每天要在线 25.3 小时。三条补救路径逐一量化:提单价 1 元/单降偏差 10,171 元,压支出 1 元/月只降 12 元,延长在线 1 小时降 5,425 元;三种手段全用上也补不平。结论:6 万不是"努力不够",是单价与单量的乘积极限根本够不到——先修单价,再谈储蓄。
关键词:目标规划;偏差变量;多期现金流;不可行诊断;能力上限
1 问题提出
1.1 问题是什么
目标很清楚:一年存 6 万 = 寄回老家 4.8 万(每月 4000 元,刚性)+ 应急 1.2 万。收入侧只有一个杠杆——跑单,单均净收入 5.6 元;支出侧是五个固定项加一笔寄回。
表 1 一个月的现金流(能力上限口径)
| 项目 | 金额 / 元 | 说明 |
|---|---|---|
| 跑单净收入 | 4,746.6 | \(5.6\times847.6\) 单 |
| 房租 | 850 | 城中村单间 |
| 餐饮 | 900 | 30 元/天 |
| 换电 / 通讯 / 杂项 | 699 | 包月 + 套餐 + 修车日用 |
| 寄回老家 | 4,000 | 4.8 万/年 |
| 月储蓄 | -1,702 | 收入减全部支出 |
表 2 题面三处口径对不上,必须先摊开
| 题面说法 | 换算 | 复核结论 |
|---|---|---|
| 月收入 9000 元 | \(9000/5.6=1,607\) 单/月 = 61.8 单/天 | 超能力上限 1.9 倍,是流水口径 |
| \(5.6\times845-6449\) | 实算 -1,717 元 | 题面写 \(-716\):\(5.6\times845=4732\),需 5733 才有 \(-716\) |
| 日均 55 单 | \(55\times26=1430\) 单/月 | 超上限 1.69 倍;实为"只存 1.2 万"口径 |
三个数放在一起就露出真相:845 单是一个月的量(≈上限),55 单是一天的量(≈1.4 倍上限),9000 元是流水而不是净收入。 混用三者会得出互相矛盾的结论——题面"日 55 单"与"845 单"正是这么撞上的。
1.2 为什么这值得建模
其一,这是典型的"目标不可行"问题,而不是"最优解"问题。 线性规划在这里只会给出一个边界解,真正有价值的是 \(u^+\) 的大小——它把"差多少"从一个感觉变成一个数。
其二,储蓄是 12 个月的累积量。 单月少花 300 元像是杯水车薪,乘 12 才看得出值 3600 元;多期结构不写进模型,任何"这个月省一点"的建议都无法比较。
其三,补救手段的效率差三个数量级。 单价、支出、在线时长、目标,四个旋钮的"每 1 元代价换多少偏差"完全不同。不算清这个,讨论就会停在"要努力"上。
1.3 Gurobi 如何求解
模型是一个 12 期的线性目标规划:12 个单量、12 个储蓄、24 个月度偏差、2 个年度偏差,共 50 个变量、36 条约束。Gurobi 走的是纯 LP 路径——presolve 先把 \(s_m\) 代换掉,问题退化成"单量取上限",根节点即闭合,单次求解 <0.02 s。全案共求解 17 次(主解 + 4 个补救方案 + 12 个目标/能力组合),总耗时不足 3 s。真正的工作量不在求解,而在口径复核:把日单量、月单量、流水、净收入四件事拆开。
2 数学模型
2.1 符号说明
| 符号 | 含义 | 取值 |
|---|---|---|
| \(n_m\) | 第 \(m\) 月单量(决策) | \([0,847.6]\) 单/月 |
| \(s_m\) | 第 \(m\) 月储蓄(决策) | 元,可为负 |
| \(p\) | 单均净收入 | 5.6 元/单 |
| \(c\) | 非寄回月支出 | 2449 元 |
| \(r\) | 每月寄回 | 4000 元 |
| \(T\) | 年储蓄目标 | 60000 元 |
| \(u^+,u^-\) | 年目标的缺口 / 盈余 | 元 |
| \(v_m^+,v_m^-\) | 月目标的缺口 / 盈余 | 元 |
2.2 目标函数
目标规划不最大化钱,而是最小化"没达到目标的程度"。月度偏差与年度偏差同时进入目标,权重都取 1。由于 12 个月的月目标之和恰好等于年目标,两套偏差在最优解处同步收缩,不会互相拉扯;月度项的作用是给出"每月差多少"的诊断量。
2.3 约束条件
每月现金流是一条恒等式——跑单净收入减掉支出,剩下的才是储蓄:
代入本案 \(p=5.6\)、\(c=2449\)、\(r=4000\):\(s_m = 5.6n_m - 6449\)。这一条就是全部悲剧的来源——斜率为 5.6,截距为 \(-6449\),要想 \(s_m>0\),单量必须超过 \(6449/5.6=1,152\) 单/月。
月度目标与年度目标分别用偏差变量吸收:
能力约束把"努力"封顶,\(s_m\) 不受符号限制:
\(s_m\) 必须允许取负——这正是本案与普通储蓄模型的区别。若强行加 \(s_m\ge0\),模型立刻不可行,连"差多少"都问不出来。
2.4 规模与性质
50 个连续变量、36 条等式/不等式,无整数变量。结构上它是一个"单变量线性系统":所有 \(n_m\) 同斜率、同上限,故最优解必然把每个 \(n_m\) 顶到 \(\bar n\)(因为 \(u^+\) 的系数为负梯度方向)。于是答案可以手算:\(s^{*}=5.6\times847.6-6449=-1,702\),\(u^{+}=60000-12\times(-1,702)=80,429\)。Gurobi 的价值不在找这个解,而在于当参数变成向量(单价分档、支出分项、能力分月)时,仍能一次给出全部偏差,并为后面的灵敏度扫描提供统一口径。
3 Gurobi 求解实现
3.1 建模要点
n = m.addVars(H, lb=0, ub=CAPM) # 月单量上限 32.6 x 26
s = m.addVars(H, lb=-GRB.INFINITY) # 储蓄可为负
vp, vm, up, um = m.addVars(H, lb=0), m.addVars(H, lb=0), m.addVar(lb=0), m.addVar(lb=0)
for k in range(H):
m.addConstr(s[k] == P * n[k] - NONREM - REMIT) # 现金流
m.addConstr(s[k] + vp[k] - vm[k] == TGY / H) # 月目标 5000
m.addConstr(gp.quicksum(s[k] for k in range(H)) + up - um == TGY)
m.setObjective(gp.quicksum(vp[k] + vm[k] for k in range(H)) + up + um, GRB.MINIMIZE)
两个细节。第一,\(s_m\) 的下界必须放开到 \(-\infty\)。写成 lb=0 会直接让模型 INFEASIBLE,Gurobi 只会回一句"无可行解",而看不出缺口有多大——本案要的恰恰是那个缺口。第二,偏差变量的符号方向不能反:\(s_m + v^+ - v^- = T/12\) 里 \(v^+\) 是"没达到",若写成 \(v^-\),模型会奖励"少存钱",最优解退化成 \(n_m=0\)。这两处是目标规划最常见的两个坑。
3.2 求解结果
表 3 GP 主解与口径复核
| 指标 | 数值 | 算式 |
|---|---|---|
| 月储蓄 | -1,702 元 | \(5.6\times847.6-6449\) |
| 年储蓄 | -20,429 元 | \(\times12\) |
| 年缺口 \(u^+\) | 80,429 元 | \(60000-12\times(-1,702)\);月缺口 \(v^+=6,702\) |
| 盈余 \(u^-\) | 0 元 | 缺口为正时恒为 0(总偏差 160,859) |
| 存 6 万所需 | 2,044 单/月 = 78.6 单/天 | 超上限 2.41 倍,需 25.3 h/天 |
4 结果分析
4.1 结构:一个月的钱怎么流走

图 1 月度收支瀑布:跑单净收入 4,747 元如何被 6449 元支出吃穿
图 1 把一个月拆成八段。第一段收入 4,747 元,接着五段生活支出共 2449 元,再一段寄回 4000 元,走到最后不是剩下的钱,而是 **-1,702 元**——横条越过零点,落到负半轴。**这张图是全案的地基:储蓄为负不是"存得少",是每个月都在借 1700 元过日子。** 五段生活支出加起来只占 38%,真正的重量是那笔 4000 元的寄回。4.2 散点:一条永远碰不到目标线的直线

图 2 日单量—月储蓄关系(气泡=每日在线小时,红区为负储蓄)
图 2 是一条斜率 145.6 元/(单/天) 的直线。它穿过零点的位置在 **44.3 单/天**——已经比能力上限 32.6 高出 36%;要碰到 5000 元的月目标线,得走到 **78.6 单/天**,气泡代表的在线时长已经涨到 25.3 小时。**两个标记点之间没有可行解:上限以内全是负数,正数全在上限以外。** 这就是"不可行"三个字在图上最直白的样子。4.3 方案对比:四个旋钮,最大的一转也不够

图 3 五条路径的年缺口对比(柱内为相对现状的改善额)
图 3 是五个方案的缺口。现状 80,429 元;压支出 300 元/月降 3,600 元,延长在线 1 小时降 5,425 元,单价加 1 元降 10,171 元,目标降到 3 万降 30,000 元。**降目标是唯一"一定成功"的旋钮,却也是最没用的**——它不产生一分钱,只是把缺口改名。真正有杠杆的是单价:每提 1 元/单,一年少 10,171 元偏差,是压支出的 848 倍。4.4 时序:12 个月只会越来越远

图 4 12 个月累计储蓄:现状、双管齐下、目标路径
图 4 三条线。绿线是目标路径,12 个月匀速爬到 6 万;红线是现状,每月往下掉 1,702 元,年末累计 **-20,429 元**;橙线是"压支出 300 + 延在线 1 小时"双管齐下,仍只有 -11,405 元。**两条实线都是向下的,说明时间不是朋友而是债主——每过一个月,缺口多 1700 元。**4.5 主灵敏度:单价才是斜率

图 5 日单量与单价对月储蓄的联合影响(右轴为达标所需单价)
图 5 三组曲线,斜率随单价从 5.6 涨到 10.6 而翻倍,但截距一直钉在 $-6449$。右轴那条下坡线是"在给定单量下达到月存 5000 所需单价":单量 32.6 时要 13.51 元/单,单量 40 时要 11.01 元/单。**当前单价 5.6 元落在最下方那条线上,位置比"收支平衡"所需的 7.61 元还低 26%——连不亏都做不到。**4.6 二维分区:可行域是一条窄缝

图 6 月单量 × 支出压缩的年储蓄分区(虚线 0 元,实线 6 万)
图 6 的填色是年储蓄。黑色虚线是收支平衡线,蓝色实线是 6 万线,两者几乎平行、相距极远:平衡线落在 1150 单附近(1050 与 1250 两列之间),6 万线要到 2045 单。纵轴把支出压到极限 600 元/月,6 万线也只左移到约 1935 单——压 600 元/月全年多 7200 元,只够把 6 万线左移 107 单。**方向对,量级太小。**4.7 第三维:目标越高,缺口按 1:1 涨

图 7 储蓄目标与单量能力对年缺口的联合影响(右轴为所需日单量)
图 7 五个目标、两种能力。能力 847.6 单时缺口从 20,429 元(目标 0)一路涨到 110,429 元(目标 9 万),斜率恰好 1——**因为储蓄在能力处已经是负数,目标每加 1 元,缺口就加 1 元**。把能力提到 1200 单/月,缺口整体下移 23,681 元,但 6 万处仍是 56,748 元。右轴提醒得更直接:目标 6 万要 78.6 单/天,9 万要 95.8 单/天,两条都远在 32.6 的虚线之上。4.8 帕累托前沿:可行域到 848 单就断了

图 8 储蓄目标—所需单量前沿与不可行阴影
图 8 是本题的"能力边界线"。前沿是 $n^{*}(T)=(T/12+6449)/5.6$,一条斜率 0.0149 单/元的直线;水平虚线是 847.6 单的能力上限。**前沿与上限的交点落在 $T=-20,429$ 元——也就是说,能力范围内能实现的最高年储蓄是负两万,不是六万。** 交点右侧全部画成阴影,那里没有解,只有缺口。4.9 分布与边际:波动救不了系统性的负

图 9 日单量随机波动下的年储蓄分布与累计概率
图 9 让日单量围绕 32.6 波动(变异系数 15%,20000 次抽样)。年储蓄的均值 -20,449 元,标准差约 8,583 元,分布整体压在零线左侧:**达到收支平衡的概率只有 1.79%,达到 6 万的概率是 0.000%**。运气好的月份能少亏一点,但没有一条路径能翻正——这不是方差问题,是均值问题。
图 10 每单口径的收入与支出分解(7.61 元成本 vs 5.60 元收入)
图 10 把月账摊到每一单:房租 1.00 元、餐饮 1.06 元、换电 0.23 元、通讯 0.12 元、杂项 0.47 元、寄回 4.72 元,合计 **7.61 元/单**,而每单只赚 5.60 元。**每跑一单净亏 2.01 元,跑得越多亏得越多——除非单价先修好。** 这张图解释了为什么"多跑单"在本案里是负效率:它放大的是亏损,不是储蓄。5 结论
第一,6 万这个目标在给定参数下不可行,缺口 80,429 元/年。 能力上限处月储蓄 -1,702 元,年储蓄 -20,429 元;\(u^+=80,429\) 元就是全部答案,\(u^-\) 恒为 0。
第二,题面的"日均 55 单"与"845 单"是两个口径。 845 是月单量(≈上限 847.6),55 是日单量(×26=1430 单/月,超上限 1.69 倍);55 单/天其实对应"只存 1.2 万应急"的口径(需 51.2 单/天)。
第三,存 6 万需日均 78.6 单、每天在线 25.3 小时。 这是能力上限的 2.41 倍,无论怎么排班都排不出来;题面的 18.7 小时隐含单均 14.3 分钟(4.2 单/时),比本案的 3.1 单/时快 35%,按那个口径也要 18.7 h。
第四,四个旋钮的效率差三个数量级。 单价每 1 元降偏差 10,171 元,延长在线每小时降 5,425 元,压支出每元/月只降 12 元,降目标 1 元降 1 元。压支出 300 + 延 1 小时 + 目标降到 3 万,三条一起上也只补 39,025 元,仍有 41,405 元缺口。
第五,先修单价,再谈储蓄。 收支平衡需单价 7.61 元/单(+36%),存 3 万需 10.56 元/单(+89%),存 6 万需 13.51 元/单(+141%)。在单价不动的前提下,任何"再努力一点"的方案都只是在 -1,702 元这个负数附近打转。
参考文献
[1] Gurobi Optimization LLC. Gurobi Optimizer Reference Manual[Z]. 2024.
[2] 司守奎, 孙玺菁. 数学建模算法与应用[M]. 3 版. 北京: 国防工业出版社, 2021.
[3] Romero C. Handbook of Critical Issues in Goal Programming[M]. Oxford: Pergamon Press, 1991.

浙公网安备 33010602011771号