复习主线:建模 → 搜索框架 → 基础算法 → A* 与启发式 → 手推 → 工程扩展。
先掌握 BFS → UCS/Dijkstra → A*;DFS、Greedy 用于对照;再按需求学习其他最短路与机器人扩展。
内容组成: 12 章、6 幅技术插图、逐步手推表、13 张概念自测卡、4 道计算/建模练习。
阅读建议: 第一次按章节顺序阅读;复习时先看第 11 节,答不清的内容再回到对应章节。
目录
- [1. 学习目标与知识地图](#1. 学习目标与知识地图)
- [2. 图与状态建模](#2. 图与状态建模)
- [3. 统一搜索框架](#3. 统一搜索框架)
- [4. 四类基础搜索策略](#4. 四类基础搜索策略)
- [5. A* 与启发式](#5. A* 与启发式)
- [6. 同一张图,手推 BFS、Dijkstra 和 A*](#6. 同一张图,手推 BFS、Dijkstra 和 A*)
- [7. 其他经典最短路:Bellman--Ford 与 Floyd--Warshall](#7. 其他经典最短路:Bellman–Ford 与 Floyd–Warshall)
- [8. 机器人路径规划中的工程扩展](#8. 机器人路径规划中的工程扩展)
- [9. 工程建模与实现检查](#9. 工程建模与实现检查)
- [10. 算法对比、复杂度与选型](#10. 算法对比、复杂度与选型)
- [11. 复习卡片、练习与学习安排](#11. 复习卡片、练习与学习安排)
- [12. 术语索引与参考资料](#12. 术语索引与参考资料)
1. 学习目标与知识地图
1.1 先抓住一条主线
核心框架
状态描述"当前情况",边决定"允许怎么走",代价决定"什么叫好",搜索策略决定"下一步检查谁"。
图搜索通常解决两个层次的问题:
- 可行性: 起点到目标有没有一条合法路径?
- 最优性: 在合法路径中,哪条总代价最小?
"找到一条路"和"找到最好的路"是不同要求。DFS 通常只能帮助回答第一个问题;Dijkstra 和满足条件的 A* 能回答第二个问题。
本笔记默认: 图有限、环境在一次搜索期间不变、边代价非负,目标可通过明确规则判断。 涉及其他条件时另行说明。这里的"最优"首先指 所建图和所定义代价下的最优 。
1.2 算法分类:分类维度可以交叉
| 分类角度 | 典型算法 | 需要回答的问题 |
|---|---|---|
| 不使用剩余代价估计 | BFS、DFS、UCS/Dijkstra | 没有目标距离估计时,如何组织探索? |
| 使用启发式估计 | Greedy、A*、Weighted A* | 怎样利用对目标的估计安排搜索顺序? |
| 最短路径问题 | Dijkstra、Bellman--Ford、Floyd--Warshall、A* | 怎样求最小总代价?需要单源还是全点对结果? |
| 机器人规划扩展 | D* Lite、JPS、Theta*、CBS/ECBS | 地图变化、栅格加速、任意角度、多机器人分别怎么处理? |
分类不能当成互斥集合
Dijkstra 既属于无信息搜索,也用于最短路径;A* 既是启发式搜索,也是满足特定条件的最短路径算法。
1.3 学完后应能独立回答
- 为什么节点可能需要包含朝向、速度或时间?
- BFS 找到的"最短",具体短在哪里?
- 为什么第一次发现一个节点,不代表找到了到它的最优路径?
- UCS 和 Dijkstra 的共同机制是什么?
- A* 为什么用 g+h,什么时候可以保证最优?
- 可采纳性和一致性有什么区别?什么时候必须重开节点?
- 四邻域、八邻域分别适合什么启发式?
- 搜索结果最优,为什么机器人仍可能无法执行?
2. 图与状态建模
2.1 把地图转换成图
图通常记为:
\mathcal{G}=(V,E)
| 符号/概念 | 含义 | 路径规划例子 |
|---|---|---|
| V | 节点集合 | 所有合法栅格状态 |
| E | 边集合 | 两个状态之间可执行的运动 |
| c(u,v) | 从 u 到 v 的边代价 | 距离、时间、能耗 |
| S | 起点 | 当前机器人状态 |
| G | 目标;也可推广为目标集合 | 目标位置及允许的朝向范围 |
| P | 路径 | 从起点到目标的一串节点或动作 |
例:四邻域栅格。 每个可通行格子是节点;上、下、左、右相邻格子之间建立合法连接;占据格及不合法连接被剔除。图也可以用邻居生成函数隐式表示,不必预先存下所有边。参考:LaValle,离散规划
2.2 节点是"状态",不一定只是"位置"
设计状态的判断准则:
给定当前状态后,是否已经能决定后续允许的动作及其代价?如果还必须询问过去的某些信息,就可能需要把这些信息加入状态。
| 模型 | 常用状态 | 为什么需要这些量 |
|---|---|---|
| 简化二维点机器人 | (x,y) | 只关心平面位置 |
| 有朝向约束的车辆 | (x,y,\theta) | 车头方向影响后续可行运动 |
| 加减速受限的车辆 | (x,y,\theta,v) | 当前速度影响刹停距离和下一步动作 |
| 考虑时变占据的规划 | (x,y,t),必要时加入其他状态量 | 同一位置在不同时刻可能有不同安全性 |
典型反例:转弯有惩罚,却只按位置去重。
两条路径到达同一格子:一条从西侧来,另一条从南侧来。即使前者当前代价更低,后者也可能因为接下来的转向更少而使总路线更便宜。如果把二者视为同一状态,便可能错误地剪掉后者。
解决方式:让状态包含足以确定转弯代价的来向或朝向,例如 (x,y,d)。同时避免加入与任务无关的量,否则状态数会迅速增长。
2.3 有向图、无向图与运动约束
- 无向图: 若能从 u 到 v,也能反向走同一连接;两方向代价相同的情形最常见。
- 有向图: u\to v 合法,不代表 v\to u 合法,例如单行道。
- 边的合法性必须检查整段运动: 两个端点无碰撞,不能推出中间轨迹无碰撞。
- 状态相同的判定必须统一: 例如朝向 0 与 2\pi 应按所用模型归一化。
建模错误,搜索无法补救
如果图允许车辆横向平移,算法可能返回一条数学上连通、车辆却无法执行的路径。搜索只会遵守已经编码到图里的约束。
2.4 路径代价:最短不一定是距离最短
对路径 P=(v_0,v_1,\ldots,v_k),常见定义为:
J(P)=\sum_{i=0}^{k-1}c(v_i,v_{i+1})
| 边代价 | 算法实际优化的目标 |
|---|---|
| 几何长度 | 总距离最短 |
| 通过该段所需时间 | 总通行时间最短 |
| 能耗估计 | 总能耗最低 |
| 距离+地形惩罚+其他可加项 | 指定综合代价最小 |
若需要混合多个指标,可定义:
c=\alpha\,c_{\text{distance}}+\beta\,c_{\text{time}}+\gamma\,c_{\text{risk}}
权重应有明确含义,并处理尺度与单位。"距离 100"和"风险 0.1"直接相加,会使结果受到数值尺度支配。风险也不一定天然可加,应先建立适合任务的代价模型。
约束与代价要区分: 禁止碰撞通常应作为可行性约束;把碰撞只设为某个有限惩罚,仍可能使算法在其他路线更贵时选择碰撞路线。
3. 统一搜索框架
3.1 搜索是在计算候选路线
搜索过程中的"沿路探索",是计算机检查状态与连接;机器人通常在规划完成后才执行路线。因此,算法扩展了很多节点,不表示机器人真实走过这些位置。
不同基础算法的主要差别可以概括为: 从候选集合里,按什么规则选出下一个节点?
3.2 六个核心记录
| 记录 | 精确定义 | 复习时的直观说法 |
|---|---|---|
| OPEN | 已发现、仍待处理的候选记录 | 下一批可以检查谁 |
| CLOSED | 已扩展状态的记录;不同实现可用其他结构代替 | 谁已经处理过,是否需要重开 |
| g(n) | 当前已知从 S 到 n 的最小代价 | 已经花了多少 |
| h(n) | 从 n 到目标的剩余代价估计 | 预计还要花多少 |
| f(n) | A* 中的 g(n)+h(n) | 经过这里的预计总代价 |
| parent(n) | 当前最佳已知到达方式中的前驱 | 最后如何还原路线 |
初始化通常设:g(S)=0,其他节点 g=+\infty,parent(S)=\varnothing。
g 不是"天然最优"
g(n) 是当前找到的最好结果,可能被后续路线改小。只有在特定算法及条件下,节点被取出时才能证明其 g 已经最优。
3.3 区分发现、取出、扩展与关闭
| 动作 | 含义 | 是否说明到该点的代价最优? |
|---|---|---|
| 发现/生成 | 第一次得到该状态,或生成一条到达它的候选路径 | 通常不能 |
| 放入 OPEN | 候选记录进入队列、栈或优先队列 | 通常不能 |
| 取出 | 按算法规则选出一条候选记录 | 取决于算法与条件 |
| 扩展 | 枚举该状态的合法后继并处理连接 | 本身不是最优性证明 |
| 关闭 | 将其记录为已扩展 | 是否永久关闭取决于更新规则 |
本文采用" 有效取出目标 → 终止;其他节点 → 扩展 "的约定。因此,计数时"取出节点数"和"扩展节点数"可能相差目标节点这一次。
"有效取出"表示:记录未过时,并满足当前实现的重复处理规则。
3.4 松弛:更便宜的路线就更新
扩展 u,检查邻居 v:
g_{\text{new}}=g(u)+c(u,v)
若:
g_{\text{new}}<g(v)
则更新:
g(v)\leftarrow g_{\text{new}},\qquad parent(v)\leftarrow u
并更新其候选优先级;如果所用算法要求重开,则将已关闭的 v 重新放回 OPEN。
例: 原先到 v 的最好代价为 10,新路线经 u 到达,其中 g(u)=3、c(u,v)=2。新代价为 5,因此把 g(v) 从 10 改为 5,父节点改为 u。
松弛是一次比较与更新,不能理解为"已经走到那里,再退回来重走"。
3.5 搜索骨架与终止条件
初始化起点记录,并放入 OPEN
while OPEN 非空:
按当前算法的规则取出候选记录
若记录过时,或按当前规则无需再处理:
跳过
若当前状态满足目标条件:
沿 parent 恢复路径并返回
扩展当前状态:
生成合法后继
按算法规则执行首次发现或代价改善处理
若 OPEN 耗尽仍未找到目标:
返回"在当前图中不存在可达目标"
这是共同结构, 不能把 BFS 的"首次发现就固定"直接复制到带权搜索中。
| 算法 | 常用安全终止方式 | 补充 |
|---|---|---|
| BFS | 目标出队时 | 等权、正确按层搜索时,首次发现目标也可终止 |
| DFS | 发现或取出目标时 | 返回可行路径,通常不保证最优 |
| Dijkstra/UCS | 目标以当前有效最小 g 记录取出时 | 需要非负边权 |
| 标准 A* | 目标以当前有效最小 f 记录取出时 | 还需要相应启发式与更新条件 |
| Greedy | 目标取出时 | 不能据此证明总代价最优 |
带权搜索不能"看见终点就停"
在后文例图中,经 A 发现目标的路线代价为 12;此时经 B,C,D 的代价 8 路线可能还没完成。发现目标只说明已有一条可行路线。
3.6 树搜索与图搜索:区别在重复状态处理
- 树搜索: 把不同动作序列产生的路径分别展开;同一状态可能以多个搜索树节点反复出现。
- 图搜索: 识别相同状态,复用记录并处理重复,避免无意义的循环探索。
这里的"树搜索"不要求原始地图是一棵树。即使原图只有一条 A\leftrightarrow B 连接,树搜索也可能不断产生 A\to B\to A\to B\cdots。
去重的依据是状态;更新的依据是代价。
- BFS:同一状态首次入队时已经得到最少边数,通常可直接标记已发现。
- Dijkstra/A*:见过该状态后,仍可能找到更便宜路线,需要比较 g。
- A* 使用可采纳但不一致启发式时:更便宜路线还可能要求重开 CLOSED 节点。
3.7 优先队列中的旧记录
两种常见维护方式:
- 降低键值: 直接修改队列中已有记录的优先级。
- 重复插入: 每次改善就插入新记录,取出时跳过旧记录。
采用第二种方式,可在队列记录中保存插入时的 g_{\text{saved}}。如果取出时 g_{\text{saved}}>g(n),说明已有更便宜路线,该记录已过时。
记录比较、状态哈希、邻居生成、碰撞检查都会影响实际性能。复杂度分析通常假设这些基础操作具有规定的成本。
4. 四类基础搜索策略
4.1 BFS:广度优先搜索
4.1.1 核心规则
使用 先进先出队列 ,先发现的节点先处理。搜索按边数分层:
0\text{ 步}\rightarrow1\text{ 步}\rightarrow2\text{ 步}\rightarrow\cdots
首次发现后继时,记录父节点并入队;通常就在此时标记已发现,防止同一状态重复入队。
4.1.2 能保证什么
- 在有限图中正确去重:存在路径时能找到,不存在时能在搜索耗尽后返回失败。
- 保证经过的 边数最少 。
- 当每条边代价相同且为正时,边数最少也就是总代价最小。
- 边权不同则不保证总代价最小。
例图提示: 两边的 S\to A\to G 代价为 12,四边的 S\to B\to C\to D\to G 代价为 8。BFS 优先前者,因为它的边更少。
4.1.3 适用与代价
适合等步长、等代价栅格,或寻找最少动作数。邻接表表示、基础操作为常数时间时,遍历时间为 O(|V|+|E|),额外记录通常为 O(|V|)。宽阔搜索前沿可能消耗较多内存。
复习口令
BFS 看层数,队列先进先出;等权时才把"最少边"直接当作"最小总代价"。
4.2 DFS:深度优先搜索
4.2.1 核心规则
优先深入当前分支,走到没有未处理后继时回退。通常使用 后进先出栈 或递归。
显式栈实现中,后压入的后继先被处理。因此,若希望按照 A,B,C 的顺序深入,压栈顺序往往应反过来。
4.2.2 能保证什么
- 有限图、正确标记重复状态:能判断可达性。
- 通常不保证最少边数,也不保证最小总代价。
- 无限状态空间中,即使目标可达,也可能一直沿某个无限分支深入。
它适合理解回溯、遍历、连通性及搜索结构;若要枚举多条路径,访问标记还需按具体任务设计,不能直接套用"一次访问后永久排除"。
4.2.3 不要混淆两种内存结论
- 在有限图上保存 visited 集合,DFS 额外记录通常仍为 O(|V|)。
- 教材中"DFS 比 BFS 省内存",往往是在比较搜索树的层宽与当前深度;递归栈也可能达到 O(|V|)。
遍历时间常写为 O(|V|+|E|)。深递归还需注意调用栈限制。
复习口令
DFS 先深入再回退,擅长探索结构;第一次找到的路径通常谈不上最优。
4.3 UCS/Dijkstra:按已付代价推进
4.3.1 二者是什么关系
UCS,即 Uniform Cost Search,常译为"一致代价搜索"。 名称不表示所有边权相同。
UCS 与 Dijkstra 的核心机制相同:
\text{每次选择 OPEN 中 }g(n)\text{ 最小的有效记录}
UCS 常从"到达一个目标"的搜索角度表述;Dijkstra 常从"一个起点到各节点最短距离"的角度表述。Dijkstra 也能在目标有效取出时提前终止。参考:MIT,Dijkstra
4.3.2 为什么非负边权很关键
设当前取出的节点为 u,它的 g 是候选中最小值。其他尚未确定的路线,若还要经过若干非负代价的边,便无法靠后续延伸把总代价降到更低。
因此可以证明: 节点第一次被有效取出时,g(u) 已是到它的最优代价。 这里强调取出,不能换成首次发现。
如果允许负边,一条当前看起来很贵的路线,后面可能因为负代价而变便宜,这个证明不再成立。
4.3.3 操作步骤
- 起点 g=0,放入最小优先队列。
- 取出最小 g 的有效节点。
- 若为目标,恢复路径;若求全部单源距离,则继续到候选耗尽。
- 对所有合法后继执行松弛。
- 代价改善则更新父节点与候选优先级。
它在所有候选路线间比较累计代价,不是只在当前位置挑最便宜的一条出边。
4.3.4 特性
- 有限图、非负边权:保证最优;零代价边也可以处理。
- 不需要启发式,对目标方向没有额外偏好,可能处理许多不相关节点。
- 邻接表与支持降低键值的二叉堆实现,常见时间复杂度为 O((|V|+|E|)\log|V|),额外记录为 O(|V|)。重复插入版本的队列可能占用 O(|E|) 记录,复杂度应按实际实现计算。
复习口令
Dijkstra 看累计代价;非负边权下,有效取出才能确定最优。
4.4 Greedy:贪心最佳优先搜索
4.4.1 核心规则
\text{优先选择 }h(n)\text{ 最小的候选节点}
它关心"从这里看,离目标还有多远",排序时不计较已经付出的 g。
在例图里,A 和 B 的启发式都为 6;若同分时先取 A,随后发现 h(G)=0,就可能直接返回总代价 12 的路线。
4.4.2 特性与误区
- 有时能很快找到可行路线,但没有一般性的最优保证。
- 即使 h 可采纳,Greedy 仍不一定最优; "估计不高估"不能替代对已付代价的考虑。
- 有限图、每个状态只扩展一次且正确去重时,最终能判断可达性;无限图不能直接套用这个结论。
- 局部看起来靠近目标的方向,可能被障碍物阻挡,导致绕行或无效探索。
复习口令
Greedy 只看剩余估计;看起来近,不代表整条路线便宜。
5. A* 与启发式
5.1 A*:已经花费+剩余估计
\boxed{f(n)=g(n)+h(n)}
每次优先取出 f 最小的有效候选记录。
| 候选节点 | 已付代价 g | 剩余估计 h | f=g+h |
|---|---|---|---|
| U | 3 | 10 | 13 |
| V | 6 | 2 | 8 |
Dijkstra 先考虑 U,因为 3<6;A* 先考虑 V,因为 8<13。Greedy 也先考虑 V,但它没有把此前已付代价纳入排序。
当 h(n)=0 时,A* 的排序就变成只看 g,退化为 UCS/Dijkstra。 若同分处理方式不同,节点顺序和最终选中的某条最优路径可能不同,但最优代价应一致。
5.2 可采纳性:不能高估最优剩余代价
记 h^*(n) 为从 n 到目标的真实最小剩余代价。本文使用常见非负约定:
0\le h(n)\le h^*(n),\qquad h(G)=0
满足该条件,称 h 可采纳 。
直观理解:启发式可以低估,但若想使用相应的最优性保证,就不能把真正便宜的方向估得过贵。
例如,真实绕墙距离为 20,忽略墙后的直线距离为 8。估计 8 虽不精确,但可以是合法下界;估计 25 则高估了。
关于 f 的精确理解
可采纳的 h 是剩余最优代价的下界,但由于当前 g(n) 可能尚未最优, 不能据此断言每个候选的 g(n)+h(n) 都是全局最优路径代价的下界 。最优性证明关注最优路径上仍被正确保留的候选节点。
5.3 一致性:沿一条边,估计下降不能过快
对每条有向边 u\to v,要求:
h(u)\le c(u,v)+h(v)
这称为 一致性 。例如边代价为 2,剩余估计不能从 10 一下掉到 3;否则 10\le2+3 不成立。
在无向图里,两个方向都应满足,即:
|h(u)-h(v)|\le c(u,v)
若 h(G)=0,沿任意到目标的路径逐边应用一致性,可推出可采纳性。因此:
\text{一致性}\Rightarrow\text{可采纳性}
反向不一定成立。参考:Berkeley,启发式与一致性
| 条件 | 约束对象 | 对实现的意义 |
|---|---|---|
| 可采纳 | 当前节点到目标的整体估计 | 配合正确更新、必要重开等规则,可保证最优 |
| 一致 | 每条边两端的估计关系 | 标准 A* 有效取出节点时,其 g 已最优,通常不必重开 |
5.4 为什么不一致启发式可能要求重开
以下是独立于后文主例图的微型反例,所有箭头为有向边:
S ──3──> A ──2──> G
└──1──> B ──1──> A
h(S)=0,h(A)=0,h(B)=3,h(G)=0
真实剩余代价为 h^*(A)=2、h^*(B)=3,因此这里的 h 可采纳。但边 B\to A 不满足一致性:
3>1+0
搜索过程:
- 扩展 S 后,A 的 f=3,B 的 f=4。
- 先扩展 A,得到目标候选 g(G)=5,关闭 A。
- 扩展 B,发现到 A 的更便宜路线,g(A)=1+1=2。
- 如果禁止重开 A: 改善无法正确传播,可能返回代价 5。
- 如果重开 A: 再次处理 A,将目标代价改善为 4,得到最优路线 S\to B\to A\to G。
最优性条件要成套使用
对有限非负边权图,A* 使用可采纳启发式、允许必要重开、正确松弛与去除过时记录,并在目标有效取出时结束,可保证最优。若采用"关闭后永不重开"的标准版本,应使用一致启发式。
5.5 可重开的 A* 参考伪代码
以下采用"重复插入+跳过旧记录"。队列记录保存节点、插入时的 g 和优先级;同分时使用固定规则。
对所有节点: g = +∞,parent = 空
g[S] = 0
CLOSED = 空集合
OPEN.push(S, g_saved=0, priority=h(S))
while OPEN 非空:
(u, g_saved) = OPEN.pop_min()
if g_saved > g[u]:
continue # 过时记录
if u in CLOSED:
continue # 此版本中无需重复处理的记录
if u 满足目标条件:
return 根据 parent 恢复的路径
CLOSED.add(u)
for 每个合法后继 v:
candidate = g[u] + c(u,v)
if candidate < g[v]:
g[v] = candidate
parent[v] = u
CLOSED.discard(v) # 若已关闭,允许重开
OPEN.push(v, g_saved=g[v], priority=g[v]+h(v))
return 无可达目标
真实实现还需处理数值精度、状态编码及碰撞检测。不能随意加入较大的比较容差后,仍声称结果严格最优。
5.6 四邻域与八邻域启发式
设栅格坐标差:
d_x=|x-x_G|,\qquad d_y=|y-y_G|
下表中的距离以"格"为坐标单位,D、D_2 则为对应移动的代价。
| 允许动作及代价 | 常用启发式 | 直观含义 |
|---|---|---|
| 四邻域;每个水平/竖直步代价 D | h=D(d_x+d_y) | 横向和纵向差分别补齐,即曼哈顿距离 |
| 八邻域;直走 D、斜走 D_2,且 D\le D_2\le2D | h=D\max(d_x,d_y)+(D_2-D)\min(d_x,d_y) | 尽量斜走,再补足较长方向 |
| 八邻域;直走 1、斜走 \sqrt2 | h=\max(d_x,d_y)+(\sqrt2-1)\min(d_x,d_y) | 上一行的常见特例,称 octile distance |
| 八邻域;直走与斜走均为 1 | h=\max(d_x,d_y) | 切比雪夫距离 |
| 每条边代价至少为其几何长度 | h=\sqrt{d_x^2+d_y^2},并使用匹配的空间单位 | 起点到目标的直线距离下界 |
这些栅格公式相当于在忽略障碍物的放松模型中求最短代价。障碍物只限制动作或增加绕行时,下界依旧有效;如果存在传送边、额外低价动作,原来的下界就需要重新检查。参考:Amit Patel,启发式设计
反例:八邻域却用曼哈顿距离。
从 (0,0) 到 (1,1),若斜走代价是 \sqrt2,真实最短代价为 \sqrt2,曼哈顿估计却为 2,发生高估。即使只多估了一点,也不能继续套用可采纳 A* 的严格最优保证。
5.7 启发式必须匹配单位与运动模型
5.7.1 单位匹配
g 与 h 必须具有相同代价单位。
- 距离目标:可用米表示的直线距离下界。
- 时间目标:若存在合法速度上界 v_{\max},可使用 h=d_{\text{straight}}/v_{\max}。
- 含非负额外惩罚的代价:可忽略这些惩罚建立下界,但要保证基础部分的尺度正确。
"已经耗时 5 秒+剩余距离 8 米"没有一致的代价含义。
5.7.2 下界的构造方法
- 放松约束: 暂时忽略障碍物、转弯惩罚或部分运动限制,计算更容易的乐观问题。
- 使用最小单位代价: 例如所有合法直走代价至少为 D,才可用 D 缩放相应距离下界。
- 组合多个下界: 若 h_1,h_2 都可采纳,\max(h_1,h_2) 仍可采纳;直接相加通常不行,因为可能重复计算同一部分代价。
更精确不一定总耗时更少
更紧的合法下界通常更有助于减少无关探索,但启发式自身也有计算成本。同分规则、实现及数据结构都会影响时间,不能只看扩展节点数。
5.8 Weighted A*:放宽最优性来争取效率
f_w(n)=g(n)+w\,h(n),\qquad w\ge1
- w=1:普通 A*。
- w>1:增加对剩余估计的偏重,往往更急于接近目标。
- 不再一般性保证严格最优。
一个明确的充分条件集合是: 有限图、非负边权、存在可达目标、原始 h 可采纳且目标处为 0、正确松弛并允许必要重开、以固定 w 排序,在目标有效取出时终止。 此时可得:
C_{\text{result}}\le w\,C^*
其中 C^* 是同一图、同一代价模型下的最优代价。例如 w=1.2,表示结果代价保证不超过最优值的 1.2 倍;不表示结果必定变差 20%。
这个界属于满足条件的具体算法,不能只看到代码中有 g+w h 就认定成立。增大 w 也不保证在每张地图上都更快。参考:CMU,Weighted A* 的代价界
5.9 本章速记
| 概念 | 一句话 |
|---|---|
| g | 当前已知的最好起点到达代价 |
| h | 剩余代价估计;要检查模型、单位和下界条件 |
| f=g+h | 普通 A* 的排序依据 |
| 可采纳 | 对真实最优剩余代价不高估 |
| 一致 | 每条边上,估计下降不超过边代价 |
| 重开 | 已扩展节点的 g 改善后,重新处理并传播改善 |
| 终止 | 满足相应条件后,有效取出目标 |
| h=0 | A* 退化为 UCS/Dijkstra |
| Weighted A* | 用代价质量界换取潜在效率收益,必须检查界的条件 |
6. 同一张图,手推 BFS、Dijkstra 和 A*
6.1 例图、代价与约定
本节使用一个 人为构造的无向图 。图的排版位置不表示实际距离,真正参与运算的是边权。
| 边 | 代价 | 边 | 代价 |
|---|---|---|---|
| S---A | 4 | A---G | 8 |
| S---B | 2 | B---C | 2 |
| C---D | 2 | D---G | 2 |
| S---X | 1 | X---Y | 1 |
统一约定:
- 起点为 S,目标为 G;每条边可以双向通行。
- 邻接顺序由上面的边按
S---A、A---G、S---B、B---C、C---D、D---G、S---X、X---Y顺序加入决定。因此扩展 S 时依次检查 A、B、X。 - 优先级相同时,先入队的记录先取出。
- 三种演示均在目标被 有效取出 时结束。
- BFS 在首次入队时记录父节点;Dijkstra 与 A* 在发现更小的 g 时松弛更新。
- "有效取出"不包含跳过的旧记录,但包含最后取出的目标;"扩展"特指生成邻居。目标取出后立即返回,因此本节扩展次数比有效取出次数少 1。
6.2 BFS:优先找边数少的路线
| 步骤 | 本次取出 | 处理结果 | 处理后的队列,左端先出 |
|---|---|---|---|
| 0 | --- | S 入队 | S |
| 1 | S | 依次发现 A、B、X | A,B,X |
| 2 | A | 发现 G,记录 parent[G]=A |
B,X,G |
| 3 | B | 发现 C | X,G,C |
| 4 | X | 发现 Y | G,C,Y |
| 5 | G | 目标出队,返回 | C,Y,搜索停止 |
回溯:G ← A ← S。结果为:
- 路径:
S → A → G。 - 边数:2。
- 路径代价:
4 + 8 = 12。 - 有效取出数:5;扩展数:4。
本例展示 BFS 的保证范围
BFS 找到了边数最少的路径,但这张图的边权不同,因此这条路径不一定总代价最小。BFS 的队列顺序不使用边权。
6.3 Dijkstra:优先处理累计代价小的候选节点
初始 g[S]=0,其他节点的 g 为无穷大。候选记录按 g 排序;相同 g 按入队先后排序。
| 步骤 | 有效取出 | g 值 | 发生的关键更新 |
|---|---|---|---|
| 1 | S | 0 | A=4,B=2,X=1 |
| 2 | X | 1 | Y=2 |
| 3 | B | 2 | C=4 |
| 4 | Y | 2 | 没有更便宜的到达方式 |
| 5 | A | 4 | 首次发现 G,暂记 G=12、父节点为 A |
| 6 | C | 4 | D=6 |
| 7 | D | 6 | 经 D 到 G 只需 8,更新 G=8、父节点为 D |
| 8 | G | 8 | 目标有效出队,返回 |
回溯:G ← D ← C ← B ← S。结果为:
- 路径:
S → B → C → D → G。 - 边数:4。
- 路径代价:
2 + 2 + 2 + 2 = 8。 - 有效取出数:8;扩展数:7。
为什么不能"第一次发现目标就返回"?
第 5 步发现 G 时,已知代价为 12;第 7 步才将其改善到 8。若在第 5 步直接结束,就会返回次优结果。
如果优先队列通过"重复插入"实现更新,第 7 步会新增一条 G=8 的记录,旧的 G=12 记录可能仍在堆中。本例在取出 G=8 后已经结束;如果继续处理堆,G=12 应被识别为过时记录并跳过。
6.4 A*:使用剩余代价下界引导搜索
采用以下启发式:
| 节点 | S | A | B | C | D | G | X | Y |
|---|---|---|---|---|---|---|---|---|
| h | 8 | 6 | 6 | 4 | 2 | 0 | 9 | 10 |
这些 h 值均不超过真实最小剩余代价,且对每条双向连接满足一致性。它们是为演示构造的数值,并非由图中排版距离计算。
| 步骤 | 有效取出 | g | h | f=g+h | 关键结果 |
|---|---|---|---|---|---|
| 1 | S | 0 | 8 | 8 | 生成 A:f=10;B:f=8;X:f=10 |
| 2 | B | 2 | 6 | 8 | 生成 C:g=4,f=8 |
| 3 | C | 4 | 4 | 8 | 生成 D:g=6,f=8 |
| 4 | D | 6 | 2 | 8 | 生成 G:g=8,f=8 |
| 5 | G | 8 | 0 | 8 | 返回最优路径 |
A 与 X 的 f 都是 10。在目标以 f=8 出队之前,没有必要扩展它们。
- 路径:
S → B → C → D → G。 - 路径代价:8,与 Dijkstra 相同。
- 有效取出数:5;扩展数:4。
6.5 三种算法的直接对照
| 算法 | 排序依据 | 返回代价 | 边数 | 有效取出数,含目标 | 邻居扩展次数 |
|---|---|---|---|---|---|
| BFS | 入队顺序/搜索层数 | 12 | 2 | 5 | 4 |
| Dijkstra | g | 8 | 4 | 8 | 7 |
| A* | g+h | 8 | 4 | 5 | 4 |
本例需要记住的结论:
- 边数少与总代价小是两个目标。
- 发现一个节点,不代表到达它的最佳代价已经确定。
- A* 可以通过有用的下界减少实际搜索,但本例的提速比例不是通用保证。
- 优先级相同的处理规则会改变取出顺序,也可能改变返回哪条等价最优路径;最优总代价的保证不因此改变。
6.6 两个自检实验
实验 A:将所有边权改成 1。 此时边数最少等价于总代价最小,BFS 与 Dijkstra 返回的最优代价应一致。重新构造 h,或直接令 h=0;不能直接沿用本例原有 h 值,因为代价变小可能让旧 h 高估。
实验 B:保持原边权,把所有 h 改成 0。 A* 的排序依据变成 g。若邻接顺序、入队顺序与去重规则都一致,它应表现为 Dijkstra;至少最优代价必须一致。
7. 其他经典最短路:Bellman--Ford 与 Floyd--Warshall
本章抓住两个区别
单源 / 全点对 :从一个起点出发,还是查询任意两点?
负边 / 负环 :允许负边不等于存在有限的最短代价。
7.1 Bellman--Ford:允许负边的单源最短路
问题 :从起点 s 出发,求到各节点的最短代价;边权可以为负。
直觉 :Dijkstra 每次优先处理最便宜的候选节点;Bellman--Ford 则多轮检查所有边,让更便宜的路线不断向外传播。
定义 d_k(v) 为从 s 到 v、 最多经过 k 条边 的最小代价:
d_0(s)=0,\qquad d_0(v)=+\infty\;(v\ne s)
d_k(v)=\min\leftd_{k-1}(v),\\ \\min_{(u,v)\\in E}\\{d_{k-1}(u)+c(u,v)\\}\\right
| 步骤 | 操作 | 为什么这样做 |
|---|---|---|
| 1 | 起点距离设为 0,其余设为无穷大 | 尚不知道任何到达其他节点的路线 |
| 2 | 最多进行 \lvert V\rvert-1 轮全边松弛 | 不受负环影响的最短路可取为简单路径,最多含 \lvert V\rvert-1 条边 |
| 3 | 某轮完全没有更新时可提前停止 | 所有边已满足当前最短路条件 |
| 4 | 再检查一轮是否仍有可达边能够松弛 | 若有,说明存在从起点可达的负环 |
- 时间复杂度:O(|V||E|)。
- 额外空间:常见实现为 O(|V|),另加图本身的存储。
- 公式按"上一轮快照"定义;实现也常采用原地松弛,一轮可能传播多条边, 不要再把该轮结果严格解释为最多 k 条边的结果 。
负边示例 :S\to A 代价 4,S\to B 代价 5,B\to A 代价 -3。到 A 的最优代价为 5-3=2。标准 Dijkstra 可能过早确定 A 的代价为 4;Bellman--Ford 可以继续改进。
7.2 负环影响的准确条件
负环是 总边权小于 0 的环 。绕环次数越多,总代价越低。
| 对于起点 s、目标 t | 对 s\to t 最短路的影响 |
|---|---|
| 负环不能从 s 到达 | 不影响这次单源搜索 |
| s 能到负环,但负环不能到 t | 不因此破坏到 t 的有限最短代价 |
| s 能到负环,负环还能到 t | 可重复绕环后前往 t,代价无下界,记为 -\infty |
| t 本身不可达 | 距离为 +\infty |
不要记成"图中有负环,所有最短路都不存在"
负环只影响能够经过该环到达的相应起终点对。普通 Bellman--Ford 的额外一轮可检测 起点可达 的负环;若要检测图中任意负环,可添加连接所有节点、边权为 0 的超级源点。
这里讨论的是标准最短路模型,允许路线重复经过节点。如果强行禁止重复节点,问题的定义与求解方法会发生变化。参考:Stanford:Bellman--Ford 与 Floyd--Warshall 讲义。
7.3 Floyd--Warshall:一次计算任意两点的最短路
适用问题 :仓库只有几十个重要站点,需要反复查询任意两个站点之间的距离,而不是只规划某一对起终点。
直觉 :逐个允许新的"中转站"。每增加中转站 k,检查 i\to k\to j 是否比目前的 i\to j 更便宜。
令 D^{(k)}_{ij} 表示只允许编号 1,\ldots,k 的节点作为中间节点时,i 到 j 的最小代价:
D^{(k)}{ij}=\min\left(D^{(k-1)}{ij},\ D^{(k-1)}{ik}+D^{(k-1)}{kj}\right)
- 初始化:对角线为 0,有边取边权,无边为 +\infty;重边取最小值,负自环应保留以便检测。
- 标准三重循环顺序: k 在最外层 ,内部遍历 i,j。
- 时间复杂度:O(|V|^3);使用距离矩阵的空间复杂度:O(|V|^2)。
- 允许负边。没有负环时,得到有限距离或不可达结果。
- 计算后若存在 D_{kk}<0,说明有负环;满足 i\leadsto k\leadsto j 的点对受到该负环影响,不应把其矩阵数值当作有限最短代价。
边界意识 :大型栅格往往有成千上万个节点,计算全部点对通常很昂贵;少量站点组成的拓扑图更适合考虑此方法。参考:MIT:全点对最短路与 Floyd--Warshall。
8. 机器人路径规划中的工程扩展
先记问题,再记算法名
变化后重规划 → D* Lite;规则栅格加速 → JPS;任意角度 → Theta*;多机器人消解冲突 → CBS / ECBS。
8.1 D* 与 D* Lite:复用已有结果进行重规划
仓库例子 :机器人沿原路径行驶,传感器发现前方临时堆放了货物。地图中的部分边变为不可通行,或代价升高。
直观过程 :
- 根据当前已知地图计算路径。
- 移动并接收新的环境信息。
- 更新发生变化的边代价。
- 复用原有搜索记录,修复受影响的部分,再继续前进。
| 要点 | 复习结论 |
|---|---|
| D* | Dynamic A*,通过增量修复处理行驶过程中获得的新地图信息 |
| D* Lite | 基于 LPA* 的增量启发式方法;与 Focussed D* 的导航行为相近,算法机制不同 |
| 相比重复运行 A* | 有机会减少对未受影响部分的重复计算,收益取决于变化位置、规模和地图结构 |
| 最优性的范围 | 在算法假设与正确更新条件下,针对 当前已知图 求最短路径 |
| 不自动保证的事情 | 在未知真实环境中最终实际走过的总路线全局最优;预测行人未来轨迹;车辆动力学可执行性 |
"环境动态变化"要分清含义
地图边代价发生变化后的快速修复,与预测"行人在两秒后占据哪里"是不同问题。后一种需求还要引入时间、运动预测、时空约束或局部避障机制。
参考:Koenig 与 Likhachev:D* Lite 原始论文及摘要。
8.2 JPS:利用栅格中的对称性减少扩展
仓库例子 :大面积空旷仓储区中,从左下到右上有许多等价的最短逐格走法。逐个扩展这些中间格子会产生重复工作。
核心机制 :沿某个方向扫描,把不必要的中间扩展跳过;遇到目标、需要改变搜索结构的"跳点"等位置,再进行搜索决策。
- 跳过的是搜索中的中间节点扩展 ,机器人仍要通过这些位置,整段连接必须无碰撞。
- 原始 JPS 针对 均匀地形代价、规定邻接与运动规则的栅格 ;典型八邻域中,直走与斜走采用固定对应代价。
- 在原算法假设下保留该栅格图上的最优解;障碍物形状、强制邻居等决定哪些点必须保留。
- 随意增加地形权重、转向代价、历史相关惩罚,会破坏对称性依据,不能直接沿用原剪枝规则。
- 搜索扩展变少不等于所有开销都消失,扫描本身仍要耗时。
参考:Harabor 与 Grastien:JPS 原始论文。
8.3 Theta*:在栅格上生成任意角度路径
仓库例子 :普通八邻域 A* 的路径呈水平、竖直和对角折线;空旷区域实际上可以沿一个其他角度直达。
核心机制 :扩展节点时,不只检查"当前节点 → 相邻节点",还尝试"当前节点的父节点 → 相邻节点"。若存在无碰撞视线,且代价更小,就更新父节点连接。
记忆要点 :
- 搜索仍利用栅格组织信息,输出路径不必沿栅格边行走。
- 视线检查必须验证 整条线段 ,不能只检查两端是否空闲。
- Basic Theta* 可以找到较短的任意角度路径,但 不保证连续空间中的真正最短路径 。
- 任意角度折线仍可能有急转弯; 不自动保证曲率、转弯半径与速度约束 。
- 应将"路径更短、更自然"和"轨迹可执行"分开验证。
8.4 CBS:多机器人的冲突分解搜索
仓库例子 :两台机器人分别从窄走廊两端出发,各自最短路径都经过走廊。单独无碰撞,不代表同时执行时没有冲突。
典型离散 MAPF 模型使用时间步,并允许"移动"与"等待"。常检查:
| 冲突 | 例子 |
|---|---|
| 顶点冲突 | 两台机器人在同一时间占据同一个节点 |
| 交换冲突 | 同一时间步,一台从 u\to v,另一台从 v\to u |
CBS 使用两层结构:
- 底层 :分别为机器人寻找满足已有约束的最优路径,常用带时间状态的单机器人搜索。
- 上层 :检查联合方案中的冲突,建立约束树。
- 若 A、B 在时刻 t 冲突于位置 v,产生两个分支:一个禁止 A 在该时刻占据该位置,另一个禁止 B。
- 在各分支内重新规划受约束机器人,继续选择和处理冲突,直至得到无冲突方案。
最优性必须配合目标来理解 :常见 CBS 优化的是所有机器人路径代价之和(sum of costs);最大完成时间(makespan)是另一种目标,需匹配相应的排序、下界和算法设置。
局限 :机器人多、瓶颈密集、冲突反复时,约束树可能很大。实际机器人还要把车身尺寸、运动持续时间、跟踪误差等纳入碰撞模型;只有节点层面的无冲突还不够。
8.5 ECBS:用有界次优换取求解效率
ECBS 是 CBS 的有界次优扩展,利用 focal search(焦点搜索) 在允许的代价范围内,优先考虑更有希望较快消除冲突的候选节点。
- 设容许因子为 w\ge1;满足算法的下界维护和上下层搜索条件时:
C_{\text{返回}}\le w\,C^*_{\text{MAPF}}
- 这里的代价是所选目标下的 联合方案代价 ,不等于每台机器人的独立最短路都能同时实现。
- w=1.1 表示最多容许相对最优联合方案高 10%;并不表示结果一定高 10%。
- ECBS 的保证来自上下层搜索与下界之间的配合, 不能简单理解为"把底层 A* 改成 Weighted A* 就完成了 ECBS" 。
- 更快是设计目标和常见实验表现;具体实例的运行时间仍需测量。
参考:Barer 等:CBS 的次优扩展论文;Li 等:对 CBS、ECBS 与有界次优性质的说明。
9. 工程建模与实现检查
9.1 状态是否包含决定未来所需的信息?
| 实际约束或目标 | 状态建模提示 |
|---|---|
| 仅位置决定后续动作与代价 | 可采用 (x,y) |
| 车辆朝向影响后续可达动作 | 加入 \theta,如 (x,y,\theta) |
| 转向惩罚依赖进入节点的方向 | 加入朝向或上一运动方向,避免错误合并 |
| 障碍物占用位置随时间变化 | 可能需要 (x,y,t) 或其他时空表示 |
| 加速度、制动距离取决于速度 | 可能需要速度等动态状态 |
状态合并条件
两条路线到达同一位置,只有在它们的后续可行动作和代价规律等价时,才能仅按位置比较 g 并合并。否则,"当前更便宜"不代表"后续一定更好"。
状态越完整,规模可能越大 ;应保留必要信息,再考虑离散精度与搜索加速。
9.2 几何与运动:节点安全不等于连接安全
- 机器人尺寸 :点机器人能通过的窄缝,真实车身可能通不过。圆形机器人可按半径及必要余量膨胀障碍;复杂形状需结合姿态进行配置空间或足迹碰撞检查。
- 角落穿越 :八邻域斜走应明确角落规则;在采用保守的禁止切角模型时,两侧直邻格都必须可通行。无论规则如何,都应与真实几何碰撞检查一致。
- 整段连接 :边的两个端点可行,不代表中间线段或运动原语可行。
- 运动学约束 :车辆的最小转弯半径、是否能倒车、是否能原地转向,必须体现在边或运动原语中。
- 网格分辨率 :二维地图边长分辨率减半,单元数约增至 4 倍;狭窄通道表示更精细,但计算和存储开销增大。
- 平滑后重验 :重新检查碰撞、净空、曲率及速度 / 加速度约束;原折线路径安全不代表平滑曲线安全。
这些检查源于配置空间建模:把机器人形状与姿态纳入可行状态和运动的定义。参考:LaValle:配置空间与障碍物。
9.3 代价与环境:先确认优化目标
| 检查项 | 正确做法 |
|---|---|
| 距离、时间、能耗混合 | 明确各项单位、尺度与权重,不能不加处理地把米与秒相加 |
| 避障风险惩罚 | 使用与目标一致的代价;安全禁区仍需硬约束,不能只寄希望于较大惩罚 |
| 负代价奖励 | 检查是否产生循环获利;常见 Dijkstra / A* 的非负代价保证可能不再适用 |
| 启发式匹配 | 优化时间就使用时间下界;改变运动规则、代价后重新验证 h |
| 地图更新 | 使旧路径失效或触发修复,避免继续使用已被阻挡的边 |
| 动态障碍物 | 明确预测时域、感知延迟及等待动作;区分全局重规划与局部避障职责 |
风险代价的解释
c=\alpha\,\text{距离}+\beta\,\text{时间}+\gamma\,\text{风险分数} 可表达偏好;系数承担单位换算和相对重要性。只有在具体概率模型成立时,风险分数才能按相应方式解释为碰撞概率。
9.4 搜索实现:优先检查的细节
| 常见错误 | 修正原则 |
|---|---|
| 第一次发现节点就永久锁定 | BFS 等权模型可在入队时去重;带权搜索要允许更小的 g 更新 |
| Dijkstra / A* 一看到目标邻居就结束 | 在满足各自条件时,目标作为有效的最优先候选被取出才结束 |
| A* 使用不一致启发式,却永不重开 CLOSED | 更小 g 到来时应按算法要求重开并传播改进 |
| 堆中旧记录再次被当成当前最优记录处理 | 每条记录保存插入时的 g 或版本号,取出时对照最新记录并跳过过时条目 |
| 只更新 g,未更新父节点 | 同步更新父节点,保证路径回溯与最终代价一致 |
| 无路径时仍然继续回溯 | OPEN 耗尽应返回"不可达",不能生成伪路径 |
| 没有固定同优先级处理规则 | 为复现实验设定 tie-break;它可影响路线形状和扩展数量 |
补充:使用浮点数比较时,应制定统一的数值策略;容差会影响严格最优性的解释。若采用惰性插入,堆可能同时含一个节点的多条记录,其内存不能再简单按"每节点一项"估算。
10. 算法对比、复杂度与选型
10.1 比较前先统一假设
下表采用: 有限显式图、正确去重、邻接表存储、边与启发式计算为常数时间 。记节点数为 N=|V|、边数为 M=|E|。"完备"表示有可行路径时能找到;本表中的有限图实现也能在搜索结束后报告不可达。
| 算法 | 完备性与最优性 | 典型最坏时间 | 额外空间,不含输入图 |
|---|---|---|---|
| DFS | 有限图且正确去重时完备;通常不保证最短 | O(N+M) | O(N) |
| BFS | 完备;最少边数,等正边权时也是最小总代价 | O(N+M) | O(N) |
| UCS / Dijkstra | 非负边权下完备且最优 | 支持 decrease-key 的二叉堆:O((N+M)\log N) | O(N) |
| Greedy | 有限图、正确去重并最终处理所有候选时完备;通常不最优 | 每节点最多扩展一次、可更新二叉堆:O((N+M)\log N) | 此实现为 O(N) |
| A* | 非负边权、可采纳 h、正确更新及必要重开时完备且最优 | 一致 h、无需重开、可更新二叉堆:O((N+M)\log N);一般不一致 h 可能反复扩展 | 单记录可更新队列实现为 O(N);惰性插入需另计旧条目 |
| Weighted A* | 上述有限图条件与正确维护下可求解;最优性通常放宽为有条件的 w 倍界 | 与启发式、重开和实现有关,不套用"每节点只扩展一次"结论 | 同样需考虑队列实现与旧条目 |
| Bellman--Ford | 允许负边;可计算不受可达负环影响的距离,并检测起点可达负环 | O(NM) | O(N) |
| Floyd--Warshall | 无负环时求全部点对最短距离;可检测负环 | O(N^3) | O(N^2) |
堆实现区别 :表中的 Dijkstra 界针对每个节点保留可更新堆项。惰性插入的常见表述为 O((N+M)\log(N+M)) 时间、O(N+M) 额外空间;简单图中常将其简化到与 \log N 同阶,但仍需交代实现。
10.2 为什么又会看到"搜索复杂度是指数级"?
这与上表不矛盾:
- 显式图角度 :复杂度按已经给出的节点数 N、边数 M 计。
- 隐式搜索角度 :节点按需生成。若平均分支数为 b、解深度为 d,搜索树规模可能随 b^d 增长;深度为 d 的末层可能就有 b^d 个节点。
- 状态建模角度 :位置再乘以朝向、速度、时间、多机器人组合,N 本身就可能很大。
因此,A* 在某张图上比 Dijkstra 少扩展很多节点,并不改变困难问题可能仍然很大的事实。DFS 在树搜索中可只保存当前分支与待处理分支;本笔记的 O(N) 空间则包括图搜索去重表,不能混为一个口径。
10.3 按需求选择学习与实现起点
| 当前问题 | 可优先研究 | 首先验证 |
|---|---|---|
| 只想判断是否可达、练习回溯 | DFS / BFS | 图是否正确、是否去重 |
| 每步等价,要求步数最少 | BFS | 所有动作是否确实等价 |
| 非负边权,需要可靠最短路基线 | Dijkstra | 松弛、终止与路径回溯 |
| 单起点到目标,有有效距离下界 | A* | 启发式是否可采纳 / 一致 |
| 接受可控代价偏差,希望较快求解 | Weighted A* | w 倍保证所需实现条件、实际速度收益 |
| 有负边 | Bellman--Ford | 是否存在相关负环,奖励模型是否合理 |
| 小规模图,任意点对查询频繁 | Floyd--Warshall | N^2 内存与 N^3 时间预算 |
| 地图边代价反复更新 | D* Lite | 变化范围、复用收益、当前地图有效性 |
| 标准均匀地形栅格,需要搜索加速 | JPS | 运动与角落规则是否符合剪枝证明 |
| 希望摆脱栅格边方向限制 | Theta* | 视线碰撞检查与后续曲率约束 |
| 多机器人需要时空无冲突方案 | CBS / ECBS | 冲突定义、联合优化目标与执行误差 |
这是一条工程入门路线,不表示任何算法在所有地图上都最快。选型应由地图、状态、目标、时限和实验数据共同决定。
10.4 最小实验清单:用数据巩固理解
在相同地图、运动规则、代价函数与运行环境下记录:
| 指标 | 回答的问题 |
|---|---|
| 是否成功、路径是否有效 | 找到的结果能否到达并满足约束? |
| 总代价 | 优化目标完成得怎样? |
| 扩展节点数、生成节点数 | 搜索工作量有多大?统计口径须固定 |
| 耗时与内存峰值 | 能否满足系统预算? |
| 重规划耗时 | 地图变化后多久能恢复可用方案? |
- 等权地图:BFS 与 Dijkstra 的最优代价一致。
- 非负带权图:A* 取 h=0 时,最优代价与 Dijkstra 一致;有多条最优路时,具体路线可以不同。
- 有路、无路、起点等于终点均能正确处理。
- 加入斜走后,重新检查启发式与角落规则。
- 地图变化、平滑处理后,重新验证路径可行性。
- 算法比较采用固定 tie-break 和相同统计口径。
11. 复习卡片、练习与学习安排
11.1 一页速记
| 想解决的问题 | 先回想什么 |
|---|---|
| 地图怎样进入算法? | 状态是节点,合法动作是边,动作代价是边权 |
| 怎样定义"最好"? | 路径总代价,且目标、单位与权重必须明确 |
| BFS 和 Dijkstra 有何区别? | 按边数分层 vs 按累计代价 g 排序 |
| UCS 和 Dijkstra 有何联系? | 核心排序与松弛机制一致,常见任务表述不同 |
| A* 为什么有方向感? | f=g+h,用剩余代价估计引导候选顺序 |
| A* 为什么可能不最优? | h 高估、拒绝必要重开、错误去重或过早终止等 |
| 最优路径为什么可能不能执行? | 图没有表达完整尺寸、姿态、运动或时间约束 |
| 图变化后如何利用旧结果? | 增量搜索与 D* Lite |
| 多个机器人怎样协调? | 时空状态、等待动作、冲突约束与 CBS/ECBS |
11.2 概念自测与折叠答案
Q1:BFS 一定能找到"最短路径"吗?
它保证边数最少。所有边代价相同且非负时,边数最少对应最小总代价;边权不同时,通常不保证总代价最小。还需要正确去重,并满足前文讨论的有限图等条件。
Q2:UCS 的"一致代价"是否表示每条边权相同?
否。UCS 按路径累计代价 g 从小到大选择候选,可以处理不同的非负边权。不要把它与"等权图"混为一谈。
Q3:g(n) 是否一直等于起点到 n 的真实最短代价?
否。它是当前已知的最佳到达代价。Dijkstra 有效取出某节点时,该值才在非负边权假设下确定为最优;一致启发式下标准 A* 也具有相应性质。
Q4:为什么 Dijkstra/A* 不能第一次发现目标就结束?
发现目标只得到一条候选路线;仍可能有更便宜的前缀等待扩展。本笔记例图中,Dijkstra 先发现总代价 12,后来改善为 8。标准最优搜索应在目标有效出队时按相应条件结束。
Q5:h=0 的 A* 是什么?
Dijkstra/UCS 的排序机制。若实现细节与平局规则一致,搜索过程也可一致;多个最优路径存在时,不应仅以路径节点序列是否相同判断正确性。
Q6:可采纳启发式与一致启发式是什么关系?
可采纳性限制到目标的估计不高估;一致性限制每条边上的估计变化。配合目标处 h=0,一致性蕴含可采纳性;反过来不一定成立。启发式可采纳但不一致时,图搜索需要允许 g 改善后的必要重开。
Q7:八邻域、直走 1、斜走 √2 时,曼哈顿距离可以直接保证 A* 最优吗?
通常不可以。例如从 (0,0) 到 (1,1),曼哈顿估计为 2,但斜走只需 √2,已经高估。应采用合适的八方向距离,或其他经证明合法的下界。
Q8:为了省开平方,直接把欧氏距离换成它的平方,可以吗?
不能随意替换。平方距离与距离单位不同,而且可能超过真实剩余长度,破坏可采纳性。应使用与代价模型匹配、经过验证的启发式。
Q9:Weighted A* 的 w=1.2,是否表示每次结果都比最优差 20%?
否。在满足相应理论条件和正确实现时,它给出的是不超过最优代价 1.2 倍的上界;实际结果可能就是最优解。速度也不保证随 w 单调改善。
Q10:D* Lite 是否自动解决了行人的预测避障?
否。它主要复用旧搜索信息,处理地图或边代价改变后的重规划。移动障碍物的未来轨迹、时间碰撞约束与局部避障还需另外建模或处理。
Q11:JPS 是否适合任意带权地图?
原始标准 JPS 的剪枝依赖规定的均匀代价栅格与运动规则。任意地形权重、转弯代价或不同运动约束可能破坏这些条件,不能直接照搬其最优性保证。
Q12:两台机器人的几何路径相交,一定碰撞吗?
不一定,还要看到达时间;同一位置在不同时间经过可以不冲突。但还应检查机器人尺寸,以及模型中的同一时刻占据和迎面换位等冲突。
Q13:图搜索报告"无路",是否证明真实环境绝对无路?
只能说明在当前图、动作集合和约束下未找到可达路径。过粗离散、过大的障碍膨胀或过强动作限制,都可能使真实可行路径没有被图表示出来。
11.3 计算练习
11.3.1 练习 A:检查松弛
已知 g[V]=10,g[U]=3,c(U,V)=2。是否更新 V?更新哪些记录?
参考答案
tentative=3+2=5<10,所以更新 g[V]=5 与 parent[V]=U,并更新其候选优先级。若采用重复入堆方式,插入带新 g 快照的记录,之后跳过旧记录。
11.3.2 练习 B:比较三种启发式
起点为 (0,0),目标为 (4,3),格子边长为 1。分别计算四邻域最少步数、八邻域最短长度(直走 1、斜走 √2)和直线距离。忽略障碍。
参考答案
四邻域:4+3=7。八邻域:3√2+1≈5.243。直线距离:√(4²+3²)=5。三者对应的动作模型不同;不能把 7 直接当作允许斜走问题的可采纳启发式。
11.3.3 练习 C:可采纳但不一致的反例
沿用第 5.4 节的有向反例:S→A=3、S→B=1、B→A=1、A→G=2。取 h(S)=0、h(A)=0、h(B)=3、h(G)=0。
问题:为什么这些 h 可采纳却不一致?如果 A 第一次扩展后永久禁止改善,会发生什么?
参考答案
从 B 到 G 的真实最小代价为 1+2=3,因此 h(B) 没有高估;其他节点也没有高估。但边 B→A 上,3≤1+0 不成立,所以不一致。A 首次以 g=3 出队,生成 G=5;随后 B 能将 A 改善为 g=2。若永久拒绝这个改善,会返回代价 5;允许重开 A 后,G 会被改善为 4,对应 S→B→A→G。
11.3.4 练习 D:状态是否完整
某机器人规划加入"每次转弯额外付出 3"的代价,但节点仍只有 (x,y)。这样是否总能正确使用最短路算法?
参考答案
不一定。后续转弯代价依赖到达该位置时的朝向,把不同来向合并为同一节点可能丢弃更优方案。应把相关方向信息纳入状态,例如 (x,y,heading),使后续动作和代价可由当前状态确定。
11.4 动手实现与验证顺序
| 阶段 | 任务 | 核心验收标准 |
|---|---|---|
| A | 有障碍的四邻域地图上实现 BFS | 能恢复路径、正确报告不可达、避免环路 |
| B | 将道路代价改为非负且不相同,实现 Dijkstra | 正确松弛,目标有效出队后返回,结果与小图手算一致 |
| C | 在 Dijkstra 框架上加入合法 h,形成 A* | 与 Dijkstra 最优代价一致;h=0 时退化正确 |
| D | 加入反例与边界情况 | 覆盖起点即目标、不可达、等价最优路、零代价边、过时堆条目、必要重开 |
| E | 加入机器人约束 | 路径与边均经过碰撞和运动可行性检查 |
对每个算法统一记录: 返回代价、有效取出数、邻居扩展次数、运行时间、候选队列峰值 。计数口径应固定,不能混用"发现数""取出数"和"扩展数"。
11.5 间隔复习建议
| 复习轮次 | 建议动作 | 目标 |
|---|---|---|
| 学完当天 | 不看答案,重画算法关系图;手推第 6 节 | 建立结构与操作直觉 |
| 约 1 天后 | 回答 11.2 的问题,标记答不清的条目 | 检查概念是否真正掌握 |
| 约 3 天后 | 从空白写出 Dijkstra/A* 核心流程 | 掌握松弛、优先队列与终止条件 |
| 约 7 天后 | 做 11.3 的反例题,并解释错误实现为何失败 | 掌握算法保证的前提 |
| 约 14 天后 | 将代价改为时间或加入转弯,重新设计状态与 h | 把概念迁移到工程问题 |
掌握度检查:
- 能解释图、状态、边、代价函数之间的关系。
- 能手推 BFS、Dijkstra、A*,区分发现、有效取出和扩展。
- 能说明 UCS 与 Dijkstra 的共同机制。
- 能写出松弛条件,并正确更新父节点。
- 能区分可采纳性与一致性,解释重开的作用。
- 能根据四/八邻域及代价单位选择 h。
- 能说明 D* Lite、JPS、Theta*、CBS/ECBS 分别解决什么问题。
- 能解释"图上最优"与"机器人可执行"之间的差别。
12. 术语索引与参考资料
12.1 中英文术语速查
| 中文 | 英文/缩写 | 复习定位 |
|---|---|---|
| 图、节点、边 | Graph、Vertex/Node、Edge | 第 2 节 |
| 状态、动作 | State、Action | 第 2 节 |
| 搜索前沿、候选集合 | Frontier/OPEN | 第 3 节 |
| 松弛 | Relaxation | 第 3、6 节 |
| 父节点 | Parent/Predecessor | 第 3 节 |
| 广度/深度优先搜索 | BFS/DFS | 第 4 节 |
| 一致代价搜索 | Uniform Cost Search,UCS | 第 4 节 |
| 贪心最佳优先搜索 | Greedy Best-First Search | 第 4 节 |
| 启发式 | Heuristic | 第 5 节 |
| 可采纳性 | Admissibility | 第 5 节 |
| 一致性 | Consistency | 第 5 节 |
| 节点重开 | Reopening | 第 5、11 节 |
| 完备性、最优性 | Completeness、Optimality | 第 10 节 |
| 有界次优 | Bounded Suboptimality | 第 5、8 节 |
| 增量搜索、重规划 | Incremental Search、Replanning | 第 8 节 |
| 跳点搜索 | Jump Point Search,JPS | 第 8 节 |
| 任意角度规划 | Any-Angle Path Planning | 第 8 节 |
| 多智能体路径规划 | Multi-Agent Path Finding,MAPF | 第 8 节 |
| 冲突搜索 | Conflict-Based Search,CBS | 第 8 节 |