程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 多單位尋路算法,尋找最優解,算法最優

多單位尋路算法,尋找最優解,算法最優

編輯:C++入門知識

多單位尋路算法,尋找最優解,算法最優


對於單個單位的尋路可以使用A*算法。但是在實際應用中往往出現多個單位同時移動的場面,而且它們會互相影響,阻礙對方的移動。所以一旦沖突,之前為每個單位計算出的路徑就會失效。

一種流行的解決方法是發現沖突的時候重新計算路徑。還有定期重新計算的等等。這些都是動態調整的方案,最後形成的路徑並非是最優的。雖然這些次優解可以滿足大部分場景的需要,但是有沒有辦法計算出最優解呢?畢竟很多動態調整的算法會有失敗的可能。

我們可以把一個最小時間單位(回合)中每個單位的可能移動的組合列出來作為節點,放入搜索樹中,然後再用A*算法進行搜索。但是在方格地圖中一個單位可以有最多5或者9(帶斜線)種不同的移動方法,其中包括靜止不動。2個單位就有5x5或9x9種組合。隨著要考慮的單位數的增加,組合數會爆炸。這時我們可以把一個回合再分解到一系列單個單位的移動,把單個移動作為節點。這樣一旦發現某些條件不滿足,比如兩個單位有沖突,就可以關閉當前節點,不繼續生成組合,從而實現對搜索樹進行剪枝。由於一個回合中各單位的移動實際並沒有優先次序,而我們現在的處理是一個單位一個單位進行,所以先被處理的單位不需要檢查和後面單位的沖突,只有後面的單位需要檢查和前面單位的沖突。當然為了成功剪枝,優先處理哪些單位也是值得考慮的。可以按某種優先級排序,比如越接近目標的可以優先處理。

舉個例子,假定一個2x2的地圖,有兩個單位A在坐標1,0處,B在坐標0,1處,都想要移動到坐標1,1處。那麼我們按先處理A後處理B的次序生成節點。假定[]表示計劃的移動列表,{}表示未計劃的單位列表。那麼搜索樹可以表示如下:

                                                []{A10,B01} //初始狀態

                                                                    /                                \                    \

                                                              [A向下]{B01}                         [A不動]{B01}未展開       [A向左]{B01}未展開

                                   /                     |       \

              [A向下B向右]{}沖突,剪枝      [A向下B不動]{}             [A向下B向上]{} 未展開

                                                     |

                                                    []{A11,B01}     ->     []{B01} 回合1結束,結算位置,A到達目的地,被移走,然後進行下一輪。

                                                                          /                   \                     \

                                                                            [B向右]{}      [B不動]{}未展開    [B向上]{}未展開

                                                                     |

                                                                             []{} 回合2結束,結算位置,B到達目的地,被移走。列表為空,達成目標狀態。

 

另一個優化是,雖然我們可以生成一個單位的所有移動方法,但是我們並不急於把所有的移動都作為節點放入open list中,而只選1~2個最有可能成功的。當前節點並不關閉,並且維護一個列表,記錄有哪些移動已經展開過了。這樣可以有效減少open list中節點的數量。假定<>為未展開的移動,上面的例子可以用如下表示:

                                                                        []{A10,B01} //初始狀態    -> []<A不動, A向左>{}

                                                                           /                

                                                            [A向下]{B01}  ->    [A向下]<B向上>{} 

                                              /                     |       

                                 [A向下B向右]{}沖突,剪枝      [A向下B不動]{}            

                                                                     |

                                                                      []{A11,B01}     ->     []{B01}  ->   []<B不動,B向上> 回合1結束,結算位置,A到達目的地,被移走,然後進行下一輪。

                                                                                                      |        

                                                                                                          [B向右]{}     

                                                                                                      |

                                                                                                                []{} 回合2結束,結算位置,B到達目的地,被移走。列表為空,達成目標狀態。

 

關於A*搜索的啟發函數,我們當然可以求出每個單位的manhattan距離、diagonal距離或者duclidean距離,然後求和作為每個節點的啟發值。更好的啟發值可以用RRA*(Reverse Resumable A*)算法來獲得。也就對目標點到當前點執行一次普通A*搜索,來獲得(不計移動單位的障礙)的實際距離。這個距離比前面提到的估算算法要准確得多。RRA*搜索生成的open list是可以重用的,實際上開銷並不大。當然對多個目標點,我們需要維護各自的open list。

具體實現可以參考github.com/silmerusse/fudge_pathfinding中的例子。對於相對簡單的地圖和較少的單位,最優解是可以很快求出的。但是對復雜度高的地圖和較多單位,則需要花費較多時間和內存空間。

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved