案例 066 带一块备用电池
案例 066 带一块备用电池
摘要:电动车满电跑 \(40.8\) km,\(12\) 份箱容只够装 \(20\) 份货。备用电池能多跑 \(40.0\) km,代价是占掉 \(1\) 份箱容、多背 \(5\) kg(能耗升 \(6.0\)%),另付 \(0.40\) 元搬运折旧。把它写成 \(12\) 个接单变量加 \(1\) 个"带不带"变量的双约束 0-1 规划,目标 $ \max \sum_i w_i x_i - 0.40 b$;再把"不换电就得多绕 \(2.6\) km、多排 \(5.0\) min"折成 \(2.56\) 元补进目标。题给目标把绕行这一项漏了,模型自报 \(78.5\) 元、判"不带";把绕行折成钱补上,最优解翻成"带",净收益 \(77.60\) 元对 \(75.94\) 元,只多 \(1.66\) 元。 备电池值不值,取的是"省下的绕行 + 多接的单"与"那 \(1\) 份箱容的机会成本"之比:单量密度一高,\(1\) 份箱容就值到 \(5.80\) 元,结论立刻翻回"不带"。密度尺度降到 \(0.9\) 左右,带电池反而亏 \(2.34\) 元。
关键词:装备-容量联合选择;双约束背包;机会成本货币化;单量密度;影子价格
1 问题提出
1.1 问题是什么
午高峰有一批顺路单,\(12\) 笔候选装在 \(12\) 份的箱子里。每笔订单占 \(q_i\) 份、值 \(w_i\) 元、往返 \(d_i\) km,见表 1。满电续航 \(40.8\) km,\(6.0\)% 的负重折损先记在账上,于是不带电池只能跑 \(38.4\) km,带电池能跑 \(76.0\) km。
表 1 12 笔候选订单的箱容、收入与里程
| 订单 | 片区 | 份数 / 份 | 收入 / 元 | 里程 / km | 单位里程收入 /(元/km) |
|---|---|---|---|---|---|
| O01 | 岗顶 | 1 | 9.00 | 2.76 | 3.26 |
| O02 | 棠下 | 2 | 13.50 | 6.44 | 2.10 |
| O03 | 石牌 | 1 | 7.50 | 3.45 | 2.17 |
| O04 | 车陂 | 3 | 16.00 | 7.82 | 2.05 |
| O05 | 岗顶 | 1 | 6.50 | 2.30 | 2.83 |
| O06 | 棠下 | 2 | 11.50 | 8.28 | 1.39 |
| O07 | 车陂 | 1 | 9.80 | 7.48 | 1.31 |
| O08 | 石牌 | 2 | 12.00 | 4.83 | 2.48 |
| O09 | 岗顶 | 1 | 5.80 | 4.02 | 1.44 |
| O10 | 车陂 | 3 | 15.50 | 10.35 | 1.50 |
| O11 | 棠下 | 1 | 8.20 | 6.32 | 1.30 |
| O12 | 员村 | 2 | 11.00 | 9.20 | 1.20 |
三个数先摆平:备电池省一次换电,绕行 \(2.6\) km 加排队插拔 \(5.0\) min,按 \(0.60\) 元/km、\(0.20\) 元/min 折成 \(2.56\) 元;它自己又要占 \(1\) 份箱容、背 \(5\) kg、付 \(0.40\) 元。两项都折成元,才有得比。
1.2 为什么要优化
第一,两笔账不在一个量纲上。续航是 km,箱容是份,收入是元。只盯着"多跑 \(40.0\) km"会漏掉箱容那一头:\(20\) 份需求挤 \(12\) 份箱容,每份箱子都有人在排队,占掉一份就是少接一单。
第二,题给目标漏了一项。$ \max \sum_i w_i x_i - 0.40 b$ 只扣了电池的搬运折旧,没扣"不带就得绕路换电"的 \(2.56\) 元。按这个式子解,最优是"不带"(自报 \(78.5\) 元);补上绕行,最优变成"带"(\(77.60\) 元)。同一组数据,一项漏算,结论反了 \(1.66\) 元。 第三,结论随单量密度翻转,不是常数:单子密时 \(12\) 份箱子是硬瓶颈,\(1\) 份箱容的机会成本高到 \(5.80\) 元,那 \(2.56\) 元绕行省不回本;单子稀、里程长时反过来。给一个"永远该带"的答案,等于把密度藏起来了。
1.3 Gurobi 如何求解
\(12\) 个 \(x_i\) 加 \(1\) 个 \(b\),共 \(13\) 个 0-1 变量、\(2\) 条约束,分支定界在毫秒级收敛。全文调用 Gurobi \(94\) 次(三方案 + 三组扫描 + 逐单剔除),另有 \(600\) 次蒙特卡洛用向量化枚举、不调求解器,总耗时 \(0.39\) 秒。四步:
- 原式:只扣 \(0.40\) 元,得"不带",自报 \(78.5\) 元——留作反例。
- 完整式:目标里补 \(-2.56(1-b)\),等价于把 \(b\) 的系数从 \(-0.40\) 改成 \(+2.16\)。这一改,最优解从"不带"翻成"带"。
- 强制两解 + 扫描:\(b\) 锁 \(0\)、锁 \(1\) 各解一次对比净收益;再把里程尺度 \(k\)、折损率 \(\theta\)、箱容 \(Q\) 各扫一组,另做 \(2^{12} \times 2 = 8192\) 个组合的全枚举画前沿。
2 数学模型
2.1 符号说明
\(I\) 为候选订单集合(\(|I| = 12\));\(w_i\) 为订单 \(i\) 的收入(元);\(q_i\) 为占用箱容(份);\(d_i\) 为配送里程(km);\(Q = 12\) 为箱容(份);\(R_0 = 40.8\) 为单电续航(km);\(\Delta R = 40.0\) 为备电池增量(km);\(\theta = 0.06\) 为负重折损率;\(c_b = 0.40\) 为电池折算成本(元);\(c_s = 2.56\) 为换电绕行折算(元);\(b \in \{0,1\}\) 为带不带,\(x_i \in \{0,1\}\) 为接不接。
2.2 目标函数
第一项是接单收入,第二项是电池的搬运与折旧,第三项是不带的代价:每跑一轮总得换一次电,绕 \(2.6\) km、排 \(5.0\) min,折成 \(2.56\) 元。题给原式只有前两项,本文把第三项显式写出来,\(b\) 的净系数于是从 \(-0.40\) 变成 \(+2.16\)。
2.3 约束条件
箱容约束。\(b\) 出现在左边:备用电池自己就吃一份箱容,这是它唯一但致命的成本。
续航约束。\(\theta = 0.06\) 时,不带是 \(40.8 \times 0.94 = 38.4\) km,带是 \((40.8 + 40.0) \times 0.94 = 76.0\) km。折损同时压基础续航和备电增量——\(5\) kg 是背在车上一直背着的。
2.4 规模与性质
\(13\) 个 0-1 变量、\(2\) 条约束。单看是两约束背包,NP 难,但 \(2^{13} = 8192\) 就能穷举,所以本文所有前沿和分布都直接全枚举,不靠启发式。结构上有意思的是:\(b\) 在一头加资源(续航 \(+40.0\) km)、在另一头减资源(箱容 \(-1\) 份),符号相反,所以最优解一定落在"续航够不够"和"箱容满不满"两条边界之一上,结论自然随密度翻转。
3 Gurobi 求解实现
3.1 建模要点
x = m.addVars(N, vtype=GRB.BINARY)
b = m.addVar(vtype=GRB.BINARY, lb=lo, ub=hi) # 强制分支靠上下界
m.addConstr(gp.quicksum(Q[i] * x[i] for i in range(N)) + b <= CAP)
m.addConstr(gp.quicksum(d[i] * x[i] for i in range(N)) <= (R0 + RB * b) * (1 - TH))
两个实现细节:一是续航右端含 \(b\),不用大 M 线性化——\(b\) 是 \(0\)-\(1\),\((R_0 + \Delta R b)(1-\theta)\) 本身就是线性的,写成含 \(b\) 的右端比设两个分支干净得多;二是强制分支只改上下界(lb=ub=1),模型结构不变、重解在毫秒级。LP 松弛的影子价格正好把"紧在哪头"标出来:不带时箱容 \(1.20\) 元/份、续航 \(1.15\) 元/km;带上电池后续航松了(影子价归 \(0.00\)),箱容涨到 \(5.75\) 元/份。备电池不是买续航,是把紧约束从续航换成了箱容。整数解下这两个价会跳,只能看方向、不能当报价。
3.2 求解结果
表 2 三种口径下的最优方案(元)
| 口径 | 带电池 | 毛收入 | 电池折算 | 换电绕行 | 净收益 | 里程 / km | 份数 |
|---|---|---|---|---|---|---|---|
| 题给目标(漏绕行) | 否 | 78.5 | 0.00 | — | 75.94 | 37.94 | 12 |
| 强制不带 | 否 | 78.5 | 0.00 | 2.56 | 75.94 | 37.94 | 12 |
| 强制带 | 是 | 78.0 | 0.40 | 0.00 | 77.60 | 41.86 | 11 |
| 完整模型(自由选) | 是 | 78.0 | 0.40 | 0.00 | 77.60 | 41.86 | 11 |
"带"比"不带"多跑 \(3.92\) km、毛收入反而少 \(0.50\) 元——它挤掉了 \(O04\) 这类大份数单,换成两笔小份数远单。净收益却高 \(1.66\) 元,全靠省下的 \(2.56\) 元绕行:\(77.60 - 75.94 = 2.56 - 0.40 - 0.50\)。
4 结果分析
4.1 结构:箱容和续航两头都紧

图 1 12 笔订单的箱容占用(棒)与配送里程(折线)
棒高是份数,折线是里程。份数大的单(\(O04\):\(3\) 份 \(16.00\) 元)不一定里程长,里程长的单(\(O10\):\(10.35\) km)份数也不小——两根轴不重合,才有了"挤掉一笔大单换两笔小单"的空间。蓝棒是带电池方案选中的 \(8\) 笔,灰的落选。

图 2 订单的收入—里程分布(球径 = 份数,斜虚线 = 等性价比线)
两道斜虚线是 \(1.5\)、\(2.5\) 元/km 的等性价比线。入选的点既不在最高那条线上,也不在最右边:\(O10\) 值 \(15.50\) 元、\(10.35\) km,落在 \(1.5\) 元/km 线上却落选,因为它占 \(3\) 份箱容;\(O01\) 只有 \(9.00\) 元却入选,因为只占 \(1\) 份。单价、里程、份数三个轴,选单时一个都不能少。
4.2 结果:一项漏算,结论反了

图 3 带与不带的净收益与两项成本(黑横线 = 毛收入)
黑横线标毛收入,菱形标净收益,中间那段就是成本。不带的毛收入 \(78.5\) 元更胖,被 \(2.56\) 元绕行砍掉一截,落到 \(75.94\) 元;带的毛收入 \(78.0\) 元瘦一点,只扣 \(0.40\) 元,落到 \(77.60\) 元。毛收入的高低和净收益的高低正好相反——这就是"两项都折成元再比较"的全部意义。

图 4 按单位里程收入降序累计的收入与里程
按性价比从高到低累加,累计里程爬到 \(38.4\) km 时只装得下 \(7\) 单,再往上就必须换电。紫色点线是 \(76.0\) km,它把可行域整整推后 \(37.6\) km,正好够吃下 \(O06\)、\(O07\) 这两笔。
4.3 灵敏度:密度、折损、增量

图 5 单量密度对净收益差的影响(阶梯 = 不带方案的毛收入)
横轴是里程尺度 \(k\),\(k\) 越小表示同样的单跑得越近、单量越密。\(k = 0.80\) 时"带"比"不带"少 \(3.64\) 元,\(k = 1.10\) 时反过来多 \(7.46\) 元。翻转点落在 \(k \approx 0.95\),本案 \(k = 1\) 站在"带"的一侧但只剩 \(1.66\) 元余量。机制在阶梯线上:\(k = 0.80\) 时不带比带多挣 \(5.80\) 元,那 \(1\) 份箱容就值 \(5.80\) 元,压过 \(2.56\) 元的绕行;\(k = 1.10\) 时这个差变成 \(-5.30\) 元,符号反了。

图 6 里程尺度与负重折损率的二维灵敏度
绿区是"带更好",红区是"不带更好"。折损率越高、里程越长,越该带:\(\theta\) 从 \(0\)% 涨到 \(12\)%,带电池的续航从 \(80.8\) km 掉到 \(71.1\) km,可不带掉得更快(\(40.8\) 降到 \(35.9\) km),差距被拉大。本案(星标)离红区只有一格,\(5\) kg 的折损率报高了,结论就会翻。

图 7 箱容从 9 份加到 14 份时净收益差的变化
箱容从 \(9.0\) 份加到 \(14.0\) 份,差额从 \(-4.04\) 元一路升到 \(11.16\) 元。\(11.0\) 份时"带"还亏 \(0.54\) 元,\(12.0\) 份就翻正到 \(1.66\) 元——本案的 $12 份正好压在翻转线上,结论对箱容差一份极其敏感。青色阶梯是带方案毛收入,跟着箱容单调抬升,说明多出来的箱容全被喂给了远单。
4.4 前沿、风险与边际贡献

图 8 8192 个 (方案, 里程) 组合的净收益前沿与不可行区
灰点是"不带"的可行方案,粉点是"带"的,右侧红区两方案都不可达。前沿是阶梯——里程多一点、收益跳一格。最优不带(方块,\(37.94\) km)与最优带(星,\(41.86\) km)都在前沿上,但不在同一段:星标在更右边,靠 \(2.16\) 元的净补贴把整条前沿抬起来。

图 9 单价在 ±15% 内扰动时「带 − 不带」差额的分布与累计概率
抽 \(600\) 组单价,带更好的概率 \(91.7\)%,平均差额 \(2.46\) 元。左尾到 \(-1.47\) 元:几笔大份数单同时涨价,箱容立刻变贵,"带"就翻车。\(8.3\)% 的翻车率不高不低——\(1.66\) 元这个结论能用,但得按周复核。

图 10 逐单边际贡献(强制剔除该单后净收益的下降)
贡献最大的是 \(O01\)(\(3.20\) 元),最小的是 \(O12\)(\(0.00\) 元)。排序按"单位箱容收入"走,不按金额:\(O01\) 只值 \(9.00\) 元却排第一,因为它只占 \(1\) 份;\(O04\) 值 \(16.00\) 元却排中游,\(3\) 份的块头摊薄了它。灰条里有 \(4\) 笔边际贡献恰好为 \(0\)——本来就没进最优解,剔掉也不影响。
5 结论
-
备电池的账要两头记:省下 \(2.56\) 元换电绕行、毛收入少 \(0.50\) 元,付出 \(0.40\) 元折旧加 \(1\) 份箱容。本案净赚 \(1.66\) 元——薄,但不是零。
-
题给目标漏了绕行项,结论会反。只扣 \(0.40\) 元时最优是"不带"(自报 \(78.5\) 元);补上 \(2.56\) 元绕行,最优翻成"带"(\(77.60\) 元)。同一组数据,两项之差 \(= 2.16\) 元,正好跨过毛收入的 \(0.50\) 元缺口。
-
结论随单量密度翻转:里程尺度 \(k\) 从 \(0.80\) 到 \(1.10\),差额从 \(-3.64\) 元走到 \(7.46\) 元,翻转点在 \(k \approx 0.95\)。单子越密,\(1\) 份箱容越贵,备电池越不该带。
-
影子价格一句话说清机制:不带时紧的是续航(\(1.15\) 元/km),带电池后续航影子价归 \(0.00\)、箱容涨到 \(5.75\) 元/份。备电池卖的不是续航,是"把续航约束买松",代价是箱容变紧。
-
稳健性 \(91.7\)%:单价 ±15% 扰动下"带更好"的概率就这么多,左尾 \(-1.47\) 元。\(1.66\) 元的优势薄到能被几笔涨价抹平,实务上按周重算,别当固定策略。
参考文献
[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.

浙公网安备 33010602011771号