0.引言
本文大量图片来自A* 算法简介网站,阅读完本文后点个赞,再移步至该网站看带动画版对比讲解。代码版本之后再出,本文不涉及代码,只是讲解原理。
两种算法均为最经典的寻路算法,有很强的适应性和可塑性,对于不同领域的特殊问题稍作改造均能发挥其价值。两种算法各有优劣,且听我娓娓道来。
开始之前我们需要明白几个概念。第一点对于寻路算法来说,我们一般会将地图中的重要节点标出来,我们的目的是找到“最好的”节点之间的先后访问顺序。
那么如何找到节点?
对于部分情形来说,节点是天然就有的。如果是一个开放地图,或者真实地图我们可以手动在交通岔路口或者弯度较大的地方标出节点,若是地图过大,可以将整张地图网格化,每个网格天然的就是一个节点。或者可以利用网格化思想,加入步长的概念,每一步就走1米,不多不少。这样就解决了我们的第一步。
1.Dijkstra
广度优先搜索的算法一般不考虑,或者说不知道目的地的信息,只知道从哪出发。这就导致我们只能像水波一样探索,一圈一圈的,直到扫描到目的地。
1.1 广度优先搜索
Dijkstra算法属于广度优先搜索。想要弄清楚 Dijkstra算法,先来看看纯种的广度优先搜索如何运作。
搜索是一轮一轮或者说一代一代进行的,对于广度优先来说,一轮就是一圈。
按照这个方式像水波一样扩散,直至找到目的地,立即停止算法。
1.2 Dijkstra
Dijkstra与广度优先搜索十分相似,但加入了一个算子,考虑了移动成本的搜索策略。
具体一点,每选中一个节点后(蓝色背景),会访问其周围的四个节点(绿色背景),如果是空白节点,则直接为该节点的值(值指的是从原始节点到该节点的最短路径的距离或者加权距离),如果是已经有值的节点,比较周围节点的值和本节点值+1,将两者的最小值重新赋予周围节点。
每轮新探索时,只选取成本最小且存在未被探索邻居的节点进行拓展。
我们假设绿色格子每格需要花费5步,而白色格子只需要花费1步。对比两种算法可得,不考虑权值的广度优先搜索得出来的结果显然是不如Dijkstra考虑权值所得到的结果更省步数。
但作为广度优先搜索,有天然的优势和缺点。优势在于:
①可以精确计算从出发点到地图上每一个位置所需要的最短路径;
②保证可以得到最优解。
但缺点也很明显,算法计算量太大,不适合做实时的或其他需要保证速度的场景。可以通过观察下面两图对比得出结论。
2.A*
A*算法使用前提是知道出发点和目的地两个坐标信息。介绍A*之前,需要首先介绍一下贪心算法寻路问题上的应用,也解释上图中为何贪心算法比Dijkstra快这么多。
2.1 贪心算法
在Dijkstra算法中,我们只关心当前节点到出发节点花费是多少,而未关注当前节点到目的节点的花费。贪心算法刚好与Dijkstra算法相反,只关注当前节点到目的节点的花费,但是这会引出一个问题——我们如何计算当前节点到目的节点的花费,我们并不知道未来要走哪条路,也不知道最近通向目的地的路是哪条(毕竟我们设计算法就是为了求最近的路,我们暂时当然不知道最近的路)。所以我们只能估计。一般会使用欧氏距离(就是直线距离)或者曼哈顿距离(只能沿着x轴和y轴这两条直线走的距离)来估计。
我们会预估当前哪个节点预期花费最少,忽略到已经产生的花费,这样与Dijkstra相反的思路就是我们的贪心思路。
贪心并不在任何情况下都有效,不保证找到最短距离,且偶尔会产生非常不乐观的路线,尤其是在存在障碍物时。这是因为预估花费时,一般不会把障碍物考虑在内。下图的花费我使用的是节点到目的地的曼哈顿距离。
2.2 A*
A*算法结合了贪心与Dijkstrad的优点,即考虑过去的花费,又对未来产生评估。通过权衡两者的值,选出最可能构成最优路线的节点。下图对未来的估计使用的是曼哈顿距离。可以直观看到三种算法的区别。
具体流程和前两种算法相似,每一代选出花费最少的节点,通过该节点延伸出去,找到邻居节点,计算所有邻居节点花费,要是一个节点出现两个花费,证明有两条路,显然有一条会更短,选择花费小的值。继续下一代,直到找到目标节点。