A*算法总结

A*是一种启发式搜索,一种有序搜索,它之所以特殊完全是在它的估价函数上,如果我要求的是从初始结点到目的结点的一个最短路径(或加权代价)的可行解, 那对于一个还不是目标结点的结点,我对它的评价就要从两个方面评价:第一,离目标结点有多近,越近越好;第二,离起始结点有多远,越近越好。记号 [a,b]是表示结点a到结点b的实际最短路径代价。设起始结点为S,当前结点为n,目标结点为G,于是n的实际代价应该是f*(n)=g*(n)+h* (n),其中g*(n)=[S,n],h*(n)=[n,G],对于是g*(n)是比较容易得到的,在搜索的过程中我们可以按搜索的顺序对它进行累积计 算,当然按BFS和DFS的不同,我们对它的估价g(n)可以满足g(n)>=g*(n),大多可以是相等的。但是对于h*(n)我们却了解得非常 少,目标结点正是要搜索的目的,我们是不知道在哪,就更不知道从n到目标结点的路径代价,但是或多或少我们还是可以估计的,记估价函数f(n)=g(n) +h(n)。

我们说如果在一般的图搜索算法中应用了上面的估价函数对OPEN表进行排序的,就称A算法。在A算法之上,如果加上一个条件,对于所有的结点x,都有h(x)<=h*(x),那就称为A*算法。如果取h(n)=0同样是A*算法,这样它就退化成了有序算法。

A*算法是否成功,也就是说是否在效率上胜过蛮力搜索算法,就在于h(n)的选取,它不能大于实际的h*(n),要保守一点,但越接近h*(n)给我们的启发性就越大,是一个难把握的东西。

过程:
1. 将开始节点放入开放列表(开始节点的F和G值都视为0);
2. 重复一下步骤:
i. 在开放列表中查找具有最小F值的节点,并把查找到的节点作为当前节点;
ii. 把当前节点从开放列表删除, 加入到封闭列表;
iii. 对当前节点相邻的每一个节点依次执行以下步骤:
1. 如果该相邻节点不可通行或者该相邻节点已经在封闭列表中,则什么操作也不执行,继续检验下一个节点;
2. 如果该相邻节点不在开放列表中,则将该节点添加到开放列表中, 并将该相邻节点的父节点设为当前节点,同时保存该相邻节点的G和F值;
3. 如果该相邻节点在开放列表中, 则判断若经由当前节点到达该相邻节点的G值是否小于原来保存的G值,若小于,则将该相邻节点的父节点设为当前节点,并重新设置该相邻节点的G和F值.
iv. 循环结束条件:
当终点节点被加入到开放列表作为待检验节点时, 表示路径被找到,此时应终止循环;
或者当开放列表为空,表明已无可以添加的新节点,而已检验的节点中没有终点节点则意味着路径无法被找到,此时也结束循环;
3. 从终点节点开始沿父节点遍历, 并保存整个遍历到的节点坐标,遍历所得的节点就是最后得到的路径;

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