• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            A*算法入門

             

            在看下面這篇文章之前,先介紹幾個理論知識,有助于理解A*算法。

             

            啟發(fā)式搜索:啟發(fā)式搜索就是在狀態(tài)空間中的搜索對每一個搜索的位置進行評估,得到最好的位置,再從這個位置進行搜索直到目標。這樣可以省略大量無畏的搜索路徑,提到了效率。在啟發(fā)式搜索中,對位置的估價是十分重要的。采用了不同的估價可以有不同的效果。

            估價函數(shù):從當前節(jié)點移動到目標節(jié)點的預(yù)估費用;這個估計就是啟發(fā)式的。在尋路問題和迷宮問題中,我們通常用曼哈頓(manhattan)估價函數(shù)(下文有介紹)預(yù)估費用。

            A*算法與BFS:可以這樣說,BFSA*算法的一個特例。對于一個BFS算法,從當前節(jié)點擴展出來的每一個節(jié)點(如果沒有被訪問過的話)都要放進隊列進行進一步擴展。也就是說BFS的估計函數(shù)h永遠等于0,沒有一點啟發(fā)式的信息,可以認為BFS是“最爛的”A*算法。

            選取最小估價:如果學過數(shù)據(jù)結(jié)構(gòu)的話,應(yīng)該可以知道,對于每次都要選取最小估價的節(jié)點,應(yīng)該用到最小優(yōu)先級隊列(也叫最小二叉堆)。在C++STL里有現(xiàn)成的數(shù)據(jù)結(jié)構(gòu)priority_queue,可以直接使用。當然不要忘了重載自定義節(jié)點的比較操作符。

            A*算法的特點:A*算法在理論上是時間最優(yōu)的,但是也有缺點:它的空間增長是指數(shù)級別的。

            IDA*算法:這種算法被稱為迭代加深A*算法,可以有效的解決A*空間增長帶來的問題,甚至可以不用到優(yōu)先級隊列。如果要知道詳細:google一下。


             

            A*尋路初探(轉(zhuǎn)載)

             

            作者:Patrick Lester

            譯者:Panic2005

             

            譯者序:很久以前就知道了A*算法,但是從未認真讀過相關(guān)的文章,也沒有看過代碼,只是腦子里有個模糊的概念。這次決定從頭開始,研究一下這個被人推崇備至的簡單方法,作為學習人工智能的開始。

            這篇文章非常知名,國內(nèi)應(yīng)該有不少人翻譯過它,我沒有查找,覺得翻譯本身也是對自身英文水平的鍛煉。經(jīng)過努力,終于完成了文檔,也明白的A*算法的原理。毫無疑問,作者用形象的描述,簡潔詼諧的語言由淺入深的講述了這一神奇的算法,相信每個讀過的人都會對此有所認識(如果沒有,那就是偶的翻譯太差了--b)。

            現(xiàn)在是年月日的版本,應(yīng)原作者要求,對文中的某些算法細節(jié)做了修改。

            原文鏈接:http://www.gamedev.net/reference/articles/article2003.asp

            原作者文章鏈接:http://www.policyalmanac.org/games/aStarTutorial.htm

            以下是翻譯的正文

            會者不難,A*(念作A)算法對初學者來說的確有些難度。這篇文章并不試圖對這個話題作權(quán)威的陳述。取而代之的是,它只是描述算法的原理,使你可以在進一步的閱讀中理解其他相關(guān)的資料。最后,這篇文章沒有程序細節(jié)。你盡可以用任意的計算機程序語言實現(xiàn)它。如你所愿,我在文章的末尾包含了一個指向例子程序的鏈接。壓縮包包括C++Blitz Basic兩個語言的版本,如果你只是想看看它的運行效果,里面還包含了可執(zhí)行文件。我們正在提高自己。讓我們從頭開始。。。

             

            序:搜索區(qū)域

            假設(shè)有人想從A點移動到一墻之隔的B點,如下圖,綠色的是起點A,紅色是終點B,藍色方塊是中間的墻。

            [-1]

            你首先注意到,搜索區(qū)域被我們劃分成了方形網(wǎng)格。像這樣,簡化搜索區(qū)域,是尋路的第一步。這一方法把搜索區(qū)域簡化成了一個二維數(shù)組。數(shù)組的每一個元素是網(wǎng)格的一個方塊,方塊被標記為可通過的和不可通過的。路徑被描述為從AB我們經(jīng)過的方塊的集合。一旦路徑被找到,我們的人就從一個方格的中心走向另一個,直到到達目的地。

            這些中點被稱為節(jié)點。當你閱讀其他的尋路資料時,你將經(jīng)常會看到人們討論節(jié)點。為什么不把他們描述為方格呢?因為有可能你的路徑被分割成其他不是方格的結(jié)構(gòu)。他們完全可以是矩形,六角形,或者其他任意形狀。節(jié)點能夠被放置在形狀的任意位置-可以在中心,或者沿著邊界,或其他什么地方。我們使用這種系統(tǒng),無論如何,因為它是最簡單的。

            開始搜索

            正如我們處理上圖網(wǎng)格的方法,一旦搜索區(qū)域被轉(zhuǎn)化為容易處理的節(jié)點,下一步就是去引導一次找到最短路徑的搜索。在A*尋路算法中,我們通過從點A開始,檢查相鄰方格的方式,向外擴展直到找到目標。

            我們做如下操作開始搜索:

             

               1,從點A開始,并且把它作為待處理點存入一個開啟列表。開啟列表就像一張購物清單。盡管現(xiàn)在列表里只有一個元素,但以后就會多起來。你的路徑可能會通過它包含的方格,也可能不會。基本上,這是一個待檢查方格的列表。

               2,尋找起點周圍所有可到達或者可通過的方格,跳過有墻,水,或其他無法通過地形的方格。也把他們加入開啟列表。為所有這些方格保存點A作為父方格。當我們想描述路徑的時候,父方格的資料是十分重要的。后面會解釋它的具體用途。

               3,從開啟列表中刪除點A,把它加入到一個關(guān)閉列表,列表中保存所有不需要再次檢查的方格。在這一點,你應(yīng)該形成如圖的結(jié)構(gòu)。在圖中,暗綠色方格是你起始方格的中心。它被用淺藍色描邊,以表示它被加入到關(guān)閉列表中了。所有的相鄰格現(xiàn)在都在開啟列表中,它們被用淺綠色描邊。每個方格都有一個灰色指針反指他們的父方格,也就是開始的方格。

            [-2]

            接著,我們選擇開啟列表中的臨近方格,大致重復前面的過程,如下。但是,哪個方格是我們要選擇的呢?是那個F值最低的。

            路徑評分

            選擇路徑中經(jīng)過哪個方格的關(guān)鍵是下面這個等式:F = G + H

            這里:

                * G = 從起點A,沿著產(chǎn)生的路徑,移動到網(wǎng)格上指定方格的移動耗費。

                * H = 從網(wǎng)格上那個方格移動到終點B的預(yù)估移動耗費。這經(jīng)常被稱為啟發(fā)式的,可能會讓你有點迷惑。這樣叫的原因是因為它只是個猜測。我們沒辦法事先知道路徑的長度,因為路上可能存在各種障礙(墻,水,等等)。雖然本文只提供了一種計算H的方法,但是你可以在網(wǎng)上找到很多其他的方法。

            我們的路徑是通過反復遍歷開啟列表并且選擇具有最低F值的方格來生成的。文章將對這個過程做更詳細的描述。首先,我們更深入的看看如何計算這個方程。

            正如上面所說,G表示沿路徑從起點到當前點的移動耗費。在這個例子里,我們令水平或者垂直移動的耗費為,對角線方向耗費為。我們?nèi)∵@些值是因為沿對角線的距離是沿水平或垂直移動耗費的的根號(別怕),或者約.414倍。為了簡化,我們用和近似。比例基本正確,同時我們避免了求根運算和小數(shù)。這不是只因為我們怕麻煩或者不喜歡數(shù)學。使用這樣的整數(shù)對計算機來說也更快捷。你不就就會發(fā)現(xiàn),如果你不使用這些簡化方法,尋路會變得很慢。

            既然我們在計算沿特定路徑通往某個方格的G值,求值的方法就是取它父節(jié)點的G值,然后依照它相對父節(jié)點是對角線方向或者直角方向(非對角線),分別增加和。例子中這個方法的需求會變得更多,因為我們從起點方格以外獲取了不止一個方格。

            H值可以用不同的方法估算。我們這里使用的方法被稱為曼哈頓方法,它計算從當前格到目的格之間水平和垂直的方格的數(shù)量總和,忽略對角線方向,然后把結(jié)果乘以10。這被稱為曼哈頓方法是因為它看起來像計算城市中從一個地方到另外一個地方的街區(qū)數(shù),在那里你不能沿對角線方向穿過街區(qū)。很重要的一點,我們忽略了一切障礙物。這是對剩余距離的一個估算,而非實際值,這也是這一方法被稱為啟發(fā)式的原因。想知道更多?你可以在這里找到方程和額外的注解。

            F的值是GH的和。第一步搜索的結(jié)果可以在下面的圖表中看到。F,GH的評分被寫在每個方格里。正如在緊挨起始格右側(cè)的方格所表示的,F被打印在左上角,G在左下角,H則在右下角。

            [-3]

            現(xiàn)在我們來看看這些方格。寫字母的方格里,G = 10。這是因為它只在水平方向偏離起始格一個格距。緊鄰起始格的上方,下方和左邊的方格的G值都等于。對角線方向的G值是。

            H值通過求解到紅色目標格的曼哈頓距離得到,其中只在水平和垂直方向移動,并且忽略中間的墻。用這種方法,起點右側(cè)緊鄰的方格離紅色方格有格距離,H值就是。這塊方格上方的方格有格距離(記住,只能在水平和垂直方向移動)H值是。你大致應(yīng)該知道如何計算其他方格的H值了~。每個格子的F值,還是簡單的由GH相加得到

            繼續(xù)搜索

            為了繼續(xù)搜索,我們簡單的從開啟列表中選擇F值最低的方格。然后,對選中的方格做如下處理:

               4,把它從開啟列表中刪除,然后添加到關(guān)閉列表中。

               5,檢查所有相鄰格子。跳過那些已經(jīng)在關(guān)閉列表中的或者不可通過的(有墻,水的地形,或者其他無法通過的地形),把他們添加進開啟列表,如果他們還不在里面的話。把選中的方格作為新的方格的父節(jié)點。

               6,如果某個相鄰格已經(jīng)在開啟列表里了,檢查現(xiàn)在的這條路徑是否更好。換句話說,檢查如果我們用新的路徑到達它的話,G值是否會更低一些。如果不是,那就什么都不做。

                  另一方面,如果新的G值更低,那就把相鄰方格的父節(jié)點改為目前選中的方格(在上面的圖表中,把箭頭的方向改為指向這個方格)。最后,重新計算FG的值。如果這看起來不夠清晰,你可以看下面的圖示。

            好了,讓我們看看它是怎么運作的。我們最初的格方格中,在起點被切換到關(guān)閉列表中后,還剩格留在開啟列表中。這里面,F值最低的那個是起始格右側(cè)緊鄰的格子,它的F值是。因此我們選擇這一格作為下一個要處理的方格。在緊隨的圖中,它被用藍色突出顯示。

            [-4]

            首先,我們把它從開啟列表中取出,放入關(guān)閉列表(這就是他被藍色突出顯示的原因)。然后我們檢查相鄰的格子。哦,右側(cè)的格子是墻,所以我們略過。左側(cè)的格子是起始格。它在關(guān)閉列表里,所以我們也跳過它。

            其他格已經(jīng)在開啟列表里了,于是我們檢查G值來判定,如果通過這一格到達那里,路徑是否更好。我們來看選中格子下面的方格。它的G值是。如果我們從當前格移動到那里,G值就會等于(到達當前格的G值是,移動到上面的格子將使得G值增加)。因為G值大于,所以這不是更好的路徑。如果你看圖,就能理解。與其通過先水平移動一格,再垂直移動一格,還不如直接沿對角線方向移動一格來得簡單。

            當我們對已經(jīng)存在于開啟列表中的個臨近格重復這一過程的時候,我們發(fā)現(xiàn)沒有一條路徑可以通過使用當前格子得到改善,所以我們不做任何改變。既然我們已經(jīng)檢查過了所有鄰近格,那么就可以移動到下一格了。

            于是我們檢索開啟列表,現(xiàn)在里面只有7格了,我們?nèi)匀贿x擇其中F值最低的。有趣的是,這次,有兩個格子的數(shù)值都是。我們?nèi)绾芜x擇?這并不麻煩。從速度上考慮,選擇最后添加進列表的格子會更快捷。這種導致了尋路過程中,在靠近目標的時候,優(yōu)先使用新找到的格子的偏好。但這無關(guān)緊要。(對相同數(shù)值的不同對待,導致不同版本的A*算法找到等長的不同路徑)那我們就選擇起始格右下方的格子,如圖:

            [-5]

            這次,當我們檢查相鄰格的時候,發(fā)現(xiàn)右側(cè)是墻,于是略過。上面一格也被略過。我們也略過了墻下面的格子。為什么呢?因為你不能在不穿越墻角的情況下直接到達那個格子。你的確需要先往下走然后到達那一格,按部就班的走過那個拐角。(注解:穿越拐角的規(guī)則是可選的。它取決于你的節(jié)點是如何放置的。)

            這樣一來,就剩下了其他格。當前格下面的另外兩個格子目前不在開啟列表中,于是我們添加他們,并且把當前格指定為他們的父節(jié)點。其余格,兩個已經(jīng)在關(guān)閉列表中(起始格,和當前格上方的格子,在表格中藍色高亮顯示),于是我們略過它們。最后一格,在當前格的左側(cè),將被檢查通過這條路徑,G值是否更低。不必擔心,我們已經(jīng)準備好檢查開啟列表中的下一格了。

            我們重復這個過程,直到目標格被添加進關(guān)閉列表(注解),就如在下面的圖中所看到的。

            [-6]

            注意,起始格下方格子的父節(jié)點已經(jīng)和前面不同的。之前它的G值是,并且指向右上方的格子。現(xiàn)在它的G值是,指向它上方的格子。這在尋路過程中的某處發(fā)生,當應(yīng)用新路徑時,G值經(jīng)過檢查變得低了-于是父節(jié)點被重新指定,GF值被重新計算。盡管這一變化在這個例子中并不重要,在很多場合,這種變化會導致尋路結(jié)果的巨大變化。

            那么,我們怎么確定這條路徑呢?很簡單,從紅色的目標格開始,按箭頭的方向朝父節(jié)點移動。這最終會引導你回到起始格,這就是你的路徑!看起來應(yīng)該像圖中那樣。從起始格A移動到目標格B只是簡單的從每個格子(節(jié)點)的中點沿路徑移動到下一個,直到你到達目標點。就這么簡單。

            [-7]

            A*方法總結(jié)

            好,現(xiàn)在你已經(jīng)看完了整個說明,讓我們把每一步的操作寫在一起:

               1,把起始格添加到開啟列表。

               2,重復如下的工作:

                  a) 尋找開啟列表中F值最低的格子。我們稱它為當前格。

                  b) 把它切換到關(guān)閉列表。

                  c) 對相鄰的格中的每一個?

                      * 如果它不可通過或者已經(jīng)在關(guān)閉列表中,略過它。反之如下。

                      * 如果它不在開啟列表中,把它添加進去。把當前格作為這一格的父節(jié)點。記錄這一格的F,G,H值。

                      * 如果它已經(jīng)在開啟列表中,用G值為參考檢查新的路徑是否更好。更低的G值意味著更好的路徑。如果是這樣,就把這一格的父節(jié)點改成當前格,并且重新計算這一格的GF值。如果你保持你的開啟列表按F值排序,改變之后你可能需要重新對開啟列表排序。

                  d) 停止,當你

                      * 把目標格添加進了關(guān)閉列表(注解),這時候路徑被找到,或者

                      * 沒有找到目標格,開啟列表已經(jīng)空了。這時候,路徑不存在。

               3.保存路徑。從目標格開始,沿著每一格的父節(jié)點移動直到回到起始格。這就是你的路徑。

             

            (注解:在這篇文章的較早版本中,建議的做法是當目標格(或節(jié)點)被加入到開啟列表,而不是關(guān)閉列表的時候停止尋路。這么做會更迅速,而且?guī)缀蹩偸悄苷业阶疃痰穆窂剑皇墙^對的。當從倒數(shù)第二個節(jié)點到最后一個(目標節(jié)點)之間的移動耗費懸殊很大時-例如剛好有一條河穿越兩個節(jié)點中間,這時候舊的做法和新的做法就會有顯著不同。)

             

            題外話

            離題一下,見諒,值得一提的是,當你在網(wǎng)上或者相關(guān)論壇看到關(guān)于A*的不同的探討,你有時會看到一些被當作A*算法的代碼而實際上他們不是。要使用A*,你必須包含上面討論的所有元素--特定的開啟和關(guān)閉列表,用F,GH作路徑評價。有很多其他的尋路算法,但他們并不是A*A*被認為是他們當中最好的。Bryan Stout在這篇文章后面的參考文檔中論述了一部分,包括他們的一些優(yōu)點和缺點。有時候特定的場合其他算法會更好,但你必須很明確你在作什么。好了,夠多的了。回到文章。

             

            實現(xiàn)的注解

            現(xiàn)在你已經(jīng)明白了基本原理,寫你的程序的時候還得考慮一些額外的東西。下面這些材料中的一些引用了我用C++Blitz Basic寫的程序,但對其他語言寫的代碼同樣有效。

            1.其他單位(避免碰撞):如果你恰好看了我的例子代碼,你會發(fā)現(xiàn)它完全忽略了其他單位。我的尋路者事實上可以相互穿越。取決于具體的游戲,這也許可以,也許不行。如果你打算考慮其他單位,希望他們能互相繞過,我建議你只考慮靜止或那些在計算路徑時臨近當前單位的單位,把它們當前的位置標志為可通過的。對于臨近的運動著的單位,你可以通過懲罰它們各自路徑上的節(jié)點,來鼓勵這些尋路者找到不同的路徑(更多的描述見#2).

            如果你選擇了把其他正在移動并且遠離當前尋路單位的那些單位考慮在內(nèi),你將需要實現(xiàn)一種方法及時預(yù)測在何時何地碰撞可能會發(fā)生,以便恰當?shù)谋苊狻7駝t你極有可能得到一條怪異的路徑,單位突然轉(zhuǎn)彎試圖避免和一個已經(jīng)不存在的單位發(fā)生碰撞。

            當然,你也需要寫一些碰撞檢測的代碼,因為無論計算的時候路徑有多完美,它也會因時間而改變。當碰撞發(fā)生時,一個單位必須尋找一條新路徑,或者,如果另一個單位正在移動并且不是正面碰撞,在繼續(xù)沿當前路徑移動之前,等待那個單位離開。

            這些提示大概可以讓你開始了。如果你想了解更多,這里有些你可能會覺得有用的鏈接:

                *自治角色的指導行為:Craig Reynold在指導能力上的工作和尋路有些不同,但是它可以和尋路整合從而形成更完整的移動和碰撞檢測系統(tǒng)。

                *電腦游戲中的長短距指導:指導和尋路方面著作的一個有趣的考察。這是一個pdf文件。

                *協(xié)同單位移動:一個兩部分系列文章的第一篇,內(nèi)容是關(guān)于編隊和基于分組的移動,作者是帝國時代(Age of Empires)的設(shè)計者Dave Pottinger.

                *實現(xiàn)協(xié)同移動:Dave Pottinger文章系列的第二篇。

            2. 不同的地形損耗:在這個教程和我附帶的程序中,地形只能是二者之-可通過的和不可通過的。但是你可能會需要一些可通過的地形,但是移動耗費更高-沼澤,小山,地牢的樓梯,等等。這些都是可通過但是比平坦的開闊地移動耗費更高的地形。類似的,道路應(yīng)該比自然地形移動耗費更低。

            這個問題很容易解決,只要在計算任何地形的G值的時候增加地形損耗就可以了。簡單的給它增加一些額外的損耗就可以了。由于A*算法已經(jīng)按照尋找最低耗費的路徑來設(shè)計,所以很容易處理這種情況。在我提供的這個簡單的例子里,地形只有可通過和不可通過兩種,A*會找到最短,最直接的路徑。但是在地形耗費不同的場合,耗費最低的路徑也許會包含很長的移動距離-就像沿著路繞過沼澤而不是直接穿過它。

            一種需額外考慮的情況是被專家稱之為influence mapping的東西(暫譯為影響映射圖)。就像上面描述的不同地形耗費一樣,你可以創(chuàng)建一格額外的分數(shù)系統(tǒng),并把它應(yīng)用到尋路的AI中。假設(shè)你有一張有大批尋路者的地圖,他們都要通過某個山區(qū)。每次電腦生成一條通過那個關(guān)口的路徑,它就會變得更擁擠。如果你愿意,你可以創(chuàng)建一個影響映射圖對有大量屠殺事件的格子施以不利影響。這會讓計算機更傾向安全些的路徑,并且?guī)椭苊饪偸莾H僅因為路徑短(但可能更危險)而持續(xù)把隊伍和尋路者送到某一特定路徑。

            另一個可能得應(yīng)用是懲罰周圍移動單位路徑上得節(jié)點。A*的一個底限是,當一群單位同時試圖尋路到接近的地點,這通常會導致路徑交疊。以為一個或者多個單位都試圖走相同或者近似的路徑到達目的地。對其他單位已經(jīng)認領(lǐng)了的節(jié)點增加一些懲罰會有助于你在一定程度上分離路徑,降低碰撞的可能性。然而,如果有必要,不要把那些節(jié)點看成不可通過的,因為你仍然希望多個單位能夠一字縱隊通過擁擠的出口。同時,你只能懲罰那些臨近單位的路徑,而不是所有路徑,否則你就會得到奇怪的躲避行為例如單位躲避路徑上其他已經(jīng)不在那里的單位。還有,你應(yīng)該只懲罰路徑當前節(jié)點和隨后的節(jié)點,而不應(yīng)處理已經(jīng)走過并甩在身后的節(jié)點。

            3. 處理未知區(qū)域:你是否玩過這樣的PC游戲,電腦總是知道哪條路是正確的,即使它還沒有偵察過地圖?對于游戲,尋路太好會顯得不真實。幸運的是,這是一格可以輕易解決的問題。

            答案就是為每個不同的玩家和電腦(每個玩家,而不是每個單位--那樣的話會耗費大量的內(nèi)存)創(chuàng)建一個獨立的knownWalkability數(shù)組,每個數(shù)組包含玩家已經(jīng)探索過的區(qū)域,以及被當作可通過區(qū)域的其他區(qū)域,直到被證實。用這種方法,單位會在路的死端徘徊并且導致錯誤的選擇直到他們在周圍找到路。一旦地圖被探索了,尋路就像往常那樣進行。

            4. 平滑路徑:盡管A*提供了最短,最低代價的路徑,它無法自動提供看起來平滑的路徑。看一下我們的例子最終形成的路徑(在圖)。最初的一步是起始格的右下方,如果這一步是直接往下的話,路徑不是會更平滑一些嗎?有幾種方法來解決這個問題。當計算路徑的時候可以對改變方向的格子施加不利影響,對G值增加額外的數(shù)值。也可以換種方法,你可以在路徑計算完之后沿著它跑一遍,找那些用相鄰格替換會讓路徑看起來更平滑的地方。想知道完整的結(jié)果,查看Toward More Realistic Pathfinding,一篇(免費,但是需要注冊)Marco Pinter發(fā)表在Gamasutra.com的文章

            5. 非方形搜索區(qū)域:在我們的例子里,我們使用簡單的D方形圖。你可以不使用這種方式。你可以使用不規(guī)則形狀的區(qū)域。想想冒險棋的游戲,和游戲中那些國家。你可以設(shè)計一個像那樣的尋路關(guān)卡。為此,你可能需要建立一個國家相鄰關(guān)系的表格,和從一個國家移動到另一個的G值。你也需要估算H值的方法。其他的事情就和例子中完全一樣了。當你需要向開啟列表中添加新元素的時候,不需使用相鄰的格子,取而代之的是從表格中尋找相鄰的國家。

            類似的,你可以為一張確定的地形圖創(chuàng)建路徑點系統(tǒng),路徑點一般是路上,或者地牢通道的轉(zhuǎn)折點。作為游戲設(shè)計者,你可以預(yù)設(shè)這些路徑點。兩個路徑點被認為是相鄰的如果他們之間的直線上沒有障礙的話。在冒險棋的例子里,你可以保存這些相鄰信息在某個表格里,當需要在開啟列表中添加元素的時候使用它。然后你就可以記錄關(guān)聯(lián)的G值(可能使用兩點間的直線距離),H值(可以使用到目標點的直線距離),其他都按原先的做就可以了。Amit Patel 寫了其他方法的摘要。另一個在非方形區(qū)域搜索RPG地圖的例子,查看我的文章Two-Tiered A* Pathfinding(譯者注:譯文: A*分層尋路)

            6. 一些速度方面的提示:當你開發(fā)你自己的A*程序,或者改寫我的,你會發(fā)現(xiàn)尋路占據(jù)了大量的CPU時間,尤其是在大地圖上有大量對象在尋路的時候。如果你閱讀過網(wǎng)上的其他材料,你會明白,即使是開發(fā)了星際爭霸或帝國時代的專家,這也無可奈何。如果你覺得尋路太過緩慢,這里有一些建議也許有效:

                * 使用更小的地圖或者更少的尋路者。

                * 不要同時給多個對象尋路。取而代之的是把他們加入一個隊列,把尋路過程分散在幾個游戲周期中。如果你的游戲以周期每秒的速度運行,沒人能察覺。但是當大量尋路者計算自己路徑的時候,他們會發(fā)覺游戲速度突然變慢。

                * 盡量使用更大的地圖網(wǎng)格。這降低了尋路中搜索的總網(wǎng)格數(shù)。如果你有志氣,你可以設(shè)計兩個或者更多尋路系統(tǒng)以便使用在不同場合,取決于路徑的長度。這也正是專業(yè)人士的做法,用大的區(qū)域計算長的路徑,然后在接近目標的時候切換到使用小格子/區(qū)域的精細尋路。如果你對這個觀點感興趣,查閱我的文章Two-Tiered A* Pathfinding(譯者注:譯文:A*分層尋路)

                * 使用路徑點系統(tǒng)計算長路徑,或者預(yù)先計算好路徑并加入到游戲中。

                * 預(yù)處理你的地圖,表明地圖中哪些區(qū)域是不可到達的。我把這些區(qū)域稱作孤島。事實上,他們可以是島嶼或其他被墻壁包圍等無法到達的任意區(qū)域。A*的下限是,當你告訴它要尋找通往那些區(qū)域的路徑時,它會搜索整個地圖,直到所有可到達的方格/節(jié)點都被通過開啟列表和關(guān)閉列表的計算。這會浪費大量的CPU時間。可以通過預(yù)先確定這些區(qū)域(比如通過flood-fill或類似的方法)來避免這種情況的發(fā)生,用某些種類的數(shù)組記錄這些信息,在開始尋路前檢查它。

                * 在一個擁擠的類似迷宮的場合,把不能連通的節(jié)點看作死端。這些區(qū)域可以在地圖編輯器中預(yù)先手動指定,或者如果你有雄心壯志,開發(fā)一個自動識別這些區(qū)域的算法。給定死端的所有節(jié)點可以被賦予一個唯一的標志數(shù)字。然后你就可以在尋路過程中安全的忽略所有死端,只有當起點或者終點恰好在死端的某個節(jié)點的時候才需要考慮它們。

            7. 維護開啟列表:這是A*尋路算法最重要的組成部分。每次你訪問開啟列表,你都需要尋找F值最低的方格。有幾種不同的方法實現(xiàn)這一點。你可以把路徑元素隨意保存,當需要尋找F值最低的元素的時候,遍歷開啟列表。這很簡單,但是太慢了,尤其是對長路徑來說。這可以通過維護一格排好序的列表來改善,每次尋找F值最低的方格只需要選取列表的首元素。當我自己實現(xiàn)的時候,這種方法是我的首選。

            在小地圖。這種方法工作的很好,但它并不是最快的解決方案。更苛求速度的A*程序員使用叫做二叉堆的方法,這也是我在代碼中使用的方法。憑我的經(jīng)驗,這種方法在大多數(shù)場合會快~倍,并且在長路經(jīng)上速度呈幾何級數(shù)提升(10倍以上速度)。如果你想了解更多關(guān)于二叉堆的內(nèi)容,查閱我的文章,Using Binary Heaps in A* Pathfinding(譯者注:譯文:在A*尋路中使用二叉堆)

            另一個可能的瓶頸是你在多次尋路之間清除和保存你的數(shù)據(jù)結(jié)構(gòu)的方法。我個人更傾向把所有東西都存儲在數(shù)組里面。雖然節(jié)點可以以面向?qū)ο蟮娘L格被動態(tài)的產(chǎn)生,記錄和保存,我發(fā)現(xiàn)創(chuàng)建和刪除對象所增加的大量時間,以及多余的管理層次減慢的整個過程的速度。但是,如果你使用數(shù)組,你需要在調(diào)用之間清理數(shù)據(jù)。這中情形你想做的最后一件事就是在尋路調(diào)用之后花點時間把一切歸零,尤其是你的地圖很大的時候。

            我通過使用一個叫做whichList(x,y)的二維數(shù)組避免這種開銷,數(shù)組的每個元素表明了節(jié)點在開啟列表還是在關(guān)閉列表中。嘗試尋路之后,我沒有清零這個數(shù)組。取而代之的是,我在新的尋路中重置onClosedListonOpenList的數(shù)值,每次尋路兩個都+5或者類似其他數(shù)值。這種方法,算法可以安全的跳過前面尋路留下的臟數(shù)據(jù)。我還在數(shù)組中儲存了諸如F,GH的值。這樣一來,我只需簡單的重寫任何已經(jīng)存在的值而無需被清除數(shù)組的操作干擾。將數(shù)據(jù)存儲在多維數(shù)組中需要更多內(nèi)存,所以這里需要權(quán)衡利弊。最后,你應(yīng)該使用你最得心應(yīng)手的方法。

            8. Dijkstra的算法:盡管A*被認為是通常最好的尋路算法(看前面的題外話),還是有一種另外的算法有它的可取之處-Dijkstra算法。Dijkstra算法和A*本質(zhì)是相同的,只有一點不同,就是Dijkstra算法沒有啟發(fā)式(H值總是)。由于沒有啟發(fā)式,它在各個方向上平均搜索。正如你所預(yù)料,由于Dijkstra算法在找到目標前通常會探索更大的區(qū)域,所以一般會比A*更慢一些。

            那么為什么要使用這種算法呢?因為有時候我們并不知道目標的位置。比如說你有一個資源采集單位,需要獲取某種類型的資源若干。它可能知道幾個資源區(qū)域,但是它想去最近的那個。這種情況,Dijkstra算法就比A*更適合,因為我們不知道哪個更近。用A*,我們唯一的選擇是依次對每個目標許路并計算距離,然后選擇最近的路徑。我們尋找的目標可能會有不計其數(shù)的位置,我們只想找其中最近的,而我們并不知道它在哪里,或者不知道哪個是最近的。


             

            看完上面的介紹,再來看一個比較經(jīng)典的題目:knight moves。貌似也叫漢密爾頓路徑,具體的我也不記得了。對這個問題我用A*算法來求解,正所謂光說不練是沒有用的

            http://acm.pku.edu.cn/JudgeOnline/problem?id=2243
            problem statement

            A friend of you is doing research on the Traveling Knight Problem (TKP) where you are to find the shortest closed tour of knight moves that visits each square of a given set of n squares on a chessboard exactly once. He thinks that the most difficult part of the problem is determining the smallest number of knight moves between two given squares and that, once you have accomplished this, finding the tour would be easy.
            Of course you know that it is vice versa. So you offer him to write a program that solves the "difficult" part.

            Your job is to write a program that takes two squares a and b as input and then determines the number of knight moves on a shortest route from a to b.

            Input Specification

            The input file will contain one or more test cases. Each test case consists of one line containing two squares separated by one space. A square is a string consisting of a letter (a-h) representing the column and a digit (1-8) representing the row on the chessboard.

            Output Specification

            For each test case, print one line saying "To get from xx to yy takes n knight moves.".

            Sample Input

            e2 e4

            a1 b2

            b2 c3

            a1 h8

            a1 h7

            h8 a1

            b1 c3

            f6 f6

            Sample Output

            To get from e2 to e4 takes 2 knight moves.

            To get from a1 to b2 takes 4 knight moves.

            To get from b2 to c3 takes 2 knight moves.

            To get from a1 to h8 takes 6 knight moves.

            To get from a1 to h7 takes 5 knight moves.

            To get from h8 to a1 takes 6 knight moves.

            To get from b1 to c3 takes 1 knight moves.

            To get from f6 to f6 takes 0 knight moves.
            題目的意思大概是說:在國際象棋的棋盤上,一匹馬共有8個可能的跳躍方向,求從起點到目標點之間的最少跳躍次數(shù)。
            A* code:

             1 #include <iostream>
             2 #include <queue>
             3 using namespace std;
             4 
             5 struct knight{
             6     int x,y,step;
             7     int g,h,f;
             8     bool operator < (const knight & k) const{      //重載比較運算符
             9         return f > k.f;
            10     }
            11 }k;
            12 bool visited[8][8];                                //已訪問標記(關(guān)閉列表)
            13 int x1,y1,x2,y2,ans;                               //起點(x1,y1),終點(x2,y2),最少移動次數(shù)ans
            14 int dirs[8][2]={{-2,-1},{-2,1},{2,-1},{2,1},{-1,-2},{-1,2},{1,-2},{1,2}};//8個移動方向
            15 priority_queue<knight> que;                        //最小優(yōu)先級隊列(開啟列表)
            16 
            17 bool in(const knight & a){                         //判斷knight是否在棋盤內(nèi)
            18     if(a.x<0 || a.y<0 || a.x>=8 || a.y>=8)
            19         return false;
            20     return true;
            21 }
            22 int Heuristic(const knight &a){                    //manhattan估價函數(shù)
            23     return (abs(a.x-x2)+abs(a.y-y2))*10;
            24 }
            25 void Astar(){                                      //A*算法
            26     knight t,s;
            27     while(!que.empty()){
            28         t=que.top(),que.pop(),visited[t.x][t.y]=true;
            29         if(t.x==x2 && t.y==y2){
            30             ans=t.step;
            31             break;
            32         }
            33         for(int i=0;i<8;i++){
            34             s.x=t.x+dirs[i][0],s.y=t.y+dirs[i][1];
            35             if(in(s) && !visited[s.x][s.y]){
            36                 s.g = t.g + 23;                 //23表示根號5乘以10再取其ceil
            37                 s.h = Heuristic(s);
            38                 s.f = s.g + s.h;
            39                 s.step = t.step + 1;
            40                 que.push(s);
            41             }
            42         }
            43     }
            44 }
            45 int main(){
            46     char line[5];
            47     while(gets(line)){
            48         x1=line[0]-'a',y1=line[1]-'1',x2=line[3]-'a',y2=line[4]-'1';
            49         memset(visited,false,sizeof(visited));
            50         k.x=x1,k.y=y1,k.g=k.step=0,k.h=Heuristic(k),k.f=k.g+k.h;
            51         while(!que.empty()) que.pop();
            52         que.push(k);
            53         Astar();
            54         printf("To get from %c%c to %c%c takes %d knight moves.\n",line[0],line[1],line[3],line[4],ans);
            55     }
            56     return 0;
            57 }
            58 

            posted on 2009-04-19 23:53 極限定律 閱讀(128258) 評論(44)  編輯 收藏 引用 所屬分類: ACM/ICPC

            評論

            # re: A*算法入門 2009-04-21 21:04 brightcoder

            good!太好了實在  回復  更多評論   

            # re: A*算法入門 2009-04-22 10:51 brightcoder

            poj這道題,你這個估值函數(shù)在判斷的時候也沒用上呀?跟普通的BFS有何區(qū)別?  回復  更多評論   

            # re: A*算法入門 2009-04-22 13:03 極限定律

            @brightcoder
            我用的隊列是優(yōu)先級隊列,而不是普通的FIFO隊列,優(yōu)先級的依據(jù)就是f,估價用的是h。  回復  更多評論   

            # re: A*算法入門 2009-07-25 19:46 X

            這道題用a*似乎沒什么優(yōu)勢..甚至是沒必要..倒不如用八數(shù)碼問題來作例子  回復  更多評論   

            # re: A*算法入門 2009-09-01 00:43 zyue1105

            這個程序的估價函數(shù)顯然是錯的,漢密爾頓距離是可能大于實際距離的,這種情況下找到的未必是最短路,只不過PKU2243的棋盤給的很小數(shù)據(jù)弱了點,如果是PKU1915的大棋盤,這個程序肯定過不了。  回復  更多評論   

            # re: A*算法入門 2009-09-07 16:39

            牛比!  回復  更多評論   

            # re: A*算法入門 2010-02-17 12:28 e

            POJ2243你真寫錯了,Heuristic函數(shù)是要仔細設(shè)計才行的,題目要你給出最優(yōu)解,不是隨便輸出一組解  回復  更多評論   

            # re: A*算法入門 2010-02-17 12:34 e

            @e
            哦,沒有錯,我看錯了,g已經(jīng)乘了十,你沒寫錯,不好意思...  回復  更多評論   

            # re: A*算法入門 2010-07-03 23:38 suneast

            up...
            說實話,lz講解的是在太詳細了...  回復  更多評論   

            # re: A*算法入門[未登錄] 2010-07-08 17:02 k

            修改后過不了1915  回復  更多評論   

            # re: A*算法入門 2010-10-21 15:26 林帆

            講得很詳細!  回復  更多評論   

            # re: A*算法入門 2011-08-14 17:15 tanghao

            poj2243:
            1. g用的是馬跳一步的歐幾里德距離,h卻是曼哈頓距離,這樣的h是非可采納的。得到的解不一定是最短距離。這算法充其量也就是個A,不是A*。
            2. g除以23就得step。又因為每點的h固定,所以每結(jié)點只記錄x y g f 就可以了。  回復  更多評論   

            # re: A*算法入門 2011-08-24 12:21 。。。無語。。

            文章你能COPY全一點嗎 里面的數(shù)字全都被你吃了?  回復  更多評論   

            # re: A*算法入門 2012-05-21 14:27 caoyuan

            他都做*10操作了 包括f,g ,h 同時*10 所以說還是 A*算法  回復  更多評論   

            # re: A*算法入門 2012-05-21 14:27 caoyuan

            他都做*10操作了 包括f,g ,h 同時*10 所以說還是 A*算法@tanghao
              回復  更多評論   

            # re: A*算法入門 2012-06-23 01:04 passengerxp

            為什么你的A*算法中對于已經(jīng)存在Close 列表的節(jié)點只是忽略,而不是把它移除并加入Open列表中呢  回復  更多評論   

            # re: A*算法入門 2012-07-19 17:54 suse

            @passengerxp
            那樣會產(chǎn)生回繞和loop而產(chǎn)生不必要的計算,雖然新加入到open的列表不會是最終的最短路徑。而且close中的刪除了可能就找不到路了。  回復  更多評論   

            # re: A*算法入門 2012-07-19 17:56 suse-nick

            @caoyuan
            *10 只是為了方便計算,提高效率。 可以見英文版,有說明。  回復  更多評論   

            # re: A*算法入門[未登錄] 2012-08-14 17:00 123

            @passengerxp
            這樣做會產(chǎn)生死循環(huán)吧,再次遍歷OPEN列表的時候又會到這一節(jié)點,然后就開始重復了
              回復  更多評論   

            # re: A*算法入門[未登錄] 2012-09-14 17:06 frank

            博主的這篇博文寫的太好了,希望博主今后能夠翻譯出更多更好的文章!  回復  更多評論   

            # re: A*算法入門 2013-01-04 13:13 0101011

            看不懂,先搜藏起來!!!


            好文要頂!  回復  更多評論   

            # re: A*算法入門 2013-05-31 11:30 qnm

            翻譯的什么東西,數(shù)字都漏了不知道么?什么質(zhì)量啊  回復  更多評論   

            # re: A*算法入門 2013-06-16 10:41 謙虛的路人

            基本思想懂了 謝謝哈`  回復  更多評論   

            # re: A*算法入門 2013-08-10 11:21 huaxingmaster

            有些翻譯漏了,比如
            Technically, in this example, the Manhattan method is inadmissible because it slightly overestimates the remaining distance. But we will use it anyway because it is a lot easier to understand for our purposes, and because it is only a slight overestimation. On the rare occasion when the resulting path is not the shortest possible, it will be nearly as short. Want to know more? You can find equations and additional notes on heuristics here.

            這段講到實際使用曼哈頓方法來作為H的估值,這種方法是“過度估計”按道理來說,應(yīng)該是it is not guaranteed to give us the shortest path,we have what is called an "inadmissible heuristic."
            也就是說是一個不被認可的啟發(fā)式估計

            最后,感謝LZ提供的連接^_^  回復  更多評論   

            # re: A*算法入門 2013-10-19 21:46 seeking

            翻譯質(zhì)量真是一般般,缺乏修正和潤色。難道過來這么多年就沒考慮維護下?  回復  更多評論   

            # re: A*算法入門 2013-11-07 20:50 free斬

            @tanghao
            正解!都改成歐幾里得距離就AC了,感覺自己思維方面還是太固化,很多方面都考慮不到,如果沒有看到下面的評論,就會一味的盲從了,不過樓主對算法的分析確實不錯。
              回復  更多評論   

            # re: A*算法入門 2013-11-07 20:58 free斬

            @kHeuristic函數(shù)中也改成求歐幾里得距離就可以了  回復  更多評論   

            # re: A*算法入門 2013-12-02 11:36 qidada

            看了評論,這篇文章看來還是有用的,,學習下看  回復  更多評論   

            # re: A*算法入門[未登錄] 2013-12-18 18:32 Summer

            親,好多數(shù)字都缺失啊,少好多數(shù)字啊。“緊鄰起始格的上方,下方和左邊的方格的G值都等于。對角線方向的G值是。”
            好多這樣的  回復  更多評論   

            # re: A*算法入門 2014-02-19 10:10 zzyoucan

            學習。  回復  更多評論   

            # re: A*算法入門 2014-05-23 12:35 最愛吹吹風

            起始點下面那個=60的格子怎么沒有算,在F=54的時候,這個格子的F值是最小的。這個是怎么避過去的。  回復  更多評論   

            # re: A*算法入門 2014-05-27 12:19 shenben

            @zyue1105 傻逼一個
              回復  更多評論   

            # re: A*算法入門[未登錄] 2014-06-11 23:18 Jason

            貌似代碼(Line 35-41)并沒有考慮s是否已經(jīng)在queue中,如果沒在queue中那么直接把s push進去就可以了,但是如果已經(jīng)在queue中,根據(jù)A*算法的描述則需要更新queue中那個對象的g,h,f的值。樓主的代碼并沒有這么做。請確認我理解的是否正確,謝謝!  回復  更多評論   

            # re: A*算法入門[未登錄] 2014-06-13 16:49 哈哈

            首先感謝LZ 用心整理這么好的資料

            有個疑問 就是估計函數(shù)為什么最后要乘以10,是不是放大誤差還是什么??  回復  更多評論   

            # re: A*算法入門 2014-11-28 23:40 Brant

            @哈哈
            不乘10,對角線上的格子估值就是1.4,是個小數(shù),在計算的時候不如整數(shù)快。  回復  更多評論   

            # re: A*算法入門 2015-01-26 11:34 newbie

            理論上A*算法是可能要把已擴展的closed表中的節(jié)點再拉回open表的,l因為一個子節(jié)點可能有多個父節(jié)點,新擴展的父節(jié)點g值可能更小。但是如果h是單調(diào)的, closed表里的節(jié)點就已經(jīng)找到最佳路徑了,不用再loop了。好像文中的例子沒有保證h是單調(diào)的,所以是不是該說明一下?  回復  更多評論   

            # re: A*算法入門 2015-03-11 11:59 coco

            多謝樓主分享 但是 你能把一些數(shù)值完善下嘛  回復  更多評論   

            # re: A*算法入門 2015-03-11 12:00 coco

            多謝樓主分享。
            但是 你能把文章中的數(shù)值完善下嘛 都缺失了!  回復  更多評論   

            # re: A*算法入門 2015-03-20 01:28 Chu

            一定要贊一個~!  回復  更多評論   

            # re: A*算法入門 2015-05-14 11:07 demo

            a* 才是bfs的特例吧  回復  更多評論   

            # re: A*算法入門 2015-05-14 11:23 demo

            @demo
            我傻逼了。。不好意思  回復  更多評論   

            # re: A*算法入門 2015-09-11 22:23 飛揚

            很感謝樓主  回復  更多評論   

            # re: A*算法入門 2016-01-28 18:08 劉生

            最后的示例程序根本編譯不過,有很多錯誤  回復  更多評論   

            # re: A*算法入門 2016-08-06 20:14 sky006

            開啟列表放到priority_queue里面就無法更新了  回復  更多評論   

            <2009年4月>
            2930311234
            567891011
            12131415161718
            19202122232425
            262728293012
            3456789

            導航

            統(tǒng)計

            常用鏈接

            留言簿(10)

            隨筆分類

            隨筆檔案

            友情鏈接

            搜索

            最新評論

            閱讀排行榜

            評論排行榜

            亚洲精品综合久久| 日本精品久久久久中文字幕| 久久久国产打桩机| 精品久久久久久中文字幕| 国产91久久综合| 99蜜桃臀久久久欧美精品网站| 久久久久久午夜成人影院| 精品久久久久久无码免费| 久久精品国产久精国产果冻传媒| 国产精品18久久久久久vr| 亚洲美日韩Av中文字幕无码久久久妻妇 | 亚洲欧美一区二区三区久久| 一本色道久久99一综合| 久久精品亚洲男人的天堂| 久久99精品久久久久久动态图| 94久久国产乱子伦精品免费| 无遮挡粉嫩小泬久久久久久久| 久久精品国产只有精品66| 久久精品国产亚洲av水果派 | 久久亚洲日韩看片无码| 青青草原1769久久免费播放| 一本久久a久久精品亚洲| 三级韩国一区久久二区综合| 青青草原1769久久免费播放 | 亚洲另类欧美综合久久图片区| 久久精品国产精品亚洲精品| 中文字幕无码免费久久| 欧美激情精品久久久久久| 国产高清国内精品福利99久久| av无码久久久久久不卡网站| 伊人久久综合精品无码AV专区| 亚洲成av人片不卡无码久久| 久久精品夜色噜噜亚洲A∨| 一本色道久久88加勒比—综合| 97r久久精品国产99国产精| 亚洲国产精品无码久久久不卡| 热久久最新网站获取| 思思久久精品在热线热| 久久人妻无码中文字幕| 久久精品国产99国产精品导航| 亚洲人成电影网站久久|