A*算法详解

来源:互联网 发布:手机免费进销存软件 编辑:程序博客网 时间:2024/05/18 00:48

第一部分:A*算法简介 
        A*算法在人工智能中是一种典型的启发式搜索算法。 

一、何谓启发式搜索算法:  

    在说它之前先提提状态空间搜索。状态空间搜索,如果按专业点的说法就是将问题求解过程表现为从 初始状态到目标状态寻找这个路径的过程。通俗点说,就是在解一个问题时,找到一条解题的过程可以从 求解的开始到问题的结果(好象并不通俗哦)。由于求解问题的过程中分枝有很多,主要是求解过程中求 解条件的不确定性,不完备性造成的,使得求解的路径很多这就构成了一个图,我们说这个图就是状态空 间。问题的求解实际上就是在这个图中找到一条路径可以从开始到结果。这个寻找的过程就是状态空间搜 索。  

    常用的状态空间搜索有深度优先和广度优先。广度优先是从初始状态一层一层向下找,直到找到目标 为止。深度优先是按照一定的顺序前查找完一个分支,再查找另一个分支,以至找到目标为止。这两种算 法在数据结构书中都有描述,可以参看这些书得到更详细的解释。 

    前面说的广度和深度优先搜索有一个很大的缺陷就是他们都是在一个给定的状态空间中穷举。这在状 态空间不大的情况下是很合适的算法,可是当状态空间十分大,且不预测的情况下就不可取了。他的效率 实在太低,甚至不可完成。在这里就要用到启发式搜索了。 

    启发式搜索就是在状态空间中的搜索对每一个搜索的位置进行评估,得到最好的位置,再从这个位置 进行搜索直到目标。这样可以省略大量无畏的搜索路径,提到了效率。在启发式搜索中,对位置的估价是 十分重要的。采用了不同的估价可以有不同的效果。我们先看看估价是如何表示的。 

    启发中的估价是用估价函数表示的,如: 

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

    其中f(n) 是节点n的估价函数,g(n)实在状态空间中从初始节点到n节点的实际代价,h(n)是从n到目 标节点最佳路径的估计代价。在这里主要是h(n)体现了搜索的启发信息,因为g(n)是已知的。如果说详细 点,g(n)代表了搜索的广度的优先趋势。但是当h(n) >> g(n)时,可以省略g(n),而提高效率。 

二、初识A*算法: 

    启发式搜索其实有很多的算法,比如:局部择优搜索法、最好优先搜索法等等。当然A*也是。这些算法 都使用了启发函数,但在具体的选取最佳搜索节点时的策略不同。象局部择优搜索法,就是在搜索的过程中 选取“最佳节点”后舍弃其他的兄弟节点,父亲节点,而一直得搜索下去。这种搜索的结果很明显,由于舍 弃了其他的节点,可能也把最好的节点都舍弃了,因为求解的最佳节点只是在该阶段的最佳并不一定是全局 的最佳。最好优先就聪明多了,他在搜索时,便没有舍弃节点(除非该节点是死节点),在每一步的估价中 都把当前的节点和以前的节点的估价值比较得到一个“最佳的节点”。这样可以有效的防止“最佳节点”的 丢失。那么A*算法又是一种什么样的算法呢?其实A*算法也是一种最好优先的算法。只不过要加上一些约束 条件罢了。由于在一些问题求解时,我们希望能够求解出状态空间搜索的最短路径,也就是用最快的方法求 解问题,A*就是干这种事情的!我们先下个定义,如果一个估价函数可以找出最短的路径,我们称之为可采 纳性。A*算法是一个可采纳的最好优先算法。A*算法的估价函数克表示为: 

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

    这里,f’(n)是估价函数,g’(n)是起点到终点的最短路径值,h’(n)是n到目标的最断路经的启发值。由 于这个f’(n)其实是无法预先知道的,所以我们用前面的估价函数f(n)做近似。g(n)代替g’(n),但 g(n)>=g’(n) 才可(大多数情况下都是满足的,可以不用考虑),h(n)代替h’(n),但h(n)<=h’(n)才可(这一点特别的重 要)。可以证明应用这样的估价函数是可以找到最短路径的,也就是可采纳的。我们说应用这种估价函数的 最好优先算法就是A*算法。哈!你懂了吗?肯定没懂!接着看! 

    举一个例子,其实广度优先算法就是A*算法的特例。其中g(n)是节点所在的层数,h(n)=0,这种h(n)肯 定小于h’(n),所以由前述可知广度优先算法是一种可采纳的。实际也是。当然它是一种最臭的A*算法。 

    再说一个问题,就是有关h(n)启发函数的信息性。h(n)的信息性通俗点说其实就是在估计一个节点的值 时的约束条件,如果信息越多或约束条件越多则排除的节点就越多,估价函数越好或说这个算法越好。这就 是为什么广度优先算法的那么臭的原因了,谁叫它的h(n)=0,一点启发信息都没有。但在游戏开发中由于实 时性的问题,h(n)的信息越多,它的计算量就越大,耗费的时间就越多。就应该适当的减小h(n)的信息,即 减小约束条件。但算法的准确性就差了,这里就有一个平衡的问题。可难了,这就看你的了!  
    预知A*算法的应用,请看《深入A*算法》。

第二部分:深入A*算法———浅析A*算法在搜索最短路径中的应用

一、前言 

    在这里我将对A*算法的实际应用进行一定的探讨,并且举一个有关A*算法在最短路径搜索 的例子。值得注意的是这里并不对A*的基本的概念作介绍,如果你还对A*算法不清楚的话, 请看姊妹篇《初识A*算法》。 

    这里所举的例子是参考AMIT主页中的一个源程序,你可以在AMIT的站点上下载也可以在我 的站点上下载。你使用这个源程序时,应该遵守一定的公约。 

二、A*算法的程序编写原理  


  A*算法是最好优先算法的一种。只是有一些约束条件而已。先来看看最好优先算法是如何编写的。如图有如下的状态空间:(起始位置是A,目标位置是P,字母后的数字表示节点的估价值)。
  如图有如下的状态空间:(起始位置是A,目标位置是P,字母后的数字表示节点的估价值)

图1 状态空间图


  搜索过程中设置两个表:OPEN和CLOSED。OPEN表保存了所有已生成而未考察的节点,CLOSED表中记录已访问过的节点。算法中有一步是根据估价函数重排OPEN表。这样循环中的每一步只考虑OPEN表中状态最好的节点。具体搜索过程如下:

  1)初始状态:
      OPEN=[A5];            CLOSED=[];
  2)估算A5,取得搜有子节点,并放入OPEN表中;
      OPEN=[B4, C4, D6];        CLOSED=[A5]
  3)估算B4,取得搜有子节点,并放入OPEN表中;
      OPEN=[C4, E5, F5, D6];      CLOSED=[B4, A5]
  4)估算C4;取得搜有子节点,并放入OPEN表中;
      OPEN=[H3, G4, E5, F5, D6]     CLOSED=[C4, B4, A5]
  5)估算H3,取得搜有子节点,并放入OPEN表中;
      OPEN=[O2, P3, G4, E5, F5, D6];  CLOSED=H3C4, B4, A5]
  6)估算O2,取得搜有子节点,并放入OPEN表中;
      OPEN=[P3, G4, E5, F5, D6];    CLOSED=[O2, H3, C4, B4, A5]
  7)估算P3,已得到解;

  算法的伪程序如下:

  
Best_First_Search()
  {
    Open = [起始节点];
    Closed = [];
    while ( Open表非空 )
    {
      从Open中取得一个节点X, 并从OPEN表中删除.
      if (X是目标节点)
      {
        求得路径PATH;
        返回路径PATH;
      }
      for (每一个X的子节点Y)
      {
        if( Y不在OPEN表和CLOSE表中 )
        {
          求Y的估价值;
          并将Y插入OPEN表中; 
//还没有排序
        
}
        else if( Y在OPEN表中 )
        {
          if( Y的估价值小于OPEN表的估价值 )
            更新OPEN表中的估价值;
        }
        else
//Y在CLOSE表中
        
{
          if( Y的估价值小于CLOSE表的估价值 )
          {
            更新CLOSE表中的估价值;
            从CLOSE表中移出节点, 并放入OPEN表中;
          }
        }
        将X节点插入CLOSE表中;
        按照估价值将OPEN表中的节点排序;
      }
 //end for
    } //end while
  } //end func

  伪程序出来了,写一个源程序应该不是问题了,依葫芦画瓢就可以。A*算法的程序与此是一样的,只要注意估价函数中的g(n)的h(n)约束条件就可以了。

2、用A*算法实现最短路径的搜索
  在游戏设计中,经常要涉及到最短路径的搜索,现在一个比较好的方法就是用A*算法进行设计。
  注意下面所说的都是以ClassAstar这个程序为蓝本,这个程序是一个完整的工程。里面带了一个EXE文件。可以先看看。
  A*算法的核心是估价函数f(n),它包括g(n)和h(n)两部分。g(n)是已经走过的代价,h(n)是n到目标的估计代价。在这个例子中g(n)表示在状态空间从起始节点到n节点的深度,h(n)表示n节点所在地图的位置到目标位置的直线距离。一个是状态空间,一个是实际的地图,不要搞错了。再详细点说,有一个物体A,在地图上的坐标是(xa,ya),A所要到达的目标b的坐标是(xb,yb)。则开始搜索时,设置一个起始节点1,生成八个子节点2 - 9 因为有八个方向。如图:

图2 节点图

  先看搜索主函数:


  再看看生成子节点函数 GenerateSuccessors:

  


  看看最重要的函数GenerateSucc:

  


 仔细看看这个程序,会发现这个程序和前面说的伪程序有一些不同,在GenerateSucc函数中,当子节点在Closed表中时,没有将子节点从Closed表中删除并放入Open表中。而是直接的重新的计算该节点的所有子节点的估价值(用PropagateDown函数)。这样可以快一些。另当子节点在Open表和Closed表中时,重新的计算估价值后,没有重新的对Open表中的节点排序。
原创粉丝点击