案例 070 不能低于 15%

案例 070 不能低于 15%

摘要:平台说「电量不低于 15%」,这句话在数学上不是一条约束,而是一族约束——因为剩余多少电取决于跑了多少公里,而公里数本身是随机的。本案把 10 单装进一个机会约束背包:里程消耗 \(d\sim N(\mu_d,0.15\mu_d)\),要求 \(P(\text{电量}<15\%)\le 5\%\)。把随机项移到一侧合并同类项,得到一条「保守里程预算」:\(48\times0.85/1.2468=32.7\) km。四方案对比:按 100% 规划能接 8 单、92.5 元,但班次结束时电量期望只有 2.1%,破线概率 81%;按 85% 硬约束接 7 单、80.5 元,破线概率仍有 47%;机会约束接 6 单、67.0 元,破线概率降到 4.2%;而中途换一次电,10 单全接、净收入 109.5 元,破线概率 2.0%。底线不是不能碰,是碰了要付概率的钱:15% 的底线真实价值是 2 单、25.5 元;它的临界点在 7.8%,过了这条线开始掉单。

关键词:机会约束;背包问题;电量底线;安全裕度;正态近似;灵敏度分析


1 问题提出

1.1 问题是什么

午高峰系统推来 10 单,期望总里程 60.1 km,毛收入 111.0 元。车的满电续航 48 km,平台要求电量始终不低于 15%。粗看这道题很简单:40.8 km 的可用里程,装单装到装不下为止——按 85% 硬约束能接 7 单、80.5 元(40.4 km)。

但这 7 单是「名义上」安全的。真实里程不会正好等于期望值:每一单都可能有绕路、等电梯、找楼栋,实测的里程消耗相对误差约 15%。跑到 40.4 km 时,实际消耗按 \(N(40.4, 6.1^{2})\) 分布,超过 40.8 km 的概率是 47%——三分之一的班次会踩穿 15% 的底线。

1.2 为什么「留 15%」不等于「安全」

第一,底线是概率事件,不是余量事件。留 15% 只是把期望值的天花板压低 15%,波动还在原地;波动幅度正比于跑得多远(\(\sigma=0.15\mu_d\)),跑得越多,尾巴越长。

第二,按 100% 规划等于把底线当没有。装到 47.0 km、8 单、92.5 元,账面还剩 2.1%,看起来只差 4 个百分点,实际破线概率高达 81%——这不是风险,这是必然。

第三,换电是唯一能把「单量」和「裕度」同时买回来的手段。一次换电把电量复位到 100%,可用里程从 32.7 km 变成两段 32.7 km,代价是 1.5 元固定成本和 3 min 停留。

1.3 Gurobi 如何求解

模型本身是一个 10 变量的 0-1 背包:目标最大化收入,约束是里程不超过保守预算。麻烦不在求解,在把概率约束变成线性约束——正态假设下 \(P(\xi\le b)\ge1-\alpha\) 等价于 \(\mu+\Phi^{-1}(1-\alpha)\sigma\le b\),代进电量不等式、把 \(\sum\mu_ix_i\) 当同类项合并,就得到一条普通的线性约束(式 4)。换电方案是两段背包,10 单分进两个各 32.7 km 的箱子,同一单只能进一个。全部 4 个方案加 7 点底线扫描、28 点热图、4 点置信扫描,合计求解墙钟 0.0 s 的模型时间,脚本总耗时 1.5 s。

2 数学模型

2.1 符号说明

\(I\) 为订单集合(\(|I|=10\)),\(x_i\in\{0,1\}\) 表示接不接第 \(i\) 单;\(\mu_i\) 为订单 \(i\) 的期望里程(km),\(d_i\) 为实际里程,\(d_i\sim N(\mu_i,0.15\mu_i)\);\(w_i\) 为单笔收入;\(R\) 为满电续航;\(E_0\) 为出发电量(100%);\(\underline{e}\) 为电量底线(15%);\(\alpha\) 为允许的破线概率(5%);\(\beta=1-\alpha\) 为置信水平;\(\gamma\) 为波动系数(0.15)。

2.2 目标函数

\[\max\ Z=\sum_{i\in I}w_{i}x_{i}\tag{1} \]

目标是毛收入,不扣里程成本——本案的取舍全在「能不能接」上,不在「接哪个更赚」上。这样做的另一个好处是:\(Z\) 就是可接单量的加权形式,单量与收入同向,图 3 的两组柱子不会打架。

2.3 电量递推与底线

\[E_{\text{end}}=E_0-\frac{100}{R}\sum_{i\in I}d_{i}x_{i}\ge \underline{e}\cdot E_0\tag{2} \]

式 (2) 是确定版本:把 \(d_i\) 当成已知数代进去,就退化成「总里程不超过 40.8 km」这一条硬约束。方案 B 就是它。

2.4 机会约束

\[P\left(E_0-\frac{100}{R}\sum_{i\in I}d_{i}x_{i}\ge \underline{e}E_0\right)\ge \beta\tag{3} \]

注意这里 \(d_i\) 是随机变量,\(x_i\) 是决策变量,约束里出现「决策 × 随机」——这是机会约束的典型形态。

2.5 线性化:保守里程预算

令 \(S=\sum_i d_ix_i\)。\(d_i\) 独立正态,故 \(S\sim N(\sum_i\mu_ix_i,\ \gamma^2\sum_i\mu_i^2x_i^2)\)。把式 (3) 括号里的不等式整理为 \(S\le R(1-\underline{e})\),再用正态分位数:

\[\sum_{i}\mu_{i}x_{i}+z_{\beta}\,\gamma\sqrt{\sum_{i}\mu_{i}^{2}x_{i}}\le R(1-\underline{e})\tag{4} \]

式 (4) 里有根号,不线性。本案采用工程上通用的保守替代:用 \(\gamma\sum_i\mu_ix_i\) 代替 \(\gamma\sqrt{\sum\mu_i^2x_i}\)(因为 \(\sqrt{\sum a_i^2}\le\sum a_i\)),于是

\[(1+z_{\beta}\gamma)\sum_{i\in I}\mu_{i}x_{i}\le R(1-\underline{e})\quad\Longleftrightarrow\quad \sum_{i\in I}\mu_{i}x_{i}\le \frac{R(1-\underline{e})}{1+z_{\beta}\gamma}\tag{5} \]

代入 \(R=48\)、\(\underline{e}=0.15\)、\(z_{0.95}=1.645\)、\(\gamma=0.15\):

\[B=\frac{48\times0.85}{1+1.645\times0.15}=\frac{40.8}{1.2468}=32.7\ \text{km}\tag{6} \]

这条式子只需一次乘除,代价是「保守」:\(\sqrt{\sum\mu_i^2x_i}\le\sum\mu_ix_i\) 的等号只在单一订单时成立,10 单同跑时右边比左边大 67% 左右,等于把置信水平从 95% 悄悄抬到了 99% 以上。本案在 4.3 节用数值验证了这一点:实际破线概率 4.2%,远低于允许的 5%。

2.6 换电方案:两段背包

\[\max\ Z=\sum_{i}w_{i}(x_{i1}+x_{i2})-c_{s},\qquad x_{i1}+x_{i2}\le1,\qquad \sum_{i}\mu_{i}x_{ik}\le B\ \ (k=1,2)\tag{7} \]

\(x_{ik}=1\) 表示第 \(i\) 单放在第 \(k\) 段。两段各有一条式 (6) 的预算,中间换一次电把电量复位,\(c_s\) 为换电固定成本。这里有个隐含假设:换电点不产生额外绕行,若绕行 \(\delta\) km,则只需把第二段的预算改成 \(B-\delta\)。

3 Gurobi 求解实现

3.1 数据

表 1 十单的基础数据与单位里程收入(全表见 data_070.csv)

订单 区域 期望里程 \(\mu_i\) /km 收入 \(w_i\) /元 份数 \(q_i\) 单位里程收入 /(元/km)
O01 天河 3.4 9.5 2 2.79
O02 越秀 4.2 11.0 3 2.62
O03 海珠 5.1 8.5 2 1.67
O04 海珠 6.3 13.0 4 2.06
O05 番禺 7.4 12.5 3 1.69
O06 番禺 8.0 10.0 2 1.25
O07 天河 9.2 14.0 4 1.52
O08 越秀 4.5 9.0 2 2.00
O09 海珠 6.6 12.0 3 1.82
O10 天河 5.4 11.5 3 2.13

总期望里程 60.1 km,是满电续航的 125%,也是保守预算 32.7 km 的 1.8 倍——一管电装不下,这是本案所有取舍的根源。

3.2 代码要点

模型只有十来行:

x = mod.addVars(N, m, vtype=GRB.BINARY)
mod.setObjective(sum(W[i]*x[i,k] for i in range(N) for k in range(m)), GRB.MAXIMIZE)
for i in range(N):
    mod.addConstr(sum(x[i,k] for k in range(m)) <= 1)      # 一单只进一段
for k in range(m):
    mod.addConstr(sum(MU[i]*x[i,k] for i in range(N)) <= B) # 式 (5)

真正花时间的是三件外围的事。第一,预算是随底线和置信水平变的,所以 budget(floor, alpha, cv) 要写成一个函数,扫描时反复调用;第二,换电方案用 \(m=2\) 的二维变量,一单只能进一段,段间由换电断开,模型不用管顺序——因为收入与顺序无关,只有里程预算有关;第三,破线概率要用解析式算,不能只报个「满足约束」,否则读者不知道到底留了多少裕度。

3.3 规模与耗时

主模型 10 个 0-1 变量、1 条约束,换电模型 20 个 0-1 变量、12 条约束。四个方案合计求解 16 ms,单次扫描点均在 5 ms 内返回 OPTIMAL;全部 43 次求解加上十张图,脚本总耗时 1.5 s。这类题的求解器从来不是瓶颈,建模口径才是:预算算错一个分母,单量就会差两单。

4 结果分析

4.1 三条预算线

图 1 三条里程预算线:满电 48 km、账面 85% 的 40.8 km、机会约束的 32.7 km;阶梯是逐单累积里程,只有蓝线以内的单装得进保守预算

图 1 把 10 单按单位里程收入降序排开、逐单累加里程,三条横线是三种口径下的预算。满电 48 km 能盖住前 8 单;账面 85% 的 40.8 km 盖住前 7 单;机会约束的 32.7 km 只盖到第 6 单。每往下一格,掉的都是收入最高的那批长单——因为排序按密度,末尾被砍的恰恰是「远而便宜」的单。

图 2 十单的收入—里程全景,气泡为份数;深色是机会约束方案选中的 6 单,虚线是等收入密度线

图 2 是原始数据的面貌:气泡大小是份数,深色为机会约束入选。等收入密度线的斜率在 2.0 元/km 附近,落在线下的 O06(8.0 km / 10.0 元)、O07(9.2 km / 14.0 元)是最先被砍的;O07 收入最高(14.0 元)却进不了保守预算,因为它一个人要吃掉 28% 的预算。

4.2 四方案对比

图 3 四方案对比:单量、净收入与破线概率同时给出,按 100% 规划多接两单却把破线概率推到 81%

表 2 四方案的量、钱与风险

方案 口径 单量 里程 /km 毛收入 /元 净收入 /元 末端电量 /% 破线概率
A 按 100% 规划 8 47.0 92.5 92.5 2.1 81%
B 85% 硬约束 7 40.4 80.5 80.5 15.8 47%
C 85% 机会约束 6 32.4 67.0 67.0 32.5 4.2%
D 85% + 提前换电 10 60.1 111.0 109.5 35.6 / 39.2 2.0%

表 2 是本案的全部结论。方案 A 最贪:8 单、92.5 元,账面末端还剩 2.1%,看着只比底线高 12.9 个百分点的缺口,但破线概率 81%——这不是「有点险」,是六成班次要推车。方案 B 补上了账面余量,破线概率仍有 47%,因为波动没被计价。方案 C 才是真正守住 95% 承诺的解:6 单、67.0 元,末端 32.5%,破线 4.2%。方案 D 用一次换电把 10 单全接回来,净收入 109.5 元,比 C 多 42.5 元,破线概率 2.0%。

于是「15% 底线值多少钱」有了明确答案:25.5 元,两单。而这两单可以用 1.5 元的换电成本买回来——换电的性价比是 3.5%,几乎没有理由不换。

4.3 电量轨迹:在哪里撞线

图 4 电量轨迹:方案 A 在第 47.0 km 处撞穿 15% 底线,方案 C 全程留 18 个百分点的裕度,方案 D 靠中途换电复位

图 4 是四条轨迹。A 的斜率最平缓(每公里耗 2.08% 电),一路下探到 2.1%,在 47.0 km 处穿过 15% 线;B 在 40.4 km 处收手,末端 15.8%,离开底线还有 0.8 个百分点,但这 0.8 个百分点不够覆盖一次 15% 的波动(\(1.645\times6.1=10.0\) km,折 20.8 个百分点的电量)。C 的末端 32.5%,裕度 17.5 个百分点,正好把 95% 分位兜住。D 是唯一一条「回头上扬」的曲线:在第一段末 30.9 km 处换电复位到 100%。

4.4 底线比例的主灵敏度

图 5 底线比例从 5% 提到 35%:可接单量在两处台阶跌落(7.8% 与 24.9%),可用里程被压到 25.0 km

表 3 底线比例扫描(95% 置信、波动 15%)

底线 \(\underline{e}\) 保守预算 /km 最优单量 最优收入 /元 相对 15% 方案
5% 36.6 7 75.0 +8.0
10% 34.7 6 69.5 +2.5
15% 32.7 6 67.0 +0.0
20% 30.8 6 66.0 -1.0
25% 28.9 5 59.0 -8.0
30% 27.0 5 57.5 -9.5
35% 25.0 5 54.5 -12.5

图 5 的曲线不是斜线而是台阶,因为单量是整数。两处跌落很清楚:底线过 7.8% 掉到 6 单,过 24.9% 掉到 5 单。临界值的解析形式是 \(\underline{e}_k=1-(1+z_\beta\gamma)\,B_k/R\),\(B_k\) 是装下 \(k\) 单所需的最短里程(取最短的 \(k\) 单):\(B_7=35.5\)、\(B_6=28.9\),代进去正好是 7.8% 与 24.9%。

结论比直觉更悲观:15% 这条线本身就在陡坡上。系统如果把底线从 15% 提到 20%,收入从 67.0 掉到 66.0 元(-1%),单量却只掉 0 单——因为 20% 仍在 6 单的平台区内;真正掉单发生在 24.9%,一过就是 12.5 元。反之,底线降到 10% 只多 1 单,降到 5% 才多 1 单。底线在 8%–25% 之间是「免费的」,出了这个区间每一步都贵。

4.5 二维灵敏度:底线 × 波动

图 6 底线比例 × 里程波动系数的二维热图:波动从 5% 抬到 20%,同样的 15% 底线从 7 单掉到 6 单

图 6 把波动系数也放进来。热图对角方向是「等效」的:底线每升高 10 个百分点,约等于波动从 5% 涨到 15% 的惩罚(两者都通过 \(1+z_\beta\gamma\) 这个乘子与 \(1-\underline{e}\) 对抗)。注意本案参数(星号)不在等高线的边缘:往左下走还有 2 单的空间,往右上走一步就掉到 6 单。对骑手来说,这意味着「雨天(波动大)+ 平台提底线」的双重挤压不是线性叠加,而是直接跳台阶。

4.6 第三维:置信水平

图 7 第三维:置信水平从 90% 抬到 99%,保守预算从 34.2 km 收到 30.2 km,单量 6→6

图 7 固定底线 15%、波动 15%,只动置信水平。90% 时预算 34.2 km、6 单;95% 时 32.7 km、6 单;97.5% 与 99% 都是 30.2 km、6 单。置信水平的边际成本同样是台阶:从 90% 到 95% 要付 1 单,从 95% 到 99% 反而只多 0 单——因为分位数 \(z_\beta\) 的增长(1.282→2.326)被 32.7 km 的平台宽度吃掉了。这解释了为什么业界的「95% 准时率」不是随便挑的:它是「再往上加钱也换不到安全」的拐点。

4.7 帕累托前沿与不可行边界

图 8 收入—里程帕累托前沿与不可行阴影:预算线右侧一律不可行,前沿在 32.7 km 处给出 67.0 元的拐点

图 8 是 1024 个子集全部枚举后的帕累托前沿:横轴里程、纵轴毛收入,红色阴影是超过 32.7 km 的不可行区。前沿在 32.4 km 处给出 67.0 元,再往右每 0.1 km 大约换 0.3 元收入,直到 47.0 km 的 92.5 元——前沿平坦说明「多跑」的边际收益在递减,而预算线正好卡在陡转点的左边。三个方块是 A、B、C 的位置,A 越过了预算线 14.3 km,B 越过 7.7 km,只有 C 落在可行域内。

4.8 末端电量的分布

图 9 末端电量的累积分布:方案 A 有 81% 的班次跌破 15%,方案 C 只有 4.2%

图 9 把三个方案的末端电量画成累积分布。读法是:曲线在 15% 处的纵坐标就是破线概率。A 的曲线在 15% 处是 81%,B 是 47%,C 是 4.2%。关键在曲线的形状:A 的分布几乎整体落在 15% 左边,说明它不是「偶尔踩线」,而是「常态踩线」;B 的均值 15.8% 虽然在线右边,但标准差 12.6 个百分点,尾巴仍然越过底线;C 的均值 32.5%、标准差 10.1,\(15\%\) 落在 1.73 个标准差之外。

4.9 分区域结构

图 10 分区域收入堆叠:机会约束把总量压掉 42.5 元,天河、越秀的短单结构基本没动,被砍的是番禺两单

图 10 把收入按区域堆叠。方案 A 与 C 的差别集中在番禺:A 拿了两单番禺(7.4 与 8.0 km),C 一单也没拿——番禺单是「远而贵」的典型,正好卡在预算边缘,它们的单位里程收入 1.69 与 1.25 元/km 只比入选门槛低一点点。天河、越秀的短单在四个方案里几乎不动,稳定性最好。若骑手只能跑半天,优先接天河、越秀,这是数据的直接结论。

5 结论

第一,15% 的底线不是余量,是置信水平。「留 15%」在确定模型里只是一条减法,在随机模型里它变成了乘子 \(1/(1+z_\beta\gamma)=0.80\):可用里程从 40.8 km 缩到 32.7 km,等于再多留 20% 的余量。这就是为什么按 85% 硬约束接单的骑手,仍然有约三成班次会踩线。

第二,底线的价格是 2 单、25.5 元,但可以用换电买回来。本案里换电成本 1.5 元换回 42.5 元收入,性价比 3.5%;只要换电绕行不超过 \(\delta\),第二段预算就从 32.7 km 减 \(\delta\),绕行 2 km 以内仍然划算。「提前换电」不是保守,是把风险预算重新分配。

第三,底线比例的临界点在 7.8% 与 24.9%。 低于 7.8% 时提高底线不花钱(8 单掉到 7 单发生在 7.8% 以下,本案区间内已过),7.8%–24.9% 之间是 6 单的平台区,一过 24.9% 立刻掉到 5 单、少 12.5 元。平台若要把底线从 15% 提到 25%,必须同时放松时长或补贴换电,否则骑手的可接单量直接砍掉三分之一。

第四,建模的教训是「保守替代要报误差」。 式 (5) 用 \(\sum\mu_ix_i\) 代替 \(\sqrt{\sum\mu_i^2x_i}\),本案实际破线 4.2%,名义 5%——保守了 0.8 个百分点。想精确,就把式 (4) 的根号用分段线性或二阶锥写进去,规模仍是 10 个 0-1 变量,Gurobi 一样秒解。


参考文献

[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.


复现方式:python batch_070.py;数据与结果见同目录 .csv / .json,图见 figs/

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