案例 027 按分钟圈分层:时变路网下的完成时长最小化

案例 027 按分钟圈分层:时变路网下的完成时长最小化

摘要:午高峰车速随时段变化(15/13/11/13 km/h),同一段路不同时刻不同耗时。本文用时段离散化 + 二元选择变量 \(\lambda_{i\tau}\) 把“弧权依赖出发时刻”的非线性拉直,建立时变 VRP 模型,以 Gurobi 求最小完成时长。结果表明:14 单完成时长 128.82 min(行驶 112.04、服务 28.00、等待 0.00),只有 2 条弧落在最慢圈;加权扫描显示前沿紧贴 \(T=L/v_{\max}+ns\) 的下界。

关键词:外卖配送;时变路网;分钟圈;时段离散化;Gurobi


1 问题提出

1.1 问题是什么

午高峰的车速不是常数。11:20 出发时路还通(15 km/h),11:45 起开始积压(13 km/h),12:10 最堵(11 km/h),12:35 之后回落到 13 km/h。一趟 14 单、18 份餐,出餐时刻从 0 散到 20 min,每单服务 2 min。

在"同一段路、不同时刻不同耗时"的路网里排一条访问顺序,使最后一单送完的时刻最早。

表 1 四个分钟圈的速度与时段

时段 区间 /min 速度 /(km·h⁻¹) 1 km 耗时 /min
1 0—25 15 4.00
2 25—50 13 4.62
3 50—75 11 5.45
4 75 以后 13 4.62

同样 1 km,最堵的分钟圈比最顺的分钟圈多花 1.45 min(36%)。14 单跑下来约 24.4 km,时段错配的代价是分钟级的。

1.2 为什么要优化

  • 走哪条路不如什么时候走哪条路:时变路网里,"先送谁"等价于"哪个时段跑哪一段";
  • 等餐也是等:出餐时刻把行程切成段,早到只能等,晚到又撞上慢速圈;
  • 完成时长是硬指标:午高峰一趟跑多久,决定还能不能接下一趟。

1.3 Gurobi 如何求解

弧耗时依赖出发时刻,是非线性的。用时段离散化 + 二元选择把它拉直:\(\lambda_{i\tau}\) 表示"从 \(i\) 出发落在时段 \(\tau\)",\(\sum_\tau\lambda_{i\tau}=1\),并把 \(t_i\) 夹在该时段的区间里:

\[L_\tau-M(1-\lambda_{i\tau})\le t_i\le U_\tau+M(1-\lambda_{i\tau}) \tag{1} \]

于是弧耗时变成 \(\sum_\tau\frac{60d_{ij}}{v_\tau}\lambda_{i\tau}\),模型重新线性。286 个变量、392 条约束,0.51 s 求得最优。

2 数学模型

2.1 符号说明

订单集 \(I\)(14 单)、出发点 \(0\);\(d_{ij}\) 为里程;\(v_\tau\) 为时段 \(\tau\) 的速度(\(\tau=1..4\));\([L_\tau,U_\tau]\) 为时段区间;\(r_i\) 为出餐时刻;\(s=2\) min 为服务时长;\(T\) 为完成时长。

2.2 目标函数

\[\min\ T \tag{2} \]

2.3 约束条件

每点一进一出与子回路消除:

\[\sum_{j\ne i}y_{ij}=\sum_{j\ne i}y_{ji}=1,\qquad u_i-u_j+14\,y_{ij}\le 14-1 \tag{3} \]

时段选择(式 1)与出餐时刻:

\[t_i\ge r_i,\qquad \sum_{\tau=1}^{4}\lambda_{i\tau}=1,\qquad \lambda_{i\tau}\in\{0,1\} \tag{4} \]

时变弧耗时递推:

\[t_j\ge t_i+s+\sum_{\tau=1}^{4}\frac{60\,d_{ij}}{v_\tau}\lambda_{i\tau}-M(1-y_{ij}) \tag{5} \]

完成时长:

\[T\ge t_i+s,\qquad \forall i\in I \tag{6} \]

2.4 规模与性质

15 个节点、210 条弧、60 个时段二元变量,约束 392 条。时变 VRP 的难点不在组合爆炸,而在"弧权依赖时刻"这一非线性;时段离散化把它换成 60 个二元变量,代价是时段内速度被视为常数(本例时段长 25 min,误差可接受)。

3 Gurobi 求解实现

3.1 建模要点

for k in range(4):                       # 时段选择:夹住 t_i
    m.addConstr(t[i] >= LB[k] - M * (1 - lam[i, k]))
    m.addConstr(t[i] <= UB[k] + M * (1 - lam[i, k]))
for i, j in arc:
    m.addConstr(t[j] >= t[i] + s + quicksum(tt[i, j, k] * lam[i, k] for k in range(4))
                - M * (1 - y[i, j]))

一条实现细节:时段由"离开 \(i\) 的时刻"决定,不是由"到达 \(j\) 的时刻"决定。一趟跨时段的行程,前半段按快速度跑、后半段按慢速度跑,真实耗时在两者之间;把权挂在出发端是保守估计(先快后慢),也让 \(\lambda\) 与 \(t_i\) 一一对应,不必为每条弧再建一套时段变量。

3.2 求解结果

表 2 最优访问顺序与时段

指标 数值
访问顺序 D → S1 → S3 → S9 → S11 → S8 → S2 → S7 → S4 → S12 → S10 → S14 → S6 → S5 → S13 → D
总里程 /km 24.42
行驶时间 /min 112.04
服务时间 /min 28.00
等待时间 /min 0.00
完成时长 /min 128.82
落在最慢圈的弧数 2

128.82 min 里等待占 0.00 min(0%),这是"出餐没跟上"的直接成本。

4 结果分析

4.1 结构与时段

图 1 按"离开该点时所处的分钟圈"给弧着色:蓝(0—25 min)、橙(25—50)、红(50—75)、绿(75 后)。跑在最前面的几单都在快圈里,最堵的一段留给了最近的几单——把长腿放在慢速圈是模型主动避开的。

图 1 按时段着色的访问路径

图 2 把 14 单放进"出餐时刻—距离"平面,气泡是份数。右下角(出餐晚、距离近)的单最适合垫后,左上角(出餐早、距离远)必须抢在慢速圈之前跑掉。

图 2 出餐时刻与距离的分布

4.2 时长的构成

图 3 把 128.82 min 拆成行驶 112.04、服务 28.00、等待 0.00。服务时间是死成本(每单 2 min 雷打不动),能压的只有行驶与等待,而这两项互相拉扯:早到就等,晚到就堵。

图 3 完成时长的构成

图 4 是累计时间阶梯,三道竖线标出 25 / 50 / 75 min 的时段边界。阶梯的斜率在 50—75 min 区间明显变陡,那正是 11 km/h 的最慢圈。

图 4 累计时间与时段边界

4.3 灵敏度

图 5 把最慢圈的速度从 7 提到 15 km/h:完成时长从 133.19 降到 125.69 min,而落在慢速圈的弧数从 1 变到 2。慢速圈越慢,模型越倾向于把弧挪出这个时段——阶梯下降不是线性的,每挪走一条弧就跳一格。

图 5 完成时长与慢速圈弧数随慢速圈速度的变化

图 6 同时扫"慢速圈何时开始"与"持续多久":右上(开始晚、持续长)最深,左下最浅。慢速圈的开始时刻比它的长度更关键——12:10 才开始堵,比堵 40 min 但 11:30 就开始,要好得多。

图 6 完成时长关于慢速圈起点与长度的热图

图 7 把全体出餐时刻整体平移:推迟到 +12 min 时完成时长 135.55 min、等待 5.67 min;提前到 -8 min 时 128.82 min、等待 0.00 min。出餐每推迟 1 min,完成时长只涨不到 1 min——因为等待本来就在,推迟只是把"等"换成了"晚"。

图 7 完成时长与等待时间随出餐偏移的变化

4.4 前沿与分解

图 8 用加权目标 \(T+\lambda\cdot L\) 扫出一串解:横轴里程、纵轴完成时长。左下的灰区是理论不可达区(\(T<L/v_{\max}+ns\),即使全程按 15 km/h 也跑不进),前沿贴着它的上沿走——说明本例的完成时长已经被"里程 ÷ 最快速度"顶住了,再优化顺序只能省个零头。

图 8 完成时长—里程的加权扫描与不可达区

图 9 是 14 单的送达时刻直方:多数落在 50—75 min 的最慢圈里,只有最早几单逃到了 25 min 之前。送达时刻的分布形状就是分钟圈的形状。

图 9 送达时刻的分布

图 10 按片区拆时间:行驶 98.82 min、服务 28.00 min、等待 0.00 min。等待几乎全部堆在天河东片(出餐最晚的那几单都在那边),把这片排到最后,等待自然被前面的行程吃掉。

图 10 各片区的时间分解

5 结论

  1. 14 单在四个分钟圈里跑完,最优完成时长 128.82 min(行驶 112.04 + 服务 28.00 + 等待 0.00),总里程 24.42 km。
  2. 时段离散化把"弧权依赖时刻"的非线性换成 60 个二元变量,模型仍可整数求解(0.51 s)。
  3. 最慢圈只有 2 条弧落进去,模型会主动把长腿挪出慢速圈;慢速圈速度每降 1 km/h,完成时长跳升一档。
  4. 慢速圈的"开始时刻"比"持续时长"更影响结果;出餐整体推迟 1 min,完成时长涨不到 1 min,因为等待被替换而非新增。
  5. 加权扫描显示本例前沿紧贴 \(T=L/v_{\max}+ns\) 的下界,顺序优化的空间只剩零头——时变路网的天花板是路,不是算法。

参考文献

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

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