A*算法证明

原创 2015年11月19日 18:52:46


此文章目标人群为还未理解A*算法的或想更进一步了解它的人士。




术语定义。较枯燥,乃常规概念,可略过。

给定一个有向图,如果图中两个结点A、B存在边,则称结点A可达结点B。

如果图中任意一个结点A通过数个中间结点可达另外一个结点B,那么称A、B存在一条路径,定义该路径的长度为该路径经过的那些边的权值之和。

可以看出来,两个结点之间可能有不止一条路径,也可能没有路径。





给定有向图的两个结点求他们之间长度最短的路径,可以用到A*算法,算法在下面列出。

对图中某一结点N以及起始结点src、终止结点dst:
整个算法在执行过程中,会从src结点开始对图中的结点进行遍历(可能某些并不会被遍历到),算法在遍历到某个结点时,会记录下src到该结点经过的路径,该路径的长度就记为g(N)。在算法初始化时,很自然的,src结点的g值为0,其它的结点g值为∞,表示还未被遍历到。另外,某个结点可能被遍历多次,例如:

           A  

      ↗        ↘

src                               D → ….

      ↘      ↗

          B     

算法(可能)通过src->A->D这条路径遍历D之后,又折回去访问B,接着再访问D。
要谨记,结点的g值是动态生成的——在第一次遍历到它的时候生成(并且可能在以后遍历的时候更新,这是后话),不要被它吓到,如果你愿意,你可以就当它是从起始点到这一点的某条路径的长度。

h(N)表示结点N到目标点dst的“预估”长度。算法开始前,每个结点的h值都是预先确定好的。(怎么确定暂且不论)A*算法的一个思想是:路径是慢慢发现的,你发现了中途某个结点后,并不知道它离你的目标点准确有多远,只能知道个大概。不要急,这个东西在后面会慢慢解释。

g(N)和h(N)的和记为f(N)。f(n)表示了算法的遍历趋势,请铭记于心,g(N)代表这个节点离起始点的(本次遍历时经过)距离,h(N)代表这个节点离终点的(预估)距离。





有了上述概念,我们先来看看算法的形式化的步骤:
1. 建立两个容器,分别名为open和closed;
2. 将src结点添加到open中,置src结点的g值为0,置src结点的parent为null;
3. 只要open容器非空,执行以下步骤:
记open容器中f值最大的结点为cur,将cur移入closed中并将其从open中删除
如果cur就是dst结点,那么算法结束,;接下来,对cur结点的每一个相邻结点n:
如果n在close中
略过它,遍历下一个相邻结点
如果n在open中
如果cur的g值加上cur到n这条边的距离小于已存在的n的g值,那么更新已存在的n的g值为前者,更新其parent为cur;否则什么也不做。
否则
将它添加到open中去,其g值设为cur的g值加上cur到n这条边的距离,其parent设为cur



在解释算法之前,我们必须声明h的选取规则:

h*(N)表示结点N到dst的实际距离

为了使A*算法生效,我们必须令:

h(i) > h(j),如果存在一条i到j的边;递归推广一下,它的含义就是如果i能到j,那么我们给i的预估值一定要比j大

为了使A*算法找到的路径是最优路径,我们必须令:

h*(N)>=h(N);解释为我们给N的预估值一定不能超过它到dst的实际最短路径长度



接下来,我们来看看这个算法是如何奏效的。

刚开始,算法建立了两个容器,其中:

open代表已遍历的结点集合,并且它可能还可能会被遍历。

closed代表已遍历的结点集合,并且它不会再被遍历。

 我们在这里引入两个结论:

1. 当f值最小的那个结点从open加入close时,它确实不会再遍历了。

证明:由于我们每次循环都是挑选open中f值最小的结点,如果挑选出的这个结点cur会在遍历某个结点n后还被遍历,那么说明n能到cur(或者cur在n前面),那这就是说h(n)<h(cur);又因为f(cur)<f(n),那么肯定有g(cur)<g(h)。g(cur)<g(h)真的存在吗?这种情况只有没遍历n之前,已经通过其它路径遍历到了cur才出现,将n从open里拿出到close遍历其可达点时发现通过n到cur并不比之前的路径到n更快,但这说明n已经遍历过了,已经在close中而不在open中。因此,当结点从open拿出到close时,代表它不会再被遍历。

2. 对h*(n) > h(n)进行推导,可以得到:最短路径上任意点的f值都小于等于终点的f值

h(n) <= h*(n)                                       (前提)
h(n) <= h*(n) + h(dst)                          (因为h*(dst)为0,所以dst到dst的预估值h(dst)只能为0)
g(n) + h(n) <= g(n) + h*(n) + h(dst)     (不等式两边加g(n))
g(n) + h(n) <= g(dst) + h(dst)              (遍历时src到n经过的距离+n到dst的实际最短距离就是这条路径上src到dst的距离)
f(n) <= f(dst)                                          (f=g+h)

算法开始

从已经遍历的结点集合里弹出一个f最小的结点

观察这个结点的所有相邻结点,如果它的相邻结点已经在closed中,那么略过它,这是因为不允许环形图的出现;否则如果它的相邻结点不在open中,那么说明该结点还未被遍历过遍历过,那么把它加进open准备访问,它g值就是它的那个刚刚弹出的父前驱结点的g加上它俩的距离,这符合我们对g的定义;那么如果它在open的话,说明之前通过其它结点其它路径(这些结点已经“关”在closed里了)遍历到过,并且它的这个老g值就是那些路径里最短的一个,既然这样,那我们就拿当前这个路径的长度和之前那个最短的进行比较,看谁最短,就更新为g。

根据结论2,并且由于我们总是让f最小的结点进入close,那么如果存在最短路径,路径上的其它点肯定先于终点进入close;不过有可能直到终点进入了open(看好,不是close),最短路径上的点都还没进入open。比如以下情况:

 
 
   10     B    20
      ↗        ↘
A                  D
     ↘     C   ↗
   14           6
  
  
起点为A,终点为D,现用A*算法求最短路径

为了求出最短路径,令
h(A)=10(小于A到D实际最短距离)
h(B)=5(小于B到D实际最短距离)
h(C)=4(小于C到D最短距离)
h(D)=0


起始时:
open = {A(g=0 h=10)}
close = {}

1. open中A最小,加入A到close中,对A的后代B和C,把他们都加入到open中,于是
open = {B(g=10 h=5), C(g=14 h=4)}
close = {A}

2. open中B最小,加入B到close中,对于B的后代D,把它加入到open中,于是
open = {C(g=14 h=4), D(g=30 h=0)}
close = {A, B}

3. open中C最小,加入C到close中,对于B的后代D,它已经存在于open中,但是因为c->d的g值更小,于是更新D的g值为C的g值加C到D的边,于是
open = {D(g=20 h=0)}
close = {A B C}

4. 从open中取出D,算法结束


可以看到,虽然终点D在步骤2进入了open,但由于f值比它小的C一直存在——直到C进入close,它才得以进入close。

另外,就算终点提前进入了open,提前得到了g值,那条最短路径出open时也会把g值更新为最正确的。




于是,当终点进入close就宣告算法结束。



如果嫌推导不好记,我们可以这样理解上述说明的h的选取规则:

即使我们提前遍历到终点,我们也无法保证这就是最短的路径,详见上述终点提前遍历到的例子;而h(N)<h*(N)则通过f(N)<f(dst)保证了这一点——它保证,如果不是最短的路径,那么终点不会被错误弹出。

相关文章推荐

A*,那个传说中的算法

老王带你揭开传说中的面纱
  • zgwangbo
  • zgwangbo
  • 2016年07月31日 14:12
  • 12300

堪称最好的A*算法

如此好贴,不能不转!原文地址:http://dev.gameres.com/Program/Abstract/Arithmetic/AmitAStar.mht 本文版权归原作者、译者所有,我只是转贴...

堪称最好的A*算法

如此好贴,不能不转!原文地址:http://dev.gameres.com/Program/Abstract/Arithmetic/AmitAStar.mht本文版权归原作者、译者所有,我只是转贴;如...
  • b2b160
  • b2b160
  • 2009年04月08日 17:58
  • 83197

一步一步写算法(之 A*算法)

【 声明:版权所有,欢迎转载,请勿用于商业用途。  联系信箱:feixiaoxing @163.com】     在前面的博客当中,其实我们已经讨论过寻路的算法。不过,当时的示例图中,可选的...

A*算法

部分来自博客博客:http://blog.csdn.net/whu_zxl/article/details/7376007?locationNum=1 友情链接:详细A*,详细A*的翻译 A St...

A*算法的初探

马上找工作了,因为自己算是从事三维建模方面的,虽说

Delphi7高级应用开发随书源码

  • 2003年04月30日 00:00
  • 676KB
  • 下载

堪称最好的A*算法

如此好贴,不能不转!原文地址:http://dev.gameres.com/Program/Abstract/Arithmetic/AmitAStar.mht 本文版权归原作者、译者所有,我只是转贴...

A*算法

第一部分:A*算法简介     写这篇文章的初衷是应一个网友的要求,当然我也发现现在有关人工智能的中文站点实在太少,我在这里 抛砖引玉,希望大家都来热心的参与。      还是说正题,我先拿A*算...

【综合算法】A*算法

A*算法 A*算法;A*(A-Star)算法是一种静态路网中求解最短路径最有效的直接搜索方法,也是许多其他问题的常用启发式算法。注意是最有效的直接搜索算法。之后涌现了很多预处理算法(ALT,CH,...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:A*算法证明
举报原因:
原因补充:

(最多只允许输入30个字)