尋路之 A* 搜尋演算法

cjjian發表於2017-06-21

Powered By Jiajian Chan

最近做到一道題,題目如下:

有 A、B 兩點,中間有一堆障礙物,求出A點到B的可行的路徑,寫出一個 DEMO 並可用任何語言實現(要求可以任意設定 A、B 點和障礙物的位置,需要做UI)。

WechatIMG171.jpeg

首先,理解一下題意,需要求出 A、B 兩點的可行路線,要注意的是可以任意設定 A、B 兩點位置以及障礙物的位置且需要做 UI。題目需一句話帶過,但需要做不少的工作。嗯,很明顯,這是一道考演算法邏輯還有 UI 的題目。

現在我們將主要工作放在如何去求出 A、B 兩點的可行的路徑呢?

估計看到題目,很多人都會無從下手。但再認真想想,其實這道題目就類似我們日常用的導航,尋找起點和終點可行的最短路線。那麼,我們可以使用搜尋演算法解決這一道題目。搜尋演算法有很多種,如:最佳優先搜尋演算法 (Best-First Search)、戴克斯特拉演算法(Dijkstra)、A 搜尋演算法和迭代加深 A 演算法(IDA* )等等。

*先來了解一下 A 搜尋演算法:**

A* 演算法綜合了 最佳優先搜尋演算法 (Best-First Search) 和 戴克斯特拉演算法(Dijkstra)的優點:在進行啟發式搜尋提高演算法效率的同時,可以保證找到一條最優路徑(基於評估函式) 維基百科

*A 搜尋演算法的估算函式:**

f(n) = g(n) + h(n)

g(n) 表示起點到任意點 n 的距離,h(n) 表示任意點 n 到目標點的距離,f(n) 則表示任意點 n 到起點以及目標點的和。f(n) 越小時,那麼起點到目標點的可行路徑越小。

接下來我們使用圖文來說明一下我們該如何計算:

我們可以將所有格子看作一個二維陣列,裡面分為可行以及不可行(即障礙物)。我們將起始點標記為 A 以及目標點(終點)標記為 B,此處我們忽略可斜走的情況(因為需要做各種限制,略麻煩),本文 Open List 存放所有 A 附近可行的方格,Close List 存放已行的不需要再關注的方格。

WechatIMG171.jpeg

(圖一)

可見圖一,起點 A 上下左右有四個方格,右邊格子為障礙物,再次我們則忽略它,那麼起點 A 相鄰可行的格子有上左下這三個。我們設定一個 Open List 用於存放可行的方格,以及一個 Close List 用於記錄已行方格。首先將起點 A 放進 Open List 中,然後搜尋起點 A 附近可行方格放到 Open List 中作記錄。

從上面 A 搜尋演算法的簡單瞭解,我們可知 A 搜尋演算法的估算函式是:f(n) = g(n) + h(n)

A 相鄰的長方形 f(n) 越小,則 A 到達 B 的可行路徑最短,因此我們需要選擇最小 f(n) 的長方形行走。接下來看看我們如何去計算 f(n) 的值。

為了方便計算,我們將方格的長寬設定為 1 ,如果可斜走那麼每一個的斜線為 gh2.png 。當然為了方便計算可使用長寬為 10,斜線為 14 的比例來計算。

a* 1.jpeg

(圖二)

如圖二,起點 A 有三塊可行的方格,我們標記為粉紅色,那麼首先我們計算這三個方格的 g 值。起點 A 的上左下的方格分別離 A 點距離 g(n) 為 1 ,所以標記粉紅色的上左下的方格 g(n) 值為 1。

那麼接下來計算 h(n) 值,計算 h(n) 值時忽略障礙物,即所有方格可行的情況下計算(如果可行斜線情況下,那麼在計算 h(n) 值的時候不計算斜走的情況,只計算任意點直行到終點距離)。那麼可計算出起點 A 下方的方格 h(n) 等於 7,左方 h(n) 等於 9,上方 h(n) 等於 9。那麼得出上左下三個方格的 f(n) 值:

起點 A 上方:f(n) = g(n) + h(n) = 1 + 9 = 10

起點 A 左方:f(n) = g(n) + h(n) = 1 + 9 = 10

起點 A 下方:f(n) = g(n) + h(n) = 1 + 7 = 8

由上面的計算可得出起點 A 下方的 f(n) 值為最小,那麼我們第一步走到起點 A 下方的方格。那麼將起點 A 下方的方格存到 Close List,且同時從 Open List 中移除。

a*2.jpeg

(圖三)

如圖三,我們走了第一步後 A 點去到了起點的下方一個,那個繼續去計算,由於上面起點已經存在於 Close List 以及已存在於 Open List 的格子我們不需要再關注,那麼圖上可看到 A 點接著可行點只有左右兩點,那麼計算 A 點到左邊格子 g(n) 為 2,h(n) 為 8,右邊格子 g(n) 為 2,h(n) 為 6。那麼 A 點左邊格子 f(n) 等於 10,右邊格子 f(n) 等於 8,因此我們第二步走 A 點右邊格子,將格子從 Open List 移除,存進 Close List(如圖四)。

a*3.jpeg

(圖四)

以此類推,我們最終可得出的路徑(如圖五)。

a*4.jpeg

(圖五)

如圖五,綠色路徑為可行的最短路徑,紅色標誌的則是已存在於 Open List 的方格。

基本原理就是如此,程式碼我就不一一列出來,我會放到 Github 或者看看 Jsfiddle 上面,有興趣的可以看一下,對應方法也有對應的註釋。可以看一下最終實現的 效果

動畫演示各種演算法地址:http://www.webhek.com/post/pathfinding.html

新手一枚,如果有什麼寫錯的或者不好的地方,請各位大大指點探討一下,我會不斷優化提升。

哦,最近本人在找工作,期待工作地區廣州、深圳、佛山,如有好工作或者內推等可以私聊一下我。

Nothing is impossible. —— @Jiajian Chan

相關文章