A*算法

来源:互联网 发布:state of the art知乎 编辑:程序博客网 时间:2024/05/17 01:14

A*算法:

A*(A-Star)算法是一种静态路网中求解最短路最有效的方法。

公式表示为: f(n)=g(n)+h(n),

其中f(n) 是节点n从初始点到目标点的估价函数,

g(n) 是在状态空间中从初始节点到n节点的实际代价,

h(n)是从n到目标节点最佳路径的估计代价。

保证找到最短路径(最优解的)条件,关键在于估价函数h(n)的选取:

估价值h(n)<= n到目标节点的距离实际值,这种情况下,搜索的点数多,搜索范围大,效率低。但能得到最优解。

如果 估价值>实际值, 搜索的点数少,搜索范围小,效率高,但不能保证得到最优解。

估价值与实际值越接近,估价函数取得就越好。

例如对于几何路网来说,可以取两节点间欧几理德距离(直线距离)做为估价值,即f=g(n)+sqrt((dx-nx)*(dx-nx)+(dy-ny)*(dy-ny));这样估价函数f在g值一定的情况下,会或多或少的受估价值h的制约,节点距目标点近,h值小,f值相对就小,能保证最短路的搜索向终点的方向进行。明显优于Dijstra算法的毫无无方向的向四周搜索。

conditions of heuristic

Optimistic (must be less than or equal to the real cost)

As close to the real cost as possible

主要搜索过程:

创建两个表,OPEN表保存所有已生成而未考察的节点,CLOSED表中记录已访问过的节点。

遍历当前节点的各个节点,将n节点放入CLOSE中,取n节点的子节点X,->算X的估价值->

While(OPEN!=NULL)

{

从OPEN表中取估价值f最小的节点n;

if(n节点==目标节点) break;

else

{

if(X in OPEN) 比较两个X的估价值f //注意是同一个节点的两个不同路径的估价值

if( X的估价值小于OPEN表的估价值 )

更新OPEN表中的估价值; //取最小路径的估价值

if(X in CLOSE) 比较两个X的估价值 //注意是同一个节点的两个不同路径的估价值

if( X的估价值小于CLOSE表的估价值 )

更新CLOSE表中的估价值; 把X节点放入OPEN //取最小路径的估价值

if(X not in both)

求X的估价值;

并将X插入OPEN表中; //还没有排序

}

将n节点插入CLOSE表中;

按照估价值将OPEN表中的节点排序; //实际上是比较OPEN表内节点f的大小,从最小路径的节点向下进行。

}

 

用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 因为有八个方向。如图:

列子:  先看搜索主函数:

   void AstarPathfinder::FindPath(int sx, int sy, int dx, int dy)
   {
     NODE *Node, *BestNode;
     int TileNumDest;

     //得到目标位置,作判断用
     TileNumDest = TileNum(sx, sy);

     //生成Open和Closed表
     OPEN=( NODE* )calloc(1,sizeof( NODE ));
     CLOSED=( NODE* )calloc(1,sizeof( NODE ));

     //生成起始节点,并放入Open表中
     Node=( NODE* )calloc(1,sizeof( NODE ));
     Node- >g = 0;

     //这是计算h值
     Node- >h = (dx-sx)*(dx-sx) + (dy-sy)*(dy-sy); // should really use sqrt().

     //这是计算f值,即估价值
     Node- >f = Node->g+Node->h;
     Node- >NodeNum = TileNum(dx, dy);
     Node- >x = dx;
     Node- >y = dy;

     OPEN- >NextNode=Node; // make Open List point to first node
     for (;;)
     {
       //从Open表中取得一个估价值最好的节点
       BestNode=ReturnBestNode();

       //如果该节点是目标节点就退出
       if (BestNode- >NodeNum == TileNumDest) // if we've found the end, break and finish
         break;
       //否则生成子节点
       GenerateSuccessors(BestNode,sx,sy);
     }
     PATH = BestNode;
   }

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

   void AstarPathfinder::GenerateSuccessors(NODE *BestNode, int dx, int dy)
   {
     int x, y;

     //依次生成八个方向的子节点,简单!
     // Upper-Left
     if ( FreeTile(x=BestNode- >x-TILESIZE, y=BestNode->y-TILESIZE) )
       GenerateSucc(BestNode,x,y,dx,dy);
     // Upper
     if ( FreeTile(x=BestNode- >x, y=BestNode->y-TILESIZE) )
       GenerateSucc(BestNode,x,y,dx,dy);
     // Upper-Right
     if ( FreeTile(x=BestNode- >x+TILESIZE, y=BestNode->y-TILESIZE) )
       GenerateSucc(BestNode,x,y,dx,dy);
     // Right
     if ( FreeTile(x=BestNode- >x+TILESIZE, y=BestNode->y) )
       GenerateSucc(BestNode,x,y,dx,dy);
     // Lower-Right
     if ( FreeTile(x=BestNode- >x+TILESIZE, y=BestNode->y+TILESIZE) )
       GenerateSucc(BestNode,x,y,dx,dy);
     // Lower
     if ( FreeTile(x=BestNode- >x, y=BestNode->y+TILESIZE) )
       GenerateSucc(BestNode,x,y,dx,dy);
     // Lower-Left
     if ( FreeTile(x=BestNode- >x-TILESIZE, y=BestNode->y+TILESIZE) )
       GenerateSucc(BestNode,x,y,dx,dy);
     // Left
     if ( FreeTile(x=BestNode- >x-TILESIZE, y=BestNode->y) )
       GenerateSucc(BestNode,x,y,dx,dy);
   }

  看看最重要的函数GenerateSucc:

   void AstarPathfinder::GenerateSucc(NODE *BestNode,int x, int y, int dx, int dy)
   {
     int g, TileNumS, c = 0;
     NODE *Old, *Successor;

     //计算子节点的 g 值
     g = BestNode- >g+1; // g(Successor)=g(BestNode)+cost of getting from BestNode to Successor
     TileNumS = TileNum(x,y); // identification purposes

     //子节点再Open表中吗?
     if ( (Old=CheckOPEN(TileNumS)) != NULL ) // if equal to NULL then not in OPEN list,
                         // else it returns the Node in Old
     {
       //若在
       for( c = 0; c <8; c++)
         if( BestNode- >Child[c] == NULL ) // Add Old to the list of BestNode's Children
                         // (or Successors).
          break;
         BestNode- >Child[c] = Old;
         //比较Open表中的估价值和当前的估价值(只要比较g值就可以了)
         if ( g g ) // if our new g value is Parent = BestNode;
           Old- >g = g;
           Old- >f = g + Old->h;
         }
       }
       else //在Closed表中吗?
         if ( (Old=CheckCLOSED(TileNumS)) != NULL ) // if equal to NULL then not in OPEN list
                              // else it returns the Node in Old
         {
           //若在
           for( c = 0; c <8; c++)
             if ( BestNode- >Child[c] == NULL ) // Add Old to the list of BestNode's
                              // Children (or Successors). break;
             BestNode- >Child[c] = Old;
             //比较Closed表中的估价值和当前的估价值(只要比较g值就可以了)
             if ( g g ) // if our new g value is Parent = BestNode;
               Old- >g = g;
               Old- >f = g + Old->h; //再依次更新Old的所有子节点的估价值
               PropagateDown(Old); // Since we changed the g value of Old, we need
                         // to propagate this new value downwards, i.e.
                         // do a Depth-First traversal of the tree!
             }
         }
         else //不在Open表中也不在Close表中
         {
           //生成新的节点
           Successor = ( NODE* )calloc(1,sizeof( NODE ));
           Successor- >Parent = BestNode;
           Successor- >g = g;
           Successor- >h = (x-dx)*(x-dx) + (y-dy)*(y-dy); // should do sqrt(), but since we
                                   don't really
           Successor- >f = g+Successor->h; // care about the distance but just which branch
           looks Successor- >x = x; // better this should suffice. Anyayz it's faster.
           Successor- >y = y;
           Successor- >NodeNum = TileNumS;
           //再插入Open表中,同时排序。
           Insert(Successor); // Insert Successor on OPEN list wrt f
           for( c =0; c <8; c++)
            if ( BestNode- >Child[c] == NULL ) // Add Old to the list of BestNode's
                               Children (or Successors).
            break;
           BestNode- >Child[c] = Successor;
         }
   }

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

 

原创粉丝点击