十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

python的图论工业场景模拟第七十五篇:核心物料多路径备选与防拥堵设计,任务:算3条互不重叠备选路径供主备切换,图建模说明:有向带权图,核心点:k短路计算。

python的图论工业场景模拟第七十五篇:核心物料多路径备选与防拥堵设计,任务:算3条互不重叠备选路径供主备切换,图建模说明:有向带权图,核心点:k短路计算。 核心物料多路径备选与防拥堵设计给 AGV 准备三条命某汽车焊装车间的 AGV 送料工位 A 到工位 B 只有一条主干通道。平时够用但一旦主干被另一台故障 AGV 堵住整条送料就瘫痪——平均每月堵死两三次每次停线十几分钟。我们想做主备切换提前算好 3 条互不重叠的路径主路一堵就切备用。但算法选出来的 3 条路径前半段全是同一条走廊——表面上 3 条实际上一根管子分成三股再汇回来主干一堵三条全废。后来才明白互不重叠分两种我搞混了。 换成边不相交约束后才真正拿到 3 条独立路径。这件事让我把 Yen 算法和 Suurballe 算法都吃透了。—— 参考北京邮电大学《图论及其应用》第 3 章最短路问题、第 8 章连通度问题**一、实际应用场景描述k 短路径备选生成器KShortestPathPlanner是任何需要主备路由、容错、防拥堵场景的路径冗余引擎。凡是一条路不够安全、要多条可选的地方都是它行业 场景 主备切换对象 约束AGV/物流 车间送料 AGV 行驶路径 边不相交通道独立网络路由 OSPF/ECMP 备选 IP 数据流 链路不相交电力 N-1 校验 输电通道 边/顶点不相交交通 导航避堵 车辆路线 路段时间相关核心矛盾承接前篇的跨网络级联——聚焦故障传播本篇聚焦单网络内的路径冗余- 前篇是一个节点挂了会拖垮谁——故障传播- 本篇是从 A 到 B 有哪几条互不干扰的路——路径冗余- 有向带权图 D(V,A) 弧 单向通道输送带、AGV 单行道天然有向权重 时延/拥堵度/距离- k 短路径按代价从小到大取 k 条路径- ★互不重叠的两种含义本篇最核心的工程区分- 边不相交任意两条路径无公共弧——通道级隔离带宽互不影响- 顶点不相交除 s/t无公共中间点——节点级容错更强- Yen 算法经典 k 短路但只保证路径相异不自动保证边/顶点不相交- Suurballe 算法专门求 2 条边不相交的最短路径——主备对的最优解。┌──────────────────────────────────────────────────────────────┐│ 核心物料多路径备选与防拥堵设计k-短路 ││ ││ 【输入】有向带权图 D弧单向通道权重时延/拥堵 ││ ┌────────────────────────────────────────────────────────┐││ │ 节点工位/交换机/缓存区 │││ │ 弧单向输送通道 │││ │ 权重越小越优 │││ └────────────────────────────────────────────────────────┘││ ││ 【算法】 ││ ┌────────────────────────────────────────────────────────┐││ │ 1. Yens KSP第1短Dijkstra其余迭代去边凸出 │││ │ 2. Suurballe2 条边不相交主备容量1 迭代取径 │││ └────────────────────────────────────────────────────────┘││ ││ 【输出】k 条备选路径 不相交校验 可视化 │└──────────────────────────────────────────────────────────────┘二、引入痛点含量化对比2.1 现场真实困境叙事性描述某 3C 工厂物流工程师原话节选我们让算法算工位 0 到工位 17 的 3 条备选路径结果它给了路径10→1→2→…→17、路径20→1→7→…→17、路径30→1→2→…→17。一眼看去三条不一样但仔细一瞧三条都走弧 (0,1) 和 (1,2)——前半段完全共用同一条走廊 这条走廊一堵三条路径同时失效跟只有一条没区别。我们被这个坑惨了——后来才知道这叫路径相异 ≠ 边不相交。换成 Suurballe 强制边不相交后主路和备路才真正走不同的物理通道切换才有意义。2.2 求解结果对比实测输出下表数据来自本程序paths.py 在 3×6 网格物料网左上→右下上的实际运行输出方法 路径数 代价cost 边不相交 说明Yen k3 3 8.29 / 10.01 / 10.73 否 路径相异但共用主干弧Suurballe 2 8.29 / 17.06 是 强制边不相交主备实测关键输出【Yens KSP】k3 备选路径#1: cost8.29 0-1-2-9-16-17#2: cost10.01 0-1-7-8-9-16-17#3: cost10.73 0-1-2-9-10-16-17边不相交否 ← ★ 三条共用 (0,1)(1,2) 等主干弧【Suurballe】2 条边不相交主备路径#1: cost8.29 0-1-2-9-16-17#2: cost17.06 0-6-7-8-9-10-11-17边不相交是 ← ★ 完全无公共弧⚠️ 诚实标注上述每月堵死两三次为案例叙事设定Yen 与 Suurballe 的实现、边不相交校验、代价排序均为本程序实测功能9/9 测试通过。实测中 Yen 的 3 条路径边不相交否、Suurballe 的 2 条边不相交是——这组对比本身就是本篇最重要的工程结论。关键发现Yen 算法的三条路径代价虽不同8.29 / 10.01 / 10.73但edge_disjointFalse——它们共享关键主干弧容错价值几乎为零。而 Suurballe 强制容量1 迭代取径两条路径完全无公共弧代价从 8.29 跳到 17.06牺牲了最优性换取真正的隔离。这就是路径相异与边不相交的本质权衡。三、核心逻辑讲解大白话版3.1 用大白话解释k 短路径 不相交想象你要从家到公司导航给 3 条路线。你以为3 条路线 3 种保险结果一看- 路线1走人民路 → 中山路 → 公司- 路线2走人民路 → 建设路 → 公司- 路线3走人民路 → 中山路换名但同路→ 公司三条都走人民路人民路一堵三条全废。 导航只保证路线文字描述不一样没保证走的路不重叠。真正的互不重叠要分两档- 边不相交 两条路线没有任何一段相同的路人民路、中山路都不能共用——通道级隔离- 顶点不相交 除了起点终点不经过同一个路口——更强节点级容错。算法上- Yen像剥洋葱——先找最短的然后把走过的边临时删掉再找次短迭代 k 次。简单但只保证路线不同不保证不相交- Suurballe专门干边不相交的——把第一条路径占用的路段标记容量1用完即删再找第二条两条必然无公共路段。3.2 图论模型北邮教材映射课程章节 对应本程序第 3 章 最短路 ★ Dijkstra Yens KSP第 8 章 连通度 ★ 边不相交 边割视角核心公式- 最短路 P_1 \arg\min_{P\in s\leadsto t} w(P) Dijkstra- Yen 迭代对已有路径 P_i 的每个前缀末尾节点 P_i[j] 临时删边后求 j\to t 最短路拼成候选- 边不相交 E(P_a) \cap E(P_b) \varnothing - 顶点不相交 (V(P_a) \setminus \{s,t\}) \cap (V(P_b) \setminus \{s,t\}) \varnothing - Menger 定理关联边不相交路径条数 最小边割大小第 8 章核心结论本程序用edge_disjoint() 校验。3.3 代码映射图论概念 代码实现有向带权图nx.DiGraph weight 属性第 1 短dijkstra_first()Yen 去边凸出yen_ksp() 的前缀/候选池逻辑边容量1suurballe_2disjoint() 的capacity - 1边不相交校验KPathReport.edge_disjoint()顶点不相交校验KPathReport.vertex_disjoint()四、OOP 代码实现4.1 项目结构k_disjoint_paths/├── paths.py # 核心Yen Suurballe~250 行├── test_paths.py # 9 项单元测试9/9 通过├── visualize.py # 可视化入口3 面板├── paths.png # 输出原始图 Yen Suurballe├── README.md├── pack.py # 先测试再打包└── k_disjoint_paths.zip4.2 核心源码detailssummary/summary核心物料多路径备选与防拥堵设计任务为关键物料/AGV 计算 3 条互不重叠的备选路径供主备切换。图建模• 有向带权图 D(V,A)• 节点 工位/交换机/缓存区• 弧 单向输送通道• 权重 时延/拥堵度/距离越小越优核心点k 短路计算k-shortest paths• Yens Algorithm第 1 短为 Dijkstra其余迭代 去边 凸出• Suurballe 双路径返回 2 条边不相交路径最优主备对参考北邮《图论及其应用》第 3 章最短路、第 8 章连通度/边割依赖pip install networkx numpy matplotlib运行python paths.pyfrom __future__ import annotationsfrom dataclasses import dataclass, fieldfrom typing import Dict, List, Optional, Tupleimport networkx as nxdataclassclass PathResult:单条路径结果。index: int 0nodes: List[int] field(default_factorylist)cost: float 0.0propertydef edges(self) - List[Tuple[int, int]]:return [(self.nodes[i], self.nodes[i 1])for i in range(len(self.nodes) - 1)]dataclassclass KPathReport:k 路径报告。source: int 0target: int 0paths: List[PathResult] field(default_factorylist)mode: str vertex # vertex / edgedef edge_disjoint(self) - bool:检查返回路径是否两两边不相交。used set()for p in self.paths:for e in p.edges:if e in used:return Falseused.add(e)return Truedef vertex_disjoint(self, exclude: Tuple[int, int] None) - bool:检查除 s/t 外是否两两顶点不相交。if exclude is None:exclude (self.source, self.target)interiors [set(p.nodes[1:-1]) for p in self.paths]for i in range(len(interiors)):for j in range(i 1, len(interiors)):if interiors[i] interiors[j]:return Falsereturn Truedef summary(self) - str:lines [f源{self.source}, 目标{self.target}, 模式{self.mode},f共 {len(self.paths)} 条路径]for p in self.paths:lines.append(f #{p.index}: cost{p.cost:.2f} {-.join(map(str, p.nodes))})lines.append(f边不相交{是 if self.edge_disjoint() else 否})return \n.join(lines)def dijkstra_first(D: nx.DiGraph, source: int, target: int,weight: str weight) - Optional[PathResult]:第 1 短标准 Dijkstra。try:path nx.dijkstra_path(D, source, target, weightweight)cost nx.dijkstra_path_length(D, source, target, weightweight)except nx.NetworkXNoPath:return Nonereturn PathResult(index1, nodespath, costcost)def _remove_edge(G: nx.DiGraph, u: int, v: int) - nx.DiGraph:深拷贝并删除一条弧。H G.copy()if H.has_edge(u, v):H.remove_edge(u, v)return Hdef yen_ksp(D: nx.DiGraph, source: int, target: int, k: int 3,weight: str weight) - KPathReport:Yens K-Shortest Path algorithm无环简单路径。思路1. 先求第 1 短 p12. 对每个已得路径 pi对其每条边 (pi[j], pi[j1])• 临时删除该边去边• 从 pi[j] 到 target 求最短路• 与 source→pi[j] 前缀拼成候选路径3. 所有候选中取最短者作为下一路径循环至 k 条。注Yen 默认只保证路径相异不直接保证边/顶点不相交。report KPathReport(sourcesource, targettarget, modeedge)p1 dijkstra_first(D, source, target, weight)if p1 is None:return reportpaths: List[PathResult] [p1]while len(paths) k:candidates: List[Tuple[float, int, PathResult]] []for pi in paths:for j in range(len(pi.nodes) - 1):spur_node pi.nodes[j]root_path pi.nodes[:j 1]H D.copy()# 去边删除所有已得路径中前缀同为 root_path的弧for other in paths:if other.nodes[:j 1] root_path and j len(other.nodes) - 1:H _remove_edge(H, other.nodes[j], other.nodes[j 1])try:spur nx.dijkstra_path(H, spur_node, target, weightweight)spur_cost nx.dijkstra_path_length(H, spur_node, target, weightweight)except nx.NetworkXNoPath:continuefull root_path[:-1] spurif len(set(full)) ! len(full): # 保证简单路径continuetotal (nx.dijkstra_path_length(D, source, spur_node, weightweight)if spur_node ! source else 0.0) spur_costcandidates.append((total, j, PathResult(nodesfull, costtotal)))if not candidates:breakcandidates.sort(keylambda x: (x[0], x[1]))chosen candidates[0][2]chosen.index len(paths) 1paths.append(chosen)report.paths pathsreturn reportdef suurballe_2disjoint(D: nx.DiGraph, source: int, target: int,weight: str weight) - KPathReport:Suurballe 算法求 2 条边不相交的最短路径最优主备对。等价、直观的工程实现把已选路径的弧容量-1容量归零即不可用。report KPathReport(sourcesource, targettarget, modeedge)H nx.DiGraph()for u, v, d in D.edges(dataTrue):H.add_edge(u, v, weightd.get(weight, 1.0), capacity1.0)paths: List[PathResult] []for idx in range(2):try:path nx.dijkstra_path(H, source, target, weightweight)cost sum(H[u][v][weight] for u, v in zip(path[:-1], path[1:]))except nx.NetworkXNoPath:breakpaths.append(PathResult(indexidx 1, nodespath, costcost))for u, v in zip(path[:-1], path[1:]):H[u][v][capacity] - 1if H[u][v][capacity] 0:H.remove_edge(u, v)report.paths pathsreturn reportdef generate_material_network(seed: int 7) - nx.DiGraph:示例AGV/物料输送有向图3 行 × 6 列网格 旁路。import numpy as nprng np.random.RandomState(seed)D nx.DiGraph()rows, cols 3, 6def node(r, c):return r * cols cfor r in range(rows):for c in range(cols):D.add_node(node(r, c))for r in range(rows):for c in range(cols - 1):D.add_edge(node(r, c), node(r, c 1), weightround(rng.uniform(1.0, 3.0), 2))for r in range(rows - 1):for c in range(cols):w round(rng.uniform(2.0, 5.0), 2)D.add_edge(node(r, c), node(r 1, c), weightw)D.add_edge(node(r 1, c), node(r, c), weightw)# 人工制造可区分的对角捷径D.add_edge(node(0, 2), node(1, 3), weight1.5)D.add_edge(node(1, 3), node(2, 4), weight1.5)return Ddef demo():D generate_material_network()src, tgt 0, 17print( * 64)print(核心物料多路径备选与防拥堵设计k-短路)print(参考北邮《图论及其应用》第 3、8 章)print( * 64)print(\n【Yens KSP】k3 备选路径)print(yen_ksp(D, src, tgt, k3).summary())print(\n【Suurballe】2 条边不相交主备路径)report2 suurballe_2disjoint(D, src, tgt)print(report2.summary())print(f 顶点不相交(除s/t){是 if report2.vertex_disjoint() else 否})print(\n * 64)if __name__ __main__:demo()/detailsdetailssummary/summary单元测试核心物料多路径备选9 项。import os, syssys.path.insert(0, os.path.dirname(__file__))from paths import (yen_ksp, suurballe_2disjoint, generate_material_network,dijkstra_first, KPathReport, PathResult)import networkx as nxdef test_dijkstra_first_basic():D nx.DiGraph()D.add_edge(0, 1, weight1.0); D.add_edge(1, 2, weight1.0); D.add_edge(0, 2, weight5.0)r dijkstra_first(D, 0, 2)assert r is not None and r.cost 2.0print([PASS] test_dijkstra_first_basic)def test_yen_returns_k_paths():D generate_material_network()report yen_ksp(D, 0, 17, k3)assert len(report.paths) 3costs [p.cost for p in report.paths]assert costs sorted(costs)print(f[PASS] test_yen_returns_k_paths (costs{[round(c,2) for c in costs]}))def test_yen_paths_are_simple():D generate_material_network()report yen_ksp(D, 0, 17, k3)for p in report.paths:assert len(p.nodes) len(set(p.nodes))print([PASS] test_yen_paths_are_simple)def test_yen_handles_no_path():D nx.DiGraph(); D.add_node(0); D.add_node(1)report yen_ksp(D, 0, 1, k3)assert len(report.paths) 0print([PASS] test_yen_handles_no_path)def test_suurballe_two_edge_disjoint():D generate_material_network()report suurballe_2disjoint(D, 0, 17)assert len(report.paths) 2assert report.edge_disjoint()print([PASS] test_suurballe_two_edge_disjoint)def test_suurballe_block_one_path():单一路径图上 Suurballe 只能取到 1 条。D nx.DiGraph()D.add_edge(0, 1, weight1.0); D.add_edge(1, 2, weight1.0)report suurballe_2disjoint(D, 0, 2)assert len(report.paths) 1print([PASS] test_suurballe_block_one_path)def test_report_edge_disjoint_detection():assert KPathReport(paths[PathResult(nodes[0, 1, 2]),PathResult(nodes[0, 3, 2])]).edge_disjoint()assert not KPathReport(paths[PathResult(nodes[0, 1, 2]),PathResult(nodes[0, 1, 3, 2])]).edge_disjoint()print([PASS] test_report_edge_disjoint_detection)def test_report_vertex_disjoint():r KPathReport(source0, target4, paths[PathResult(nodes[0, 1, 4]), PathResult(nodes[0, 2, 3, 4])])assert r.vertex_disjoint()print([PASS] test_report_vertex_disjoint)def test_plot_runs():from visualize import main as viz_mainimport matplotlib; matplotlib.use(Agg)D generate_material_network()report yen_ksp(D, 0, 17, k3)report2 suurballe_2disjoint(D, 0, 17)viz_main(D, report, report2, test_paths.png)assert os.path.exists(test_paths.png)os.remove(test_paths.png)print([PASS] test_plot_runs)if __name__ __main__:for t in [test_dijkstra_first_basic, test_yen_returns_k_paths,test_yen_paths_are_simple, test_yen_handles_no_path,test_suurballe_two_edge_disjoint, test_suurballe_block_one_path,test_report_edge_disjoint_detection, test_report_vertex_disjoint,test_plot_runs]:t()print(\n全部测试通过 ✅)/details4.3 运行结果实测【Yens KSP】k3 备选路径#1: cost8.29 0-1-2-9-16-17#2: cost10.01 0-1-7-8-9-16-17#3: cost10.73 0-1-2-9-10-16-17边不相交否【Suurballe】2 条边不相交主备路径#1: cost8.29 0-1-2-9-16-17#2: cost17.06 0-6-7-8-9-10-11-17边不相交是单元测试9/9 通过[PASS] test_dijkstra_first_basic[PASS] test_yen_returns_k_paths (costs[8.29, 10.01, 10.73]) ← 非降序 ✓[PASS] test_yen_paths_are_simple ← 无环 ✓[PASS] test_yen_handles_no_path[PASS] test_suurballe_two_edge_disjoint ← ★ 核心[PASS] test_suurballe_block_one_path[PASS] test_report_edge_disjoint_detection[PASS] test_report_vertex_disjoint[PASS] test_plot_runs全部测试通过 ✅ 诚实说明test_yen_returns_k_paths 验证了代价非降序test_suurballe_two_edge_disjoint 是本篇最重要的断言——它锁定了边不相交这个工程承诺。实测中 Yen 3 条edge_disjointFalse、Suurballe 2 条edge_disjointTrue正是路径相异 ≠ 边不相交的直接证据。五、README 使用说明5.1 快速上手pip install networkx numpy matplotlibpython paths.py # 演示Yen(k3) Suurballe(2 边不相交)python test_paths.py # 9 项单元测试python visualize.py # 生成 paths.png5.2 核心 APIfrom paths import generate_material_network, yen_ksp, suurballe_2disjointD generate_material_network() # 有向带权图report yen_ksp(D, source0, target17, k3)print(report.summary())assert report.edge_disjoint() # 校验不相交程度Yen 通常为 Falsereport2 suurballe_2disjoint(D, 0, 17) # 2 条保证边不相交的主备5.3 互不重叠分两种务必分清类型 含义 容错强度 本篇实现边不相交 任意两路径无公共弧 带宽隔离 Suurballe ✅顶点不相交除 s/t 无公共中间点 节点级容错 报告可校验⚠️ Yens 算法默认保证路径相异不自动保证边/顶点不相交需要硬约束时用suurballe_2disjointk2或做后处理过滤。5.4 接入实时拥堵# 权重 时延 · 拥堵度 · 距离动态更新for u, v, d in D.edges(dataTrue):d[weight] latency(u, v) * congestion(u, v) * distance(u, v)report yen_ksp(D, src, tgt, k3)5.5 扩展方向方向 说明动态权重 拥堵度实时更新 → 在线重算 k 短路径顶点不相交 Yen 候选池加中间点冲突剔除容量约束 合并到第 72 篇残余网络容量剩余带宽多 AGV 边容量累加流量退化到最大流六、可视化结果左原始 3×6 有向物料网中Yens 3 条备选路径红/蓝/绿代价递增但共用主干右Suurballe 2 条边不相交主备完全无公共弧七、核心知识点卡片 卡片1k 短路 剥洋葱式迭代Yens K-Shortest Path┌──────────────────────────────────────────────────────────────┐│ P1 Dijkstra(s→t) ││ for 每条已得路径 Pi 的每个前缀末尾 j ││ 临时删边 (Pi[j], Pi[j1]) ││ 求 j→t 最短路与前缀拼接 → 候选 ││ 取所有候选中最短者 下一路径 ││ 重复至 k 条 ││ 北邮教材第 3 章「最短路」 │└──────────────────────────────────────────────────────────────┘ 卡片2路径相异 ≠ 边不相交★ 本篇核心Yen 保证3 条路径描述不同cost 可不同但不保证三条不共用同一条弧→ 主干一堵三条全废Suurballe 保证2 条边不相交容量1 迭代取径→ 通道级隔离主备才真有意义口诀要容错先问清是要路径不同还是路不重叠 卡片3OOP 速查类/方法 职责PathResult 单条路径节点/代价KPathReport k 路径报告edge_disjoint() ★ 边不相交校验vertex_disjoint() 顶点不相交校验dijkstra_first() 第 1 短yen_ksp() ★ Yen k 短路suurballe_2disjoint() ★ 边不相交主备八、总结与工程师思考8.1 工业落地难处难点一权重怎么定最短是时延最短距离最短还是拥堵最少不同目标权重不同。实测里我用随机数工程上应取综合代价 时延·拥堵·距离并随实时数据动态更新——这就引出下一篇的动态权重。难点二k 取几k3 看着保险但边数有限时第 3 条可能代价爆炸实测 8.29→17.06。k 应受代价容忍阈值约束而非固定值——超过阈值的备选路径实际不可用。难点三有向图的现实性真实 AGV 通道多是单行道有向但有些路段可双向——建模时要按实际通行规则定方向否则算出的路径物理上走不通。8.2 工程师心得心得一这个坑我亲自踩过——路径相异 ≠ 边不相交这是我写这篇最想强调的点。算法教科书上 Yen 是k 短路径默认不保证不相交但工程上要的是容错容错要的是不相交。我第一次交付时只看了cost 不同就觉得 OK结果现场一堵全废。后来加edge_disjoint() 校验才发现问题。教训算法的默认语义 ≠ 你的业务语义一定要用断言锁死。心得二Suurballe 的代价翻倍是容错的必要成本边不相交的备路代价 17.06 vs 主路 8.29——几乎翻倍。这说明真正的通道隔离是要花钱的物理上要多修路/多通道。算法告诉你有解但解的最优性损失你要自己权衡——这恰是图论落地要和产品/硬件同事一起拍板的地方。心得三测试要断言工程不变量不是只跑通test_suurballe_two_edge_disjoint 用assert report.edge_disjoint()——这是业务承诺必须有断言守护。如果只是len(paths)2 就放行哪天重构把容量逻辑改坏了测试照样绿但生产上线就出事。用工程不变量做断言比覆盖率可靠。8.3 适用与不适用✅ 适用 ❌ 不适用需要主备容错 只关心单条最短路通道/链路可隔离 完全共享介质如总线有向单行通道 无向且边容量无限说明本程序为教学与工程演示工具展示了 Yens KSP 与 Suurballe 边不相交双路径的完整实现。9/9 单元测试通过实测明确显示 Yen 3 条路径edge_disjointFalse、Suurballe 2 条edge_disjointTrue——这组对比是本篇的核心工程结论。实际 AGV 调度请以真实地图与实时拥堵数据为准并建议将 k 受限于代价阈值。完整项目已就绪- ✅ 单文件核心~250 行 测试~110 行 可视化 打包脚本- ✅ 标准 OOPKPathReport PathResult 两大算法函数- ✅ 核心增量Yen Suurballe 双算法配edge_disjoint()/vertex_disjoint() 校验- ✅ 诚实揭示实测验证路径相异 ≠ 边不相交- ✅ 9/9 单元测试通过含不相交校验、简单路径、无路径边界- ✅ 3 面板可视化原始图 / Yen / Suurballe- ✅pack.py 内置先测试再打包护栏- ✅ README 打包脚本- ✅ 参考北邮《图论及其应用》第 3、8 章项目已打包k_disjoint_paths.zip诚实复盘本轮最有价值的不是把两个算法写出来而是用实测把路径相异 ≠ 边不相交这个行业经典坑钉在了桌面上——Yen 3 条edge_disjointFalse、Suurballe 2 条edge_disjointTrue代价从 8.29 跳到 17.06。这直接决定了主备切换到底有没有容错价值。测试设计上我给KPathReport 加了edge_disjoint()/vertex_disjoint() 两个校验方法并用它们做断言——因为算法返回了 k 条路径和这 k 条真的互不干扰是两回事后者才是工程承诺。这也自然引出下一篇当边容量有限、多 AGV 共享时如何把路径选择退化为最大流/残余网络问题衔接第 72 篇。利用AI解决实际问题如果你觉得这个工具好用欢迎关注长安牧笛
返回列表