A*算法入門


一:概述

    A*算法在游戲中應用是十分廣泛的,許許多多的游戲在尋路方面都會考慮使用該算法(當然除該算法外,前輩們也想出很多其他辦法),它是一種啟發式的尋路搜索算法。今天這邊重點全面分析探討A*算法。

 二:術語

    此處對接下來將要討論的內容中的相關術語約定如下:

    start-node       :起始節點。即:用戶請求尋路時的起點

    end-node         :目標節點/稱之為結束節點/終點。即:用戶請求尋路的終點/目的地點

    current-node    :當前節點。即:當前正在分析的節點

    adjacency-node:鄰接節點。即:與當前節點相鄰接的節點。如:在 N x M 型地圖中,一個節點的鄰接節點是指與該相連的周邊8個節點,即稱為鄰接節點。

    evaluate-func   :估值計算函數(即:h-value值計算函數)

    g-value            :從 start-node 節點到 current-node 節點的實際代價

    h-value            :從 current-node 到 end-node 節點的估價值/預估代價

    f-value             :指 current-node 的 g-value + h-value 和值,即:估價值。其定義是:從 start-node 經過 current-node 節點到達 end-node 節點的估價值。(或許可以簡單理解為 current-node 的 A* 值吧)

    open-table       :開表。即:待考察節點列表

    close[d]-table   :閉表。即:已考察節點列表(close后面加不加 d,完全不重要,不需要糾結)

三:算法思想

    整體上,A*算法主要是將節點划分為未考察的、待考察的以及已考察的三大類。剛開始時的(所有)節點都是未考察的,而待考察的節點則全都被放在open-table中,所有已經被考察過的節點全都放在close-table中。因此,明顯的 open-table、close-table 表起初都是空的。算法細節如下:

    Step_001:重置數據;如:需要重置 start-node 的 g-value = 0;h-value = evaluate-func(........);

    Step_001:將 start-node 入 open-table;

    // 第一層循環邏輯開始

    Step_002:取出 open-table 中 f-value 值最小的節點作為 current-node,並做如下處理。(剛開始時,f-value 最小肯定是 start-node,因為表中只有這個節點)

        Step_003:如果 current-node 不存在,則尋路失敗退出

        Step_004:如果 current-node 為 end-node,則尋路成功退出,並將 end-node 的前驅節點置為 current-node,然后返回由 end-node 逆序倒推其前驅節點直到 start-node 的所有節點組成的路徑返回

        Step_005:將 current-node 入 close-table;

        // 第二層循環開始

        Step_006:逐個遍歷 current-node 的 adjacency-node,並做如下處理

            Step_007:如果 adjacency-node 在閉表中,則不做任何處理

            Step_008:如果 adjacency-node 不在開表中,則更新其 g-value、h-value 值;並將該 adjacency-node 的前驅節點置為 current-node 節點,最后再將 adjacency-node 入 open-table

            Step_009:如果 adjacency-node 已在開表中,則比較如果從當前的 current-node 到該 adjacency-node 的 g-value 是否會比 adjacency-node 此時的 g-value 值更優(即:更小)。

                             如果不會更優,則不做任何處理;

                             如果會更優,則將 adjacency-node 的 g-value 值更新為從當前 current-node 到 adjacency-node 的 g-value 值,並將 adjacency-node 的前驅更改為當前的 current-node節點

            Step_010:回到 Step_006 繼續處理下一個鄰接節點

        Step_011:只要 open-table 表非空,則繼續回到 Step_002 處理

四:討論

    A*算法思想其實非常容易理解,並且其在尋找單源最短路徑方面算是速度最為快速的。與Dijkstra算法相比,因為A*是啟發性的搜索算法,其是更有目的性地(依賴啟發函數的指引下)向目的節點逼近,它不能說是盲目性地搜索算法。而Dijkstra算法則不同,它是盲目性地按算法規則一步步尋找下去。因此,Dijkstra算法是適合單源到其他所有節點的最短路徑的搜索算法。當然,從搜索的結果路徑來看,Dijkstra算法從源節點到任何節點的路徑肯定都是最短的,而A*卻未必,因為A*的啟發函數只是預估、估算出代價罷了,沒辦法保證不把路徑給帶偏。只是這種路徑偏離一般情況下是不會很離譜的,多數情況下也都會非常接近最短路徑的,這主要是要看啟發函數如何選擇,不同的啟發函數結果路徑可能會不一樣。再從時間復雜度上看,Dijkstra算法的時間復雜度,如果是任何兩點間的最短路徑的話,算法時間復雜度為O(n^3),而A*則要好很多,從上面算法思想中可以大約看出,時間代價最大的是在每一層循環遍歷處理所有節點環節上,如果以 N x M 地圖來例,且每個節點鄰接節點算上斜角的也才8個,記為 m。因此明確兩點間的A*尋路時間復雜度 T(n) = n * (c1 + m) + c2,因此,時間復雜度為漸近O(n),而任何兩點間的A*尋路時間復雜度為O(n^2)。另外,在第一層循環中,由於Step_003、Step_004步驟的存在,則A*的每次尋路是隨時都有可能提前結束的。

    在實際項目中,我們會選擇使用A*,但一般還會配合其他的一些技術手段共同協作。因為A*雖說在搜索最短路徑方面速度很快,但代價也是很大的。試想現在游戲,隨隨便便一張地圖都有非常多的格子的。如:2D斜45度游戲中游戲地圖少則88 x 88,多則大型地圖,其格子總數永永不止88 x 88了,因此純用A*作為尋路的代價就大了(試想,如果在最背的情況下,是需要遍歷所有節點的,此時 n = (88 * 88) = 7744次,該值是還沒考慮常數系數等成份的)。而且A*對存儲空間的要求也比較大,地圖格子數越多,需要的存儲空間也是越來越大。如上文思想一點中所述,有open-table、有close-table、又有未初化節點表等等。這對一些內存資源本來就比較匱乏的設備來說,也是壓力巨大(比如:手持設備等,當然現在的手持設備一般也能承受的住就是了,只要你的app/游戲不亂設計的話,一般都沒什么太大壓力)。

    今天博文暫且到此,后面再分享有關A*算法的高級話題討論。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM