案例 080 收入-时间-疲劳的三目标

案例 080 收入-时间-疲劳的三目标

摘要:外卖小哥想多赚、想少跑、还不想太累,三个目标彼此打架。日收益 \(R(h)=17.5h-0.35h^{2}\) 边际递减,疲劳 \(F(h)=h^{2}/30\) 边际递增,在线小时 \(h\) 夹在中间。用 6 段折线把 \(R(h)\) 线性化后做 20 次 ε-约束扫描:时间上限 \(\varepsilon_{1}\) 从 2 h 扫到 14 h,收益从 33.4 元涨到 176.4 元,代价是疲劳从 0.13 涨到 6.53。膝点按"边际替代率等于整段平均替代率"定在 7.00 h,收益 105.3 元、疲劳 1.63,此时 1 单位疲劳还能换回 27.0 元。换成加权和 \(\max R-wh\) 做同样的 20 次求解,只得到 7 个不同的 \(h\)——全是折点;ε 扫描的 20 个点全部落在段内,加权和一个也采不到。\(\varepsilon_{2}\) 收到 1.2 以下,膝点被疲劳约束砍掉,前沿塌回半天档。共 63 次求解,墙钟 2 s。

关键词:三目标优化;ε-约束法;Pareto 前沿;分段线性化;边际替代率

1 问题提出

1.1 问题是什么

小哥一天的账本只有三行,把平台口径摆出来:

  • 收益:\(R(h)=17.5h-0.35h^{2}\),首小时进账 17.5 元,边际每小时递减 0.7 元;
  • 疲劳:\(F(h)=h^{2}/30\),跑 7 h 累 1.63,跑 14 h 累 6.53,平方级往上翻;
  • 硬边界:平台强制下线,\(0\le h\le 14\)。

三者不是一个方向:收益要往后推、时间和疲劳要往前压。所以这题没有"最优解",只有"一串同样合理、但取舍不同的解"。题面称时间与收益的交点在 \(h\approx 9.2\) h,这一点第 4.1 节用模型核实。

表 1 三种方案在同一模型下的取值

方案 在线小时 PWL 收益 / 元 疲劳 \(F\) 相对膝点 定位
时间最少(半天) 4.00 64.01 0.53 -39.2% 保出勤
膝点 7.00 105.35 1.63 100.0% 替代率转折
纯收益最大 14.00 176.40 6.53 67.4% 满负荷 14 h

1.2 为什么要优化

第一,"多跑一小时值不值"是可以算汇率的。收益对疲劳求导得到边际替代率 \(\mathrm{MRS}(h)\),它随 \(h\) 单调下降:跑 2 h 时 1 单位疲劳换 121 元,跑 14 h 时只换 8.3 元。汇率跌破心理价位的地方,就是该收工的地方。

第二,三目标必须降维,否则解不出来。三个目标同时最大没有唯一解,求解器只会随机返回一个顶点。降维有两条路:加权和(把疲劳折成钱)和 ε-约束(把时间、疲劳关进约束)。前者要你先回答"1 单位疲劳值多少元",后者只要回答"今天最多跑几小时"——后者骑手答得出来。

第三,线性化会改变前沿的形状。\(R(h)\) 是二次的,Gurobi 只认线性,6 段折线一插,前沿从光滑曲线变成 7 个折点的折线。折点让加权和"看得见"的解骤减,这正是本案例要展示的东西。

1.3 Gurobi 如何求解

\(R(h)\) 与 \(F(h)\) 都通过 λ 形式(凸组合 + SOS2)写成线性表达式,\(h\)、\(R\)、\(F\) 由同一组 \(\lambda\) 驱动,因此折线与真实曲线的偏差只来自分段本身,不来自变量之间的耦合。单次模型 10 个变量、1 条 SOS2、4 条线性约束,根节点即收敛;20 次 ε 扫描、20 次加权和、20 次网格、20 次疲劳价格扫描合计 63 次求解,墙钟 2 s。

2 数学模型

2.1 符号

\(h\) 在线小时,\(R\) 日收益(元),\(F\) 疲劳指数,\(x_{k}\) 第 \(k\) 个折点(\(k=0,\dots,6\)),\(\lambda_{k}\) 凸组合权重,\(\varepsilon_{1}\) 时间上限,\(\varepsilon_{2}\) 疲劳上限,\(w\) 加权和权重,\(\pi\) 疲劳的影子价格。

2.2 目标

三目标的原始形态是向量最大化,同时要收益大、时间短、疲劳小:

\[\max\ \left(R(h),\ -h,\ -F(h)\right) \]

它没有唯一解。本案例保留收益、把时间和疲劳降成约束,两个量的定义与 ε-约束模型为:

\[R(h)=17.5h-0.35h^{2},\qquad F(h)=\frac{h^{2}}{30},\qquad 0\le h\le 14\tag{1} \]

\[\max_{h}\ R(h)\quad \mathrm{s.t.}\quad h\le\varepsilon_{1},\quad F(h)\le\varepsilon_{2}\tag{2} \]

2.3 约束

折线线性化是本案例的核心,也是后面所有"折点"的来源:

\[R(h)\approx\sum_{k=0}^{6}\lambda_{k}R(x_{k}),\quad h=\sum_{k=0}^{6}\lambda_{k}x_{k},\quad \sum_{k=0}^{6}\lambda_{k}=1,\quad \lambda\in\mathrm{SOS2}\tag{3} \]

其中 \(x_{k}=14k/6\),\(R(x_{k})\) 为折点处的真实收益。\(R\) 在 \([0,14]\) 上严格凹,λ 形式的线性松弛本身就在折点取极值,SOS2 只是把"不许跨段插值"写成显式约束。疲劳项不需要单独近似:\(F(h)\) 由同一组 \(\lambda\) 线性组合,\(F\) 与 \(R\) 一一对应。加权和的对照模型只有一个参数 \(w\):

\[\max_{h}\ R(h)-w\,h\tag{4} \]

2.4 规模与性质

10 个连续变量、1 条 SOS2、4 条线性等式、1—2 条 ε 约束,LP + SOS2 分支,单次求解在毫秒级。前沿结构完全由 \(R\) 的凹性决定:只要疲劳不紧,最优解就是 \(h^{\ast}=\min\{\varepsilon_{1},\sqrt{30\varepsilon_{2}}\}\),即 \(\varepsilon_{1}\) 本身;\(\varepsilon_{2}\) 一旦低于 \(F(\varepsilon_{1})\),疲劳约束接管,前沿被横向切断。

3 Gurobi 求解实现

3.1 建模要点

lam = m.addVars(7, lb=0.0, ub=1.0)
h, Rv = m.addVar(lb=0.0, ub=14.0), m.addVar(lb=-GRB.INFINITY)
m.addConstr(gp.quicksum(lam) == 1)
m.addConstr(h  == gp.quicksum(lam[k] * XP[k] for k in range(7)))
m.addConstr(Rv == gp.quicksum(lam[k] * RP[k] for k in range(7)))
m.addSOS(GRB.SOS_TYPE2, [lam[k] for k in range(7)])

一个实现细节:\(h\) 与 \(R_{v}\) 都由 \(\lambda\) 定义,不要再给 \(h\) 单独加"对齐折点"的等式,否则 SOS2 会把 \(h\) 钉死在折点上、段内点全部丢失——那正好是加权和法的失败模式,别在建模阶段自己先犯一遍。

3.2 求解结果

表 2 $\varepsilon_{1}$ 扫描的 6 个代表点

\(\varepsilon_{1}\) / h 最优 \(h\) PWL 收益 真实收益 偏差 疲劳 \(F\) 段号 MRS
2.00 2.00 33.37 33.60 -0.23 0.156 1 120.8
3.89 3.89 62.43 62.85 -0.42 0.546 2 56.9
5.16 5.16 80.64 80.95 -0.32 0.917 3 40.4
6.42 6.42 97.58 97.94 -0.36 1.408 3 30.4
8.32 8.32 120.85 121.32 -0.47 2.350 4 21.1
14.00 14.00 176.40 176.40 0.00 6.533 6 8.3

表 2 的偏差列是这次线性化的账单:折点处为 0,段中点最大 0.48 元,占满负荷收益的 0.27%。凹函数的弦总在曲线下方,所以折线是保守估计——用它做决策只会少估收益,不会把人骗去多跑。

4 结果分析

4.1 结构:折线把光滑前沿切成 7 段

图 1 真实收益曲线与 6 段折线的对照:折点处相等,段内折线永远压在曲线下方

图 1 里蓝虚线是模型真正"看见"的世界,红实线是小哥真实的世界,两者只在 7 个折点重合。6 段等宽 2.33 h,所以每段中点的偏差都是同一个数(0.48 元,图内标注),相对满负荷收益不到 0.27%,肉眼看不出差别——折线把"凹"这件事切成了 6 块相等的直线。第 1 段斜率 16.68 元/h,第 6 段只剩 8.52 元/h,落差就是"边际递减"被离散化的痕迹。关于题面的 $h\approx 9.2$ h:模型在该点给出 $R=131.4$ 元,等于理论上限 218.8 元的 60.1%。按"六成收益"口径,9.2 h 是成立的;但按本案例采用的"边际替代率等于平均替代率"口径,膝点在 7.00 h,与 9.2 h 差 2.2 h(24%)。偏差来源是膝点定义本身不唯一——它不是模型算出来的量,而是人选的判据,第 4.3 节说明为什么选这一条。

4.2 ε1 扫描:20 个点铺满整条前沿

图 2 20 个 ε 扫描解在 (h, R) 平面的散布,气泡大小是疲劳

20 个 $\varepsilon_{1}$ 从 2 h 均匀排到 14 h,解就跟着走到 20 个不同的 $h$。气泡大小是疲劳:$h$ 越大泡越大,收益增幅却越来越小,泡的性价比一路走低。从 2 h 到 14 h,收益从 33.4 元涨到 176.4 元(5.3 倍),疲劳却从 0.13 涨到 6.53(49 倍)——收益是线性量级,疲劳是平方量级,这就是冲突的量化形态。

图 3 三种方案:多 71.1 元收益要多付 4.90 疲劳

三方案柱把取舍摆到一张图上:半天 4 h 只有 64.0 元,膝点 7.00 h 有 105.3 元,满负荷 14 h 有 176.4 元。从膝点到满负荷,收益只多 71.1 元(67.4%),疲劳却多 4.90(300%)。这笔交易在任何"疲劳也值点钱"的人看来都不划算,这就是膝点存在的意义。

4.3 膝点与边际替代率

\[\mathrm{MRS}(h)=\frac{\mathrm{d}R/\mathrm{d}h}{\mathrm{d}F/\mathrm{d}h}=\frac{17.5-0.7h}{2h/30}=\frac{15(17.5-0.7h)}{h}\tag{5} \]

\[\mathrm{MRS}(h^{\ast})=\frac{R(14)-R(0)}{F(14)-F(0)}=\frac{176.40}{6.533}\ \Rightarrow\ h^{\ast}=7.00\tag{6} \]

式 6 的含义是:在膝点,再多跑 1 h 换来的收益/疲劳汇率,刚好等于从 0 跑到 14 h 的平均汇率。往前(\(h<7.00\))汇率高于平均,多跑划算;往后低于平均,多跑是赔本赚吆喝。实算 \(\mathrm{MRS}(7.00)=27.0\) 元/疲劳单位,与弦斜率 27.0 完全吻合,验证了式 6 求根的正确性。

图 4 边际收益递减:再跑 1 h 的进账从 17.1 元降到 8.1 元

图 4 从另一个角度说同一件事:再跑 1 h 的进账从 17.1 元一路降到 8.1 元,曲线下的面积才是累计收益——增量在退、总量还在进,边际线跌破膝点价位的那一段就是"跑太多"的区间。

4.4 主灵敏度:时间、疲劳与疲劳价格

图 5 ε1 扫描:收益沿折线爬升,边际替代率同步掉到 7.5 以下

图 5 是 $\varepsilon_{1}$ 的主灵敏度:左轴收益沿折线单调爬升($R$ 在 $[0,14]$ 上单调增),右轴 MRS 从 121 掉到 8.3。一升一降,两条线之间的落差就是"该不该继续跑"的判断依据。

图 6 ε1 与 ε2 的最优收益场:带星号的格点表示疲劳约束已经卡住

图 6 是 $\varepsilon_{1}\times\varepsilon_{2}$ 的最优收益场(5 乘 4 共 20 个求解点)。带星号的格点表示疲劳约束已经卡住,判据是 $\sqrt{30\varepsilon_{2}}<\varepsilon_{1}$:$\varepsilon_{2}=0.8$ 一行全是星号,收益被压到 64 元;$\varepsilon_{2}=6.53$ 一行没有星号,收益完全由时间上限决定。至于"收多紧才塌":膝点 $h=7.00$ 对应疲劳 1.63,所以 $\varepsilon_{2}\ge1.63$(满负荷疲劳的 25%)时膝点不受影响、前沿形状不变;收到 1.2(对应 $h\le6.00$ h)时膝点被砍,收益只剩 92 元,掉 12.7%;收到 0.8 就压到 77 元,塌掉 27%。结论是:**ε2 的临界值就是 $F(h^{\ast})$,把疲劳上限压到膝点疲劳以下,前沿才会真的塌。**

图 7 疲劳的影子价格越高,最优在线小时被压得越短

图 7 换成第三维:给疲劳标一个影子价格 $\pi$(元/疲劳单位),最优 $h$ 随 $\pi$ 单调下降,从 1 元时的 14.0 h 压到 60 元时的 3.7 h。这其实是加权和法的另一面——把疲劳折成钱时,$\pi$ 就是那个 $w$,取值完全靠主观,模型只能告诉你"取多少会得到什么"。

4.5 加权和采不到段内点,ε-约束采得到

加权和法把疲劳折成钱:\(\max R-wh\)。在 6 段折线上,它的解只可能出现在"支撑线斜率为 \(w\)"的地方,也就是折点。20 个权重从 7.52 扫到 17.68,覆盖了全部 6 段斜率的区间,结果只解出 7 个不同的 \(h\)(0.00, 2.33, 4.67, 7.00, 9.33, 11.67, 14.00)。

图 8 帕累托前沿:ε-约束铺满整段,加权和只能落在 7 个折点上

图 8 是主图。蓝实线是 ε-约束可达的前沿,橙圈标出加权和能落到的 7 个折点,蓝点是 ε 扫描的 20 个解——20 个全部落在段内部,加权和一个也拿不到。原因很直接:段内点只有当 $w$ 恰等于该段斜率时才最优,此时最优解不唯一,求解器按顶点返回,段内点被折叠回端点。ε-约束不经过权重,直接把 $h$ 钉在 $\varepsilon_{1}$ 上,段内点自然可达。更狠的一层:当前沿出现非凸段时,加权和不是"采不到段内点",而是"整段都采不到"。078、079 的门槛奖励台阶就是这种形状——收益在 50 单处跳一下,跳变前后的连线落在可行域外,任何 $w$ 都支撑不到它。ε-约束对前沿凸性没有任何要求,这也是它在本系列里反复被选作"画前沿"工具的原因。代价是它给不出一组权重、也给不出唯一解,它给的是前沿本身,选择权交回给人。

4.6 波动风险与各段账单

\[H=h^{\ast}(1+0.12\zeta),\quad R=17.5H-0.35H^{2},\quad \zeta\sim N(0,1)\tag{7} \]

图 9 膝点方案下收益的波动分布:八成线的触发概率 2.9%

前面所有结论都建立在"$h$ 完全可控"的假设上。实际上单量波动会让在线时长本身浮动,图 9 让 $h$ 按 12% 的变异系数波动后重算收益:均值 105.1 元,标准差 10.6 元,跌到膝点收益八成线(84.3 元)以下的概率是 2.9%。膝点方案的"稳"是稳在期望上,不是稳在每一天。

图 10 各折线段的收益增量与平均边际:第 1 段 38.9 元,第 6 段只剩 19.9 元

图 10 把 6 段各自的贡献拆开:第 1 段 38.9 元、第 6 段 19.9 元,降幅 49%;段平均边际从 16.7 掉到 8.5,第 3 段之后跌破 13 元/h,与膝点落在第 3 段末端(7.00 h 约等于 3 乘 2.33 h)互相印证。

5 结论

  1. 三目标不能同时最大,但能同时交代清楚。ε-约束把 (收益, 时间, 疲劳) 降成"max 收益,时间与疲劳进约束",20 次扫描给出 20 个互不支配的解,与解析式 \(h^{\ast}=\min\{\varepsilon_{1},\sqrt{30\varepsilon_{2}}\}\) 完全一致。

  2. 膝点在 7.00 h,收益 105.3 元。判据是边际替代率等于整段平均替代率,此时 \(\mathrm{MRS}=27.0\),与弦斜率 27.0 吻合;题面所说的 9.2 h 属于"收益达到理论上限六成"的另一条判据,两者差 2.2 h,差异来自判据而非模型误差。

  3. 加权和 20 次求解只得到 7 个解,ε-约束 20 次得到 20 个。PWL 折点把加权和的解空间折叠成 7 个顶点,20 个段内点全部丢失;前沿一旦出现非凸段(如门槛奖励),加权和连整段都采不到。

  4. ε2 的临界值是 \(F(h^{\ast})=1.63\),占满负荷疲劳的 25%;收到 1.2 以下前沿开始塌,收到 0.8 时 \(h\) 被压到 4.90 h,只剩 77 元。

  5. 线性化是保守的。6 段折线在段内始终低于真实曲线,最大偏差 0.48 元,占满负荷收益的 0.27%;换来的是 20 次求解 2 s 的求解速度。

参考文献

[1] Gurobi Optimization LLC. Gurobi Optimizer Reference Manual[Z]. 2024.

[2] Kellerer H, Pferschy U, Pisinger D. Knapsack Problems[M]. Berlin: Springer, 2004.

[3] Martello S, Toth P. Knapsack Problems: Algorithms and Computer Implementations[M]. Chichester: John Wiley & Sons, 1990.

[4] 司守奎, 孙玺菁. 数学建模算法与应用[M]. 3 版. 北京: 国防工业出版社, 2021.

posted @ 2026-11-11 18:05  小青蛙电气  阅读(2)  评论(0)    收藏  举报