日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

深入A*算法

發(fā)布時(shí)間:2024/4/11 编程问答 50 豆豆
生活随笔 收集整理的這篇文章主要介紹了 深入A*算法 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.



一、前言

  在這里我將對A*算法的實(shí)際應(yīng)用進(jìn)行一定的探討,并且舉一個(gè)有關(guān)A*算法在最短路徑搜索的例子。值得注意的是這里并不對A*的基本的概念作介紹,如果你還對A*算法不清楚的話,請看姊妹篇《初識(shí)A*算法》。

  這里所舉的例子是參考AMIT主頁中的一個(gè)源程序,使用這個(gè)源程序時(shí),應(yīng)該遵守一定的公約。

二、A*算法的程序編寫原理

  我在《初識(shí)A*算法》中說過,A*算法是最好優(yōu)先算法的一種。只是有一些約束條件而已。我們先來看看最好優(yōu)先算法是如何編寫的吧。

  如圖有如下的狀態(tài)空間:(起始位置是A,目標(biāo)位置是P,字母后的數(shù)字表示節(jié)點(diǎn)的估價(jià)值)

  搜索過程中設(shè)置兩個(gè)表:OPEN和CLOSED。OPEN表保存了所有已生成而未考察的節(jié)點(diǎn),CLOSED表中記錄已訪問過的節(jié)點(diǎn)。算法中有一步是根據(jù)估價(jià)函數(shù)重排OPEN表。這樣循環(huán)中的每一步只考慮OPEN表中狀態(tài)最好的節(jié)點(diǎn)。具體搜索過程如下:

1)初始狀態(tài):                
 OPEN=[A5];CLOSED=[];
2)估算A5,取得搜有子節(jié)點(diǎn),并放入OPEN表中;
 OPEN=[B4,C4,D6];CLOSED=[A5]
3)估算B4,取得搜有子節(jié)點(diǎn),并放入OPEN表中;
 OPEN=[C4,E5,F5,D6];CLOSED=[B4,A5]
4)估算C4;取得搜有子節(jié)點(diǎn),并放入OPEN表中;
 OPEN=[H3,G4,E5,F5,D6];CLOSED=[C4,B4,A5]
5)估算H3,取得搜有子節(jié)點(diǎn),并放入OPEN表中;
 OPEN=[O2,P3,G4,E5,F5,D6];CLOSED=[H3,C4,B4,A5]
6)估算O2,取得搜有子節(jié)點(diǎn),并放入OPEN表中;
 OPEN=[P3,G4,E5,F5,D6];CLOSED=[O2,H3,C4,B4,A5]
7)估算P3,已得到解;

  看了具體的過程,再看看偽程序吧。算法的偽程序如下:

Best_First_Search()
{
 Open = [起始節(jié)點(diǎn)];
 Closed = [];
 while (Open表非空)
 {
  從Open中取得一個(gè)節(jié)點(diǎn)X,并從OPEN表中刪除。
  if (X是目標(biāo)節(jié)點(diǎn))
  {
   求得路徑PATH;
   返回路徑PATH;
  }
  for (每一個(gè)X的子節(jié)點(diǎn)Y)
  {
   if (Y不在OPEN表和CLOSE表中)
   {
    求Y的估價(jià)值;
    并將Y插入OPEN表中;
   }
   //還沒有排序
   else if (Y在OPEN表中)
   {
    if (Y的估價(jià)值小于OPEN表的估價(jià)值)
     更新OPEN表中的估價(jià)值;
   }
   else //Y在CLOSE表中
   {
    if (Y的估價(jià)值小于CLOSE表的估價(jià)值)
    {
     更新CLOSE表中的估價(jià)值;
     從CLOSE表中移出節(jié)點(diǎn),并放入OPEN表中;
    }
   }
   將X節(jié)點(diǎn)插入CLOSE表中;
   按照估價(jià)值將OPEN表中的節(jié)點(diǎn)排序;
  }//end for
 }//end while
}//end func

  啊!偽程序出來了,寫一個(gè)源程序應(yīng)該不是問題了,依葫蘆畫瓢就可以。A*算法的程序與此是一樣的,只要注意估價(jià)函數(shù)中的g(n)的h(n)約束條件就可以了。不清楚的可以看看《初識(shí)A*算法》。好了,我們可以進(jìn)入另一個(gè)重要的話題,用A*算法實(shí)現(xiàn)最短路徑的搜索。在此之前你最好認(rèn)真的理解前面的算法。不清楚可以找我。我的Email在文章尾。

三、用A*算法實(shí)現(xiàn)最短路徑的搜索

  在游戲設(shè)計(jì)中,經(jīng)常要涉及到最短路徑的搜索,現(xiàn)在一個(gè)比較好的方法就是用A*算法進(jìn)行設(shè)計(jì)。他的好處我們就不用管了,反正就是好!^_*

  注意下面所說的都是以ClassAstar這個(gè)程序?yàn)樗{(lán)本,你可以在這里下載這個(gè)程序。這個(gè)程序是一個(gè)完整的工程。里面帶了一個(gè)EXE文件。可以先看看。

  先復(fù)習(xí)一下,A*算法的核心是估價(jià)函數(shù)f(n),它包括g(n)和h(n)兩部分。g(n)是已經(jīng)走過的代價(jià),h(n)是n到目標(biāo)的估計(jì)代價(jià)。在這個(gè)例子中g(shù)(n)表示在狀態(tài)空間從起始節(jié)點(diǎn)到n節(jié)點(diǎn)的深度,h(n)表示n節(jié)點(diǎn)所在地圖的位置到目標(biāo)位置的直線距離。啊!一個(gè)是狀態(tài)空間,一個(gè)是實(shí)際的地圖,不要搞錯(cuò)了。再詳細(xì)點(diǎn)說,有一個(gè)物體A,在地圖上的坐標(biāo)是(xa,ya),A所要到達(dá)的目標(biāo)b的坐標(biāo)是(xb,yb)。則開始搜索時(shí),設(shè)置一個(gè)起始節(jié)點(diǎn)1,生成八個(gè)子節(jié)點(diǎn)2- 9 因?yàn)橛邪藗€(gè)方向。如圖:

  仔細(xì)看看節(jié)點(diǎn)1、9、17的g(n)和h(n)是怎么計(jì)算的。現(xiàn)在應(yīng)該知道了下面程序中的f(n)是如何計(jì)算的吧。開始講解源程序了。其實(shí)這個(gè)程序是一個(gè)很典型的教科書似的程序,也就是說只要你看懂了上面的偽程序,這個(gè)程序是十分容易理解的。不過他和上面的偽程序有一些的不同,我在后面會(huì)提出來。

  先看搜索主函數(shù):

void AstarPathfinder::FindPath(int sx, int sy, int dx, int dy) {NODE *Node, *BestNode;int TileNumDest;//得到目標(biāo)位置,作判斷用TileNumDest = TileNum(sx, sy);//生成Open和Closed表OPEN = ( NODE* )calloc(1,sizeof( NODE ));CLOSED=( NODE* )calloc(1,sizeof( NODE ));//生成起始節(jié)點(diǎn),并放入Open表中Node=( NODE* )calloc(1,sizeof( NODE ));Node->g = 0;//這是計(jì)算h值// should really use sqrt().Node->h = (dx-sx)*(dx-sx) + (dy-sy)*(dy-sy);//這是計(jì)算f值,即估價(jià)值Node->f = Node->g+Node->h;Node->NodeNum = TileNum(dx, dy);Node->x = dx; Node->y = dy;// make Open List point to first nodeOPEN->NextNode=Node;for (;;){//從Open表中取得一個(gè)估價(jià)值最好的節(jié)點(diǎn)BestNode=ReturnBestNode();//如果該節(jié)點(diǎn)是目標(biāo)節(jié)點(diǎn)就退出// if we've found the end, break and finish break;if (BestNode->NodeNum == TileNumDest)//否則生成子節(jié)點(diǎn)GenerateSuccessors(BestNode,sx,sy);}PATH = BestNode; }

  再看看生成子節(jié)點(diǎn)函數(shù):

void AstarPathfinder::GenerateSuccessors(NODE *BestNode, int dx, int dy) {int x, y;//哦!依次生成八個(gè)方向的子節(jié)點(diǎn),簡單!// Upper-Leftif ( FreeTile(x=BestNode->x-TILESIZE, y=BestNode->y-TILESIZE) )GenerateSucc(BestNode,x,y,dx,dy);// Upperif ( FreeTile(x=BestNode->x, y=BestNode->y-TILESIZE) )GenerateSucc(BestNode,x,y,dx,dy);// Upper-Rightif ( FreeTile(x=BestNode->x+TILESIZE, y=BestNode->y-TILESIZE) )GenerateSucc(BestNode,x,y,dx,dy);// Rightif ( FreeTile(x=BestNode->x+TILESIZE, y=BestNode->y) )GenerateSucc(BestNode,x,y,dx,dy);// Lower-Rightif ( FreeTile(x=BestNode->x+TILESIZE, y=BestNode->y+TILESIZE) )GenerateSucc(BestNode,x,y,dx,dy);// Lowerif ( FreeTile(x=BestNode->x, y=BestNode->y+TILESIZE) )GenerateSucc(BestNode,x,y,dx,dy);// Lower-Leftif ( FreeTile(x=BestNode->x-TILESIZE, y=BestNode->y+TILESIZE) )GenerateSucc(BestNode,x,y,dx,dy);// Leftif ( FreeTile(x=BestNode->x-TILESIZE, y=BestNode->y) )GenerateSucc(BestNode,x,y,dx,dy); }

  看看最重要的函數(shù):

void AstarPathfinder::GenerateSucc(NODE *BestNode,int x, int y, int dx, int dy) {int g, TileNumS, c = 0;NODE *Old, *Successor;//計(jì)算子節(jié)點(diǎn)的 g 值// g(Successor)=g(BestNode)+cost of getting from BestNode to Successorg = BestNode->g+1;// identification purposesTileNumS = TileNum(x,y);//子節(jié)點(diǎn)再Open表中嗎?// if equal to NULL then not in OPEN list, else it returns the Node in Oldif ( (Old=CheckOPEN(TileNumS)) != NULL ){//若在for( c = 0; c < 8; c++)// Add Old to the list of BestNode's Children (or Successors).if( BestNode->Child[c] == NULL )break;BestNode->Child[c] = Old;//比較Open表中的估價(jià)值和當(dāng)前的估價(jià)值(只要比較g值就可以了)// if our new g value is < Old's then reset Old's parent to point to BestNodeif ( g < Old->g ){//當(dāng)前的估價(jià)值小就更新Open表中的估價(jià)值Old->Parent = BestNode;Old->g = g;Old->f = g + Old->h;}}else//在Closed表中嗎?// if equal to NULL then not in OPEN list, else it returns the Node in Oldif ( (Old=CheckCLOSED(TileNumS)) != NULL ){//若在for( c = 0; c< 8; c++)// Add Old to the list of BestNode's Children (or Successors).if ( BestNode->Child[c] == NULL )break;BestNode->Child[c] = Old;//比較Closed表中的估價(jià)值和當(dāng)前的估價(jià)值(只要比較g值就可以了)// if our new g value is < Old's then reset Old's parent to point to BestNodeif ( g < Old->g ){//當(dāng)前的估價(jià)值小就更新Closed表中的估價(jià)值Old->Parent = BestNode;Old->g = g;Old->f = g + Old->h;//再依次更新Old的所有子節(jié)點(diǎn)的估價(jià)值// 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!PropagateDown(Old);}}//不在Open表中也不在Close表中else{//生成新的節(jié)點(diǎn)Successor = ( NODE* )calloc(1,sizeof( NODE ));Successor->Parent = BestNode;Successor->g = g;// should do sqrt(), but since we don't reallySuccessor->h = (x-dx)*(x-dx) + (y-dy)*(y-dy);// care about the distance but just which branch looksSuccessor->f = g+Successor->h;// better this should suffice. Anyayz it's faster.Successor->x = x;Successor->y = y;Successor->NodeNum = TileNumS;//再插入Open表中,同時(shí)排序。// Insert Successor on OPEN list wrt fInsert(Successor);for( c =0; c < 8; c++)// Add Old to the list of BestNode's Children (or Successors).if ( BestNode->Child[c] == NULL )break;BestNode->Child[c] = Successor;} }

  哈哈!A*算法我懂了!當(dāng)然,我希望你有這樣的感覺!不過我還要再說幾句。仔細(xì)看看這個(gè)程序,你會(huì)發(fā)現(xiàn),這個(gè)程序和我前面說的偽程序有一些不同,在GenerateSucc函數(shù)中,當(dāng)子節(jié)點(diǎn)在Closed表中時(shí),沒有將子節(jié)點(diǎn)從Closed表中刪除并放入Open表中。而是直接的重新的計(jì)算該節(jié)點(diǎn)的所有子節(jié)點(diǎn)的估價(jià)值(用PropagateDown函數(shù))。這樣可以快一些!另當(dāng)子節(jié)點(diǎn)在Open表和Closed表中時(shí),重新的計(jì)算估價(jià)值后,沒有重新的對Open表中的節(jié)點(diǎn)排序,我有些想不通,為什么不排呢?:-(,會(huì)不會(huì)是一個(gè)小小的BUG。你知道告訴我好嗎?

  好了!主要的內(nèi)容都講完了,還是完整仔細(xì)的看看源程序吧!希望我所的對你有一點(diǎn)幫助,一點(diǎn)點(diǎn)也可以。如果你對文章中的觀點(diǎn)有異議或有更好的解釋都告訴我。我的email在文章最后!

總結(jié)

以上是生活随笔為你收集整理的深入A*算法的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。