實現A星算法
【更新】
稍微將A*算法進行修正,使用BFS(按F值對open表排序),另外,新增評估函數,用來測量當前點到終點的線段上的隨機某一點是否是墻或已訪問結點,是的話返回1,否則返回0。
function path_add_barrial_tracing(state, pt1, pt2) local xabs, yabs = math.abs(pt1.x - pt2.x), math.abs(pt1.y - pt2.y) if xabs == 0 or yabs == 0 then return 0 end local xr, yr = math.ceil(math.random(1,xabs)), math.ceil(math.random(1,yabs)) localv = path_get_point(state, {x= xr, y= yr}) if v ~= 1 then return 1 else return 0 end end
效果圖:
-----------------------------------------------------
寫在前面
看到過類似的將尋路算法可視化的文章。
- A*算法可視化詳解與實現 - 代碼妖嬈 - 博客頻道 - CSDN.NET
- A*尋路算法的探尋與改良(一) - 知乎專欄
尋思著將它們整合進遊戲框架,總體上說,整合難度比較低。
實現思路
先前實現了TableLayout,設定長寬,可以均勻排布容器內各元素。題圖中的方格也是這樣的實現思路。
只實現了廣度遍歷BFS和深度遍歷DFS,兩者是經典的遍歷算法。
書上一般采用open和closed兩個表的方式,這裏為了圖簡單,就用一張表實現了。圖的結構二維矩陣表示,1表示未訪問過,3、4表示起點和終點,2表示墻,6及以上表示訪問過。那麽每次訪問只要將矩陣中的值標記下即可。
- BFS是將當前訪問方塊的相鄰未訪問方塊添加進表的末尾,每次從表頭取出將要訪問的方塊。由於添加的方塊要隔一段時間才能訪問到,所以可能導致表的體積迅速增大。因此,BFS的效率比較差,它會遍歷所有的方塊,將它能夠找到全局最優解。《後天》中的海水淹沒城市也是BFS的體現。
- DFS每次將當前方塊的相鄰未訪問方塊添加進表的頭部,每次從表頭取方塊。所以與BFS不同,新添加的方塊立馬就被訪問,故表的體積不會迅速增大。它能快速找到解,但不一定是全局最優解。生活中的閃電生長方式就是DFS。
兩種算法只是對於表的添加方式不同,DFS是添加到頭部,BFS是添加到尾部。
這裏主要的難點在於,方塊的顏色是漸變的,離起點(紅色)近的呈黑色,遠的呈天藍色。計算方式是求任意點到起點的距離,然後根據HSL轉換到RGB,HSL的色相是固定的,而亮度是可以調整的,從而實現漸變效果。
-------------------------------------------------
下面實現(偽)A*算法。
BFS和DFS方法都有缺點:BFS能找到全局最優,然而它需要將所有位置都訪問一遍,耗時間;DFS快,但只是局部最優,且DFS如何挑選需展開的結點也沒有明確規定。
A*對上述方式有了改進。A*給出了一個評估函數F。F=G+H。G是當前移動量,H是評估的待移動距離,兩者總和是當前結節的評估值,當然,值越小,走這條路的可能性越大。
解決方法很簡單:令G=累積的移動距離;令H=當前位置離終點的哈密頓距離。在展開結點的時候,對待選結點按F值排序,使F值最小的最先展開,從而節省時間。
A*的實現一般用open和closed表,它將open表中的結點按F排序,選代價最小的展開。也就是說,A*算法每一次將open表中的結點進行排序,而A*的展開方式類似於BFS。為什麽是基於BFS?因為BFS產生的open表結點是當前已遍歷結點的輪廓,這有點像最小生成樹算法,從當前輪廓中挑選代價最小的結點進行展開。唯一影響A*效率的就是F的計算方法。
PS:由於偷懶,實現A*與上面的不同!上面是將open進行排序,而我只將當前結點的相鄰結點進行排序,所以算法效果肯定沒A*好。再者,本系列的目標是做GUI,算法可視化只是一個demo。
階段性總結
A*算法的關鍵是設立一個評估函數,就如同阿法狗對局面的估計一樣。它采用的其實類似於最小生成樹的方式(把格子想成上下左右相連的圖),只是最小生成樹的挑選規則是確定的(路徑長度確定),A*的挑選規則是不確定的(評估函數不精準)。這導致最小生成樹產生最優解,而A*不一定能得到最優解。
由http://zhuanlan.zhihu.com/p/25593280備份。
實現A星算法