移动机器人学习1: a*算法的理解

感谢路径规划之a*这篇文章的步步引导,在此基础上,记下自己对a*算法的理解,以及启发式函数的理解。


1.广度优先搜索与Dijkstra:

设想一下算法在一个地图中寻找另外一点,最简单的方式就是从出发点开始,一圈圈向外膨胀。从起点开始,首先遍历起点周围邻近的点,然后再遍历已经遍历过的点邻近的点,逐步的向外扩散,直到找到终点。这便是广度有限算法bfs。这个过程像在火山的岩浆蔓延,逐渐占满空间直到达到目标点。然后从目标点回溯,得到最短路径。

在网格中移动,移动都周围的格子的代价都是走一步,是相同的。但是很多情况并非一样,比如在地图中,a城市到相邻的b、c城市距离并非一样。在移动代价不同的时候,就需要Dijkstra了。

在Dijkstra算法中,需要计算每一个节点距离起点的总移动代价。同时,还需要一个优先队列结构。对于所有待遍历的节点,放入优先队列中会按照代价进行排序。在算法运行的过程中,每次都从优先队列中选出代价最小的作为下一个遍历的节点。直到到达终点为止。

如果不考虑节点移动代价差异的广度优先搜索与Dijkstra算法,它们的结果将变得一致。

对于移动代价相同的情况,使用bfs这种方法显然是暴力的,做了很多无用功,它遍历了自身周围的全部点,而非优先选择某一些。这就引出了最佳优先搜索。

2.最佳优先搜索:

原理很简单,使用一个优先队列,以到达目标点的距离大小作为比较值。在添加了节点周边的点之后,优先选择距离目标点近的点。换句话说,原来bfs是一圈圈扩大,现在则是优先向着目标点走去。这可以大大加快路径的搜索速度。

但是缺点也很明显,如果起点与目标点之间存在障碍物,最佳优先搜索找到的就可能不是最优路径。打一个比方:小明在四合院中央要到四合院背后,眼前有两条路,从前门走,再绕到院子后面;直接向着院子后方走,但是要绕过一大堆的家具。在家具中经过的时间大于绕路走,但是最佳优先搜索只会指导向前走。

3.a*算法:

可以说,a算法是综合了以上的算法优点。a算法通过下面这个函数来计算每个节点的优先级:

f(n) = g(n) + h(n)

这里的g(n)可以看做bfs的移动代价,h(n)则是最佳优先搜索的代价。

  1. f(n)是节点n的综合优先级。当我们选择下一个要遍历的节点时,我们总会选取综合优先级最高(值最小)的节点。
  2. g(n) 是节点n距离起点的代价。
  3. h(n)是节点n距离终点的预计代价,这也就是a*算法的启发函数。关于启发函数我们在下面详细讲解。

a*算法在运算过程中,每次从优先队列中选取f(n)值最小(优先级最高)的节点作为下一个待遍历的节点。
另外,a*算法使用两个集合来表示待遍历的节点,与已经遍历过的节点,这通常称之为open_set和close_set

 * 初始化open_set和close_set;
 * 将起点加入open_set中,并设置优先级为0(优先级最高);
 * 如果open_set不为空,则从open_set中选取优先级最高的节点n:
    * 如果节点n为终点,则:
        * 从终点开始逐步追踪parent节点,一直达到起点;
        * 返回找到的结果路径,算法结束;
    * 如果节点n不是终点,则:
        * 将节点n从open_set中删除,并加入close_set中;
        * 遍历节点n所有的邻近节点:
            * 如果邻近节点m在close_set中,则:
                * 跳过,选取下一个邻近节点
            * 如果邻近节点m也不在open_set中,则:
                * 设置节点m的parent为节点n
                * 计算节点m的优先级
                * 将节点m加入open_set中

启发函数
上面已经提到,启发函数会影响A*算法的行为。

  • 在极端情况下,当启发函数h(n)始终为0,则将由g(n)决定节点的优先级,此时算法就退化成了Dijkstra算法。
  • 如果h(n)始终小于等于节点n到终点的代价,则a*算法保证一定能够找到最短路径。但是当h(n)的值越小,算法将遍历越多的节点,也就导致算法越慢。
  • 如果h(n)完全等于节点n到终点的代价,则a*算法将找到最佳路径,并且速度很快。可惜的是,并非所有场景下都能做到这一点。因为在没有达到终点之前,我们很难确切算出距离终点还有多远。
  • 如果h(n)的值比节点n到终点的代价要大,则a*算法不能保证找到最短路径,不过此时会很快。
    在另外一个极端情况下,如果h()n相较于g(n)大很多,则此时只有h(n)产生效果,这也就变成了最佳优先搜索。

由上面这些信息我们可以知道,通过调节启发函数我们可以控制算法的速度和精确度。因为在一些情况,我们可能未必需要最短路径,而是希望能够尽快找到一个路径即可。这也是a*算法比较灵活的地方。

对于网格形式的图,有以下这些启发函数可以使用:

  • 如果图形中只允许朝上下左右四个方向移动,则可以使用曼哈顿距离(Manhattan distance)。
  • 如果图形中允许朝八个方向移动,则可以使用对角距离。
  • 如果图形中允许朝任何方向移动,则可以使用欧几里得距离(Euclidean distance)。
a*总结

总而言之,就是如何在set中选择下一个待遍历的节点。在bfs中按谁先进入set选谁,最佳优先搜索按谁离得目标近选谁,a*则是用它们两的综合选择。另外一方面,启发函数十分重要,决定了速度与精度。

4.发式函数的理解

启发函数是一种思想,它给算法选择增加了一个度量,在算法增加信息,在不知道如何选择下一个呆遍历的节点时候,指导算法选择。

换言之,bfs并没有充分利用好全部的已知信息,没有将目标点位置的信息利用。甚至a*也没有,是不是甚至可以创造一个启发函数H_1(),它描述了目标点距离障碍物的距离或者方向,当节点到达障碍物附近时候,指导下一个节点的选择。只不过这样的启发函数带来的算法复杂度在效率上是否划算。

在优达学城的自动驾驶入门课中,学习到了,我们还可以利用多个启发函数,比如在H_1()``H_2()都未大于真实代价的情况下,我们可以:

H()  =  max(  H_1(),   H_2()  )

这样是不是更加机智

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值