案例093 罚款不是一条直线

案例093 罚款不是一条直线

摘要:8 单、一台车、三档阶梯罚金:超时 0—5 min 扣 1.5 元,5—15 min 扣 3.5 元,超过 15 min 扣 8 元并降级(折算 60 元)。用顺序变量 \(y_{ij}\)、完成时刻 \(C_i\) 与三组 0-1 变量 \(u_{i,1},u_{i,2},u_{i,3}\) 建模,罚金按累积差值 1.5/+2/+4.5 叠加,降级用指示约束 \(\sum_i u_{i,3}\ge 1\Rightarrow z=1\) 一次性计 60 元。Gurobi 求得最优顺序 O1 → O7 → O6 → O3 → O8 → O2 → O4 → O5,总罚金 79.5 元(逐单 19.5 + 降级 60),里程 40.00 km。四组反面实验给出具体数字:直接按 1.5/3.5/8 相加总额 91.0 元(多算 11.5);漏掉 \(u_3\le u_2\le u_1\) 得 75.5 元(虚低 4.0);门控误写成 60 min 得 10.5 元;降级按单计得 139.5 元。灵敏度显示一档阈值 1→10 min 时罚款 79.5→74.0 元,二档阈值 12→30 min 时从 76.5 元断崖跌到 15.5 元,目标函数非凸、LP 松弛弱。

关键词:三档阶梯罚金;累积差值;指示约束;降级成本;非凸目标;VRPTW

1 问题提出

8 单、一台车、一条城区路网。每单有一句承诺,晚到不是"每分钟多少钱",而是跳档:超时 0—5 min 扣 1.5 元,5—15 min 扣 3.5 元,超过 15 min 扣 8.0 元并降级——降级不是罚金,是失去接单资格,平台折算 60 元。三档之外还有一个隐藏条款:超时超过 60 min 的单直接判为配送失败。

罚金的形状决定了这个案例的全部难点。它不是一条直线,是一张三级台阶;台阶不能用一个连续变量乘斜率写出来,只能用 0-1 变量叠加。叠加的方式有两种:把 1.5、3.5、8 三个数直接相加,或者把 1.5、2、4.5 三段累积差值相加。两种写法在只超一档时都是 1.5 元,从二档起就分叉:二档单被算成 1.5+3.5=5 元,三档单被算成 13 元——本案 2 单落进第三档,直接相加会把总额从 79.5 元算成 91.0 元。更隐蔽的错误是漏掉档位之间的单调关系 \(u_{3}\le u_{2}\le u_{1}\):漏掉之后,模型会让一单"只判三档、免掉一档",用 4.5 元买断一次 8 元的处罚,总额虚低到 75.5 元。

本案例要求把这两种错法都跑出来,给出具体数字,而不是只说"要注意"。

2 数学模型

设客户集合 \(\mathcal{N}=\{1,\dots,8\}\),\(0\) 为站点。\(t_{ij}\) 为行程时间(min),\(s=4\) 为单点服务时间,\(D_i\) 为承诺时刻,\(\ell_i=C_i-D_i\) 为超时分钟数。

顺序与时间。 \(y_{ij}\in\{0,1\}\) 表示 \(i\) 紧接 \(j\),\(C_i\) 为完成时刻:

\[\sum_{i\in\{0\}\cup\mathcal{N},i\ne j}y_{ij}=1,\qquad \sum_{j\in\mathcal{N},j\ne i}y_{ij}\le 1,\qquad \forall i,j\tag{1} \]

\[C_j\ \ge\ C_i+s+t_{ij}-\bar M(1-y_{ij}),\qquad C_0=0,\qquad \forall i\in\{0\}\cup\mathcal{N},\ j\in\mathcal{N}\tag{2} \]

式 (2) 只给下界,沿被选中的弧时间严格递增,因此不会出现子回路,也不需要额外的子回路消除约束。

三档罚金。 \(u_{i,1},u_{i,2},u_{i,3}\in\{0,1\}\) 分别表示"吃到一档 / 二档 / 三档"。门控的写法是"下一档没买,就封在上一档的边界上":

\[\ell_i\ \le\ M u_{i,1}\tag{3} \]

\[\ell_i\ \le\ 5+M(u_{i,2}+u_{i,3}),\qquad \ell_i\ \le\ 15+M u_{i,3},\qquad \ell_i\le 60\tag{4} \]

\[u_{i,3}\ \le\ u_{i,2}\ \le\ u_{i,1},\qquad \forall i\in\mathcal{N}\tag{5} \]

降级是一次性成本。 只要有任意一单进了第三档,降级就发生一次,付 60 元;不是每单都付。引入 \(z\in\{0,1\}\):

\[u_{i,3}\ \le\ z,\quad \forall i;\qquad z\ \le\ \sum_{i\in\mathcal{N}}u_{i,3}\tag{6} \]

目标。 用累积差值 \(1.5,\ 2,\ 4.5\),而不是 1.5/3.5/8:

\[\min\ Z=1.5\sum_i u_{i,1}+2\sum_i u_{i,2}+4.5\sum_i u_{i,3}+60z\tag{7} \]

三档都吃满时 \(1.5+2+4.5=8\),正好是合同上的第三档罚金;直接写 \(1.5+3.5+8=13\) 就把同一笔钱收了两遍。式 (5) 是这套写法的逻辑骨架:\(u_{3}=1\) 必须带着 \(u_{1}=u_{2}=1\) 一起出现,否则 4.5 元就能买到 8 元的额度。

3 Gurobi 求解实现

模型有 72 个顺序变量、8 个完成时刻、25 个 0-1 变量,Gurobi 在 3.42 s、12378 个分支节点内求到最优。目标后加 \(0.001\times\) 里程做字典序近似:最多 0.05 元,改不动 1.5 元的档差,只在等罚方案里挑一条短路,保证报告里的顺序可复现。

大 M。 任意路径的完成时刻不超过最长的 8 段行程加 8 次服务,得 \(\bar C=171.5\) min;式 (3)(4) 取 \(M=\lceil\bar C\rceil=172\) 即绝对安全。本案实际最大超时只有 55.0 min,\(M\) 取 80 已足够,取 172 只是不想让读者去猜。

表 1 四种写法的对照(同一算例)

写法 单档系数 总额 / 元 偏差
累积差值(正确) 1.5 / +2 / +4.5 79.5 —
直接相加 1.5 / 3.5 / 8 91.0 +11.5
漏 u₃≤u₂≤u₁ 1.5 / +2 / +4.5 75.5 -4.0
门控误写 60 1.5 / +2 / +4.5 10.5 -69.0
降级按单计 1.5 / +2 / +4.5 139.5 +60.0

表 1 的第四行是把第三档门控写成 \(\ell\le 60+M u_3\) 的结果。60 是降级上限、不是档位边界;以它为门控,\(u_3\) 永远不会被触发,所有 15—60 min 的超时都按 3.5 元收,总额 10.5 元、降级 0 单。第五行把 60 元按单收(\(60\sum_i u_{i,3}\)),本案 2 单降级就要付 120 元,总额 139.5 元,比正确值高 60.0 元——存在性成本只能乘一次。

4 结果分析

4.1 阶梯与落点

图 1 三档罚金阶梯(实线)与档位累计值 1.5/3.5/8(棒棒糖),箭头为档间增量 +2、+4.5

图 1 是本案的"定价表"。曲线在 0、5、15 min 三处跳,跳幅分别是 1.5、2、4.5;三个累计值 1.5、3.5、8 是棒棒糖的高度,增量才是系数。这张图解释了为什么目标里的系数是 1.5/2/4.5:它是台阶的"层高",不是台阶的"海拔"。把海拔当系数相加,第三档就变成 13 元。

图 2 8 单在超时—罚金平面上的落点(气泡面积为完成时刻)

图 2 把最优解逐单放进这张定价表。3 单计罚且全部越过 5 min,其中、2 单越过 15 min,最长超时 55.0 min。落点分两簇:O4 超时 55.0 min,深陷第三档,8 元之外还拖出一次 60 元降级;另一簇只有一单停在 5—15 min 之间( 11.0 min),属于"再慢一点就跳档"的临界单。优化的着力点全在这些临界单身上:把 15.4 min 压到 14.6 min 能省 4.5 元(若它同时是唯一的降级单,还能连带省掉 60 元),把 12 min 压到 11 min 一分不省。

图 3 三种写法的罚款总额对比

图 3 是三种写法的总额。正确写法 79.5 元;直接相加 91.0 元,多算 11.5 元——两个三档单各被算成 13 元(而非 8 元),一个二档单被算成 5 元(而非 3.5 元);漏掉 \(u_3\le u_2\le u_1\) 得到 75.5 元,虚低 4.0 元,假解里 O4 超时 55.0 min,却只记 \(u_3=1\)、\(u_1=u_2=0\),付 4.5 元。这不是数值误差,是逻辑漏洞:模型发现"只买最贵那一档的增量"比"买满三档"便宜,于是主动跳过了前两档。

4.2 时序与灵敏度

图 4 最优顺序下的完成时刻(实线)与各单的承诺+5、承诺+15 线

图 4 把顺序摊平。蓝线在 O1 处远低于承诺线,中段开始贴近"承诺+5",末段有两单冲破"承诺+15"。超时是累积的:前段每慢 1 min,后段全部顺延 1 min,所以把最紧的单往前挪的收益不是线性的,而是"救一单、赔一串"。

图 5 一档阈值 1—30 min 对罚款总额(左轴)与计罚单数(右轴)的影响

图 5 扫一档阈值。1 min 与 5 min 完全不动(79.5 → 79.5 元),因为现有方案的超时全部在 11 min 以上,5 min 这条线根本没碰到;放到 10 min 后 5—10 min 区间的超时改按 1.5 元收,模型换成另一条路线、只保留 1 单三档,总额降到 74.0 元,此后到 30 min 不再变化(74.0 元)。右轴的计罚单数反而从 3 单升到 5 单——放宽阈值不是让人免罚,而是把二档罚单降成一档罚单。剩下的 14.0 元是逐单罚金,加上 60 元降级,都与一档阈值无关,所以曲线在这里彻底走平。

图 6 一档阈值 × 超时量的罚金档位热图(星号为本案 5 min)

图 6 把"定价表"和"阈值"放进同一张图:横轴是超时量,纵轴是一档阈值,颜色是单档罚金。三条色带的分界是一条斜率为 1 的直线 \(\ell=\theta_1\)——阈值抬高,一档区整体右移。这张图没有连续过渡,只有四个离散色块,说明罚金对参数的导数几乎处处为零、在边界处无穷大;这正是阶梯罚金让梯度类方法失效、也让 LP 松弛很弱的原因。

图 7 二档阈值 12—50 min 在三种档间差额系数下的罚款总额

图 7 动第二个参数:把 +2 与 +4.5 同乘系数 0.5、1、1.5,同时扫描二档阈值。\(\theta_2=12\) 时三线相差 14.2 元,抬到 20 min 仍差 10.5 元;抬到 30 min 时整组曲线从 76.5 元跌到 15.5 元——模型换了一条顺序,把两单 50 min 以上的超时压到 15 min 内,60 元降级被整笔省掉,剩下的差别只是档间差额本身。再抬到 40、50 min 只剩 12.0 元的一档罚款。阈值决定"要不要付 60 元",差额只在付之前决定付多少;一旦跨过临界值,曲线的落差由 60 元主导,差额系数退居次要。

4.3 前沿、分布与分解

图 8 里程预算—最小罚款前沿(阴影为里程不可行区)

图 8 换一个问法:给定里程预算,罚款能压到多少。预算低于 34.0 km 无可行路径(阴影);34 km 时最小罚款 84.5 元,比无预算约束多 5.0 元;放宽到 37 km 降到 80.0 元,42 km 才回到 79.5 元,再放到 50 km 一分不降。前沿只在 34—42 km 之间跳两次,之后水平——里程与罚款之间只有两个可交易的点,越过它们,多跑的路换不回任何罚款。

图 9 超时分钟分布(柱)与累计分布(线),虚线为 5/15 min

图 9 看分布。3 单有超时,分别是 55 / 52 / 11 min,其余 5 单提前或准时。分布被 15 min 劈成两段:1 单在 5—15 min(可救),2 单在 15 min 以上(结构性)。累计曲线在 15 min 处几乎垂直——这 2 单只占 25% 的单量,却触发了全部降级并吃掉 87% 的罚金。

图 10 逐单罚金堆叠(一档 1.5 / 二档 +2 / 三档 +4.5)与降级 60 元

图 10 按单拆账:逐单罚金合计 19.5 元,降级 60 元,总额 79.5 元。60 元占 75%,是最大的一笔,而它只由一单触发。这解释了模型的"怪异"行为:为了不降级,它宁可让某单多超 14 min;一旦降级无法避免,那一单的超时分钟数就彻底无所谓了——阈值型成本让目标函数在 15 min 处出现断崖,断崖右侧的目标值对超时量不敏感。

4.4 为什么这是非凸问题

把罚金看作超时量的函数:它在 [0,5] 上取 1.5、在 (5,15] 上取 3.5、(15,60] 上取 8,每一步都是向上跳,函数的次梯度在跳点处不存在,整体既不凸也不连续。LP 松弛把 \(u\) 放到 [0,1],最优解会取 \(u=0\) 或分数端点,松弛下界就是"全部免罚"的 0 元,与整数最优差 79.5 元——松弛一点用都没有。可行的做法只有两条:把阶梯拆成 0-1 变量(本案),或者对每一档单独建子问题再比较。前者靠 Gurobi 的分支定界,8 单 25 个 0-1 变量在 1 s 内可解;后者在 8 单时已经要枚举 4 种档位组合。

顺带一个反例:若把降级写成 \(60\sum_i u_{i,3}\),2 单降级就要付 120 元,总额 139.5 元。存在性成本与逐单成本相乘,是这类"一次性罚则"最常见的重复计费。

5 结论

(1)最优顺序 O1 → O7 → O6 → O3 → O8 → O2 → O4 → O5,总罚金 79.5 元:逐单 19.5 元(一档 3 单、二档 3 单、三档 2 单)加降级 60 元,里程 40.00 km。
(2)阶梯要用累积差值写:系数是 1.5、+2、+4.5,累计才是 1.5/3.5/8。直接按 1.5/3.5/8 相加,一单三档要收 13 元,本案总额从 79.5 元虚增到 91.0 元(+11.5)。
(3)\(u_3\le u_2\le u_1\) 不能漏:漏掉后模型用 4.5 元买断 8 元档位,给出 75.5 元的假解,虚低 4.0 元,且解里出现"三档=1、一二档=0"的荒谬组合。
(4)降级是指示约束:\(\sum_i u_{i,3}\ge 1\Rightarrow z=1\),60 元只付一次。写成 \(60\sum_i u_{i,3}\) 会多付 60.0 元。
(5)灵敏度:一档阈值 1→10 min 时罚款 79.5→74.0 元,之后到 30 min 不再变化;二档阈值 12→30 min 时罚款从 76.5 元断崖式跌到 15.5 元。阶梯罚金使目标非凸、LP 松弛弱,只能靠 0-1 建模加分枝定界。


参考文献

[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_093.py;数据与结果见同目录 .csv / .json,图见 figs/

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