案例 019 单行道与掉头:有向路网上的必经点最短路

案例 019 单行道与掉头:有向路网上的必经点最短路

摘要:天河单行路网被建成 6×6 的有向网格(86 条弧),从 (0,0) 经 5 个必经点到 (5,5)。本文用流守恒 + 必经点 + 掉头触发约束建立 MIP,掉头折合 0.35 km。结果表明:最优路径 7.20 km、掉头 2 次,成本 7.90 km 当量(26.3 min);忽略单行的最短路只要 7.20 km,守规矩多走 0.00 km。绕行系数中位数 1.30,按直线估时会平均少算 30%。

关键词:外卖配送;单行道;掉头惩罚;有向最短路;Gurobi


1 问题提出

1.1 问题是什么

天河路、体育东路一带单行与双向混杂。把一个 6×6 的路口网格按棋盘式走向建成有向图(偶数行东行、奇数行西行、偶数列北行、奇数列南行),其中约 45% 的街道设为单行、其余双向,共 86 条弧:单行贡献绕行,双向提供掉头的入口。起点 (0,0),须经过 5 个必经点到 (5,5)。

在有向路网里找一条从起点到终点、经过全部必经点的路径,使"里程 + 掉头惩罚"最小;掉头一次折合 0.35 km。

表 1 必经点与其几何位置

编号 网格坐标 直线距离 /km 路网最短距离 /km 绕行系数
R1 (1,1) 0.64 0.90 1.41
R2 (2,4) 2.01 2.70 1.34
R3 (3,0) 1.35 1.35 1.00
R4 (4,2) 2.01 2.70 1.34
R5 (5,4) 2.88 4.05 1.41

1.2 为什么要优化

  • 单行不可逆:一段 0.45 km 的街走错方向,要绕三面才能回来,代价 1.35 km 起;
  • 掉头是隐性成本:路口掉头要在车流里等 30—50 s,折合 0.35 km;能少一次就少一次;
  • 必经点顺序不能随便:几何上最近的路,在有向图里可能要先北上再折回。

1.3 Gurobi 如何求解

弧变量 \(x_a\in\{0,1\}\)、掉头变量 \(r_v\in\{0,1\}\)。流守恒把路径串起来(起点 +1、终点 \(-1\)、其余 0),必经点用"出弧非空"约束:

\[\sum_{a\in\delta^+(v)}x_a-\sum_{a\in\delta^-(v)}x_a=b_v,\qquad \sum_{a\in\delta^+(v)}x_a\ge 1\ (v\in R) \tag{1} \]

掉头只在"同一路口进出互为反向"的弧对上触发:

\[r_v\ge x_a+x_b-1,\qquad a=(u,v),\ b=(v,u)\in A \tag{2} \]

模型 411 个变量、439 条约束,0.045 s 求得最优。随后对掉头惩罚、必经点数、终点位置做三轮扫描。

2 数学模型

2.1 符号说明

有向图 \(G=(V,A)\),\(|V|=36\)、\(|A|=86\);\(d_a\) 为弧长,\(\tau=0.35\) km 为掉头惩罚,\(R\) 为必经点集(\(|R|=5\));\(b_v\) 为流守恒右端(起点 +1、终点 \(-1\)、其余 0)。

2.2 目标函数

\[\min\ Z=\sum_{a\in A}d_a x_a+\tau\sum_{v\in V}r_v \tag{3} \]

2.3 约束条件

流守恒(单位流 = 一条路径):

\[\sum_{a\in\delta^+(v)}x_a-\sum_{a\in\delta^-(v)}x_a=b_v,\qquad \forall v\in V \tag{4} \]

必经点约束:

\[\sum_{a\in\delta^+(v)}x_a\ge 1,\qquad \forall v\in R \tag{5} \]

掉头触发(\(a\) 与 \(b\) 互为反向弧):

\[r_v\ge x_a+x_b-1 \tag{6} \]

\[x_a,\ r_v\in\{0,1\} \tag{7} \]

2.4 规模与性质

86 个 0-1 弧变量、239 个掉头变量,约束 439 条。这是"带转向惩罚的最短路 + 必经点",属 NP-难;单点对最短路本可用 Dijkstra,但加入必经点与转向惩罚后必须上 MIP。\(r_v\) 只对真实存在的反向弧对建,避免全枚举。

3 Gurobi 求解实现

3.1 建模要点

uturn = [(a, b) for a in A for b in A
         if a[1] == b[0] and b[1] == a[0]]        # 仅在互为反向的弧对上
for v in V:
    m.addConstr(x.sum(v, "*") - x.sum("*", v) == b[v])
for v in R:
    m.addConstr(x.sum(v, "*") >= 1)             # 必经点
for a, b in uturn:
    m.addConstr(r[a[1]] >= x[a] + x[b] - 1)     # 掉头

一条实现细节:掉头变量只对"互为反向的弧对"建约束。单行道没有反向弧,天然不可能掉头;若对所有进出的弧对都建 \(r_v\ge x_a+x_b-1\),会把"正常左转"误判成掉头,模型立刻变得不可行或严重高估。

3.2 求解结果

表 2 最优路径

指标 数值
路径 (0,0) → (1,0) → (1,1) → (1,0) → (2,0) → (3,0) → (4,0) → (4,1) → (4,2) → (3,2) → (3,3) → (2,3) → (2,4) → (3,4) → (4,4) → (5,4) → (5,5)
里程 7.20 km
掉头次数 2 次
掉头当量 0.70 km
最优成本 7.90 km 当量
忽略单行的最短里程 7.20 km(少 0.00 km)
通行时间(18 km/h) 26.3 min

单行造成的绕行是 0.00 km,占实际里程的 0%——"守规矩"的成本主要在绕行,不在掉头。

4 结果分析

4.1 路网结构与路径

图 1 中灰箭头是全部 86 条单向弧,蓝色粗线是模型选出的路径:它先沿对角线方向"斜切"到 R1,再北上取 R2,绕到东侧拿下 R4、R5,最后才折向终点。路径多次贴着网格边缘走——因为内圈的单行方向会把它困住。

图 1 单行路网与最优路径

图 2 把 5 个必经点摆在一起:柱是路网最短距离,红线是"路网 ÷ 直线"的绕行系数。R3 在 (3,0),直线只有 1.35 km,路网却要 1.35 km(系数 1.00)——它在起点的正东,但那条街是西行的。

图 2 必经点的路网距离与绕行系数

4.2 成本分解与累计过程

图 3 从直线距离 3.18 km 出发:单行与网格结构把距离推到 7.20 km(+4.02),必经点的折返再推到 7.20 km(+0.00),最后一次掉头加 0.70 km。绕行才是大头,掉头只是零头。

图 3 最优成本的逐项分解

图 4 用累计曲线比较"守单行"与"忽略单行":两条线的前半段几乎重合,从第 6 段起分开,守规矩的路径要多走 0.00 km 才到终点。差距不是均匀产生的,而是在某几个路口被迫折返时一次性拉开。

图 4 两条路径的累计里程

4.3 掉头惩罚的灵敏度

图 5 把 \(\tau\) 从 0.1 扫到 1.4 km:总成本由 7.30 升到 8.10 km 当量,掉头次数从 2 次变到 0 次。曲线在 $\tau\approx$0.35 附近有一个小台阶:再往上加惩罚,模型宁可多绕 0.4 km 也不掉头。

图 5 最优成本与掉头次数随掉头惩罚的变化

图 6 同时扫惩罚与必经点数:横带随 \(\tau\) 变深,纵带随必经点数上升。两者近似可加——每多一个必经点约多 0.6—1.0 km,每多一次掉头固定加 \(\tau\),等值线因此呈"近乎水平 + 轻微倾斜"的格子状。

图 6 最优成本关于掉头惩罚与必经点数的热图

4.4 终点、前沿与路网统计

图 7 换终点:5 个候选终点的总成本在 7.90—9.25 km 之间,掉头次数在 2—2 次之间波动。终点越靠西,路径越要逆着单行"回头",成本越高。

图 7 不同终点下的最优成本与掉头次数

图 8 把各惩罚下的解画在"里程—掉头次数"平面上:\(\tau\) 很小时模型愿意掉头换里程(右下),\(\tau\) 变大后点整体左上移。灰色区是"多次掉头"的不合常理区,本案的解贴在它的下边界。

图 8 里程—掉头次数的方案前沿

图 9 是全部 35 个路口相对起点的绕行系数累计分布:中位数 1.30,最坏 1.41——在这个单行网格里,"直线距离 × 1.3"才是靠谱的时间估计,比按 1.0 估算平均多算 30%。

图 9 绕行系数的累计分布

图 10 按走向拆开每一段:东西向共 4.05 km、南北向共 3.15 km,16 段里 9 段是东西向。在有向网格里每一段都只有一个街区长,绕行只能靠"多走几段"实现,所以"东西多还是南北多"直接反映路径被迫绕到哪一侧。

图 10 最优路径的逐段走向构成

5 结论

  1. 6×6 单行网格、5 个必经点下,最优路径 7.20 km、掉头 2 次,成本 7.90 km 当量,折合 26.3 min。
  2. 忽略单行的最短路只要 7.20 km,守规矩多走 0.00 km(0%)——单行的代价是绕行,不是掉头。
  3. 掉头惩罚 \(\tau\) 在 0.35 km 附近有一个台阶:再大一点,模型宁可多绕也不掉头。
  4. 绕行系数中位数 1.30、最坏 1.41;按"直线 × 1.3"估时比按 1.0 估更接近实际。
  5. 掉头变量只对互为反向的弧对建模(\(r_v\ge x_a+x_b-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.py;数据与结果见同目录 .csv / .json,图见 figs/

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