一、Dijkstra算法
1.与BFS区别
BFS维护一个队列(queue)弹出节点为最浅层级
Dijkstra弹出节点具备最小函数值g(n):从起点开始到n这个节点路上累计的cost总和
2.特点
(1)g(n)表示cost的总和
(2)当弹出的节点为n时,在拓展时,对于n节点所有的邻居节点m,如果m节点未被拓展,则检查m节点的cost g(m),g(m)可不可以通过n进行下降,可以理解为m自身的cost为g(m),如果设定为n走向m时可以将m整体的cost降低,则把cost更新
(3)dijkstra算法具备最优性保证
算法运行过程中,图中任何被拓展过的节点,可以保证说里边存储的G值是最小的从start到n的路径累计cost
二、Dijkstra算法流程
1.流程
(1)维护一个优先级队列(priority queue)去对所有的节点的g值进行排序,弹出具有最小g值的节点
(2)初始化g值,起点为0,未被发现的节点为∞大
(3)进入循环
如果队列为空,则返回,即未找到节点为死路
弹出g值最小的节点n
标记n被拓展过的节点(close list)
如果n是目标,则成功
对于所有n的邻居节点m
如果m的g值∞大,则为未被发现的节点
则m的g值为n的+cost
将m压入queue
如果g(m)>g(n)+cnm
更新g(m)= g(n)+cnm
结束循环
2.优缺点
(1)优点:完备、最优
(2)dijkstra的拓展仍然是均匀拓展的,是一种穷举的方式盲目推广,当每个边的cost为1时,成为BFS
三、A*算法
1.定义
(1)A*算法的实质为dijkstra算法与贪心算法的结合
(2)累计cost:g(n)
从当前节点到目标节点估计的代价:h(n)
计算最小的f(n)=g(n)+h(n)弹出
2.流程
注意:
h(n)一开始不知,在拓展的过程中去计算(例如欧氏距离)
对于A*算法,更新g(m)同时还需要重新计算f(m)
3.实例
4.A*算法最优性讨论
结论:
上图中,从整体g+h角度s直接到g的代价为5,上边代价为10,结果与真实路径代价相反
原因:
h的估计不合理,h的估计应该小于实际的真实路径代价
5.如何设计合适的启发式函数(admissible heuristics)
原则:
对于任何节点,到达终点估计的所花费的代价 小于等于 真实代价
欧氏距离(√)与曼哈顿距离设计 (×)
四、Dijkstra算法与A*算法对比
dijkstra算法为均匀扩散,而启发式函数的加入使得a*算法具有对目标贪心的引导性
五、次优解 weighted a*算法
通过牺牲部分最优性能换取求解速度,系数代表搜索向目标前进的权重
系数=1,为原始a*算法
系数->∞,则g忽略,为贪心算法
系数->0,则h忽略,为dijkstra算法
六、工程注意事项
1.怎么将grid map转化为graph:定义连接方式,四连接或者八连接
2.如何设计最佳启发式函数
现象:原始欧式距离作为启发式函数:搜索拓展过的节点过多,效率低下,目的性过低
原因:欧式距离h(n)远小于h*(n),小非常多,导致两者之间有差距
对于一个高度结构化的地图,有结构化移动的规则,则最短路径是可以计算的,可以理解为一个蚂蚁爬行最短路线问题,所以在结构化的地图中,可以直接计算出h*(n)作为启发式函数
3.Tie breaker
原因:许多路径都具有相同的f,并不知道选哪个,没有倾向性会拓展到哪一侧,导致拓展低效
解决方法:
对于相同的f,修改其h
tie breaker核心思想
思路:
1.当具有相同的f时,对比其h,h小的放在前边
2.在代价计算中加一个倾向性,例如再加一个离start与goal连线之间的距离为cross cost
核心都为打破对称性