生活随笔
收集整理的這篇文章主要介紹了
迷宫寻路系列常用算法逻辑探究
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
前言:
又到了人才流動的高峰季節,“金三銀四”,過了這個村,就沒那個店,面試者勤奮地準備題典,面試官也在奮筆疾書,
有些面試官喜歡廣度的知識覆蓋,而有些面試官喜歡深度的知識探求。
筆者不是面試者,也不面試官,但想結合自身的學習和工作經歷,對深度型的題材做下嘗試和研究。
這篇讓我們談談迷宮尋路系列,分基礎篇,進階篇和難度篇。
基礎篇:
讓我們先來構造一個游戲場景:
在一個迷宮中,鼠精靈需要繞過巨石,找到迷宮中的出口,求最短路徑?
?
關于該問題,大家肯定不假思索的提到:?寬度優先遍歷(BFS)。
?
// 1).初始化工作//? ? 1.1). 把源節點坐標放入隊列中queue.push((x, y,step=0));//? ? 1.2). 標示該節點訪問過visited[(x, y)] = true// 2).BFS procedurewhile ( !queue.empty() ) {? ? // 2.1).取出當前節點? ? (x, y, step) <= queue.pop()? ? // 2.2).判斷是否為目標節點, 并返回? ? if ( (x, y) == (dest.x, dest.y) ) {? ?? ???return (step);? ? }? ? // 2.3).遍歷(x, y)的鄰近節點? ? foreach ((x', y') in neighbor(x, y)) {? ?? ???// 2.3.1).可到達且沒有訪問過? ?? ???if (is_available(x', y') and visited[(x, y)] == false) {? ?? ?? ?? ?queue.push((x', y', step + 1));? ?? ?? ?? ?// 標示訪問過? ?? ?? ?? ?visited[(x', y')] = true? ?? ???}? ?? ?? ?? ? }? ?? ?}// 3). 不存在路徑return unavailable 復制代碼
注: 判斷是否到達目標節點的代碼片段比較靈活, 為了加速可放到2。3。1) IF判斷里面。
確實很簡單,不過這是最基本的。
進階篇:
同樣的場景,如果迷宮很大,那使用BFS的話,效果就不是很高。那是否存在更高效的算法呢?
有兩種成熟而常規的實現思路: A*算法和雙向寬度優先搜索。
1)A*算法:
該算法引入啟發式評估函數,用以加速最短路徑求解過程。
核心概念:
?
- 歷史代價g(n): 從初始節點到n節點的實際代價,代表過去和現在
- 預測評估h(n): 當前節點n到目標節點的預測代價,代表未來
- 啟發評估f(n): 節點n的估價函數,其滿足f(n) = g(n) + h(n)
這邊特別要注意的一個先決條件:預測函數h(n)<= 實際的真實代價
預測函數h(n)越接近于真實代價,其啟發評估的效果越好。
更詳細的請參考如下博文“Amit's A star Page中譯文”。
這邊我們選擇曼哈頓距離作為預測函數h(n),整體的框架代碼如下:
?
// 1).初始化工作//? ? 1.1). 把源節點坐標放入優先隊列中priority_queue.push((x, y, f(x,y)=0));//? ? 1.2). 標示(x, y)的代價為0, 其余皆為無限大cost[(x, y)] = (f(x, y) = 0)// 2).BFS procedurewhile ( !priority_queue.empty() ) {? ? // 2.1).取出當前節點? ? (x, y, f(x,y)=step) <= priority_queue.pop()? ? //??過濾掉中間節點? ? if ( f(x,y) > cost[(x, y)] ) {? ?? ???continue;? ? }? ?? ?? ? // 2.2).判斷是否為目標節點, 并返回? ? if ( (x, y) == (dest.x, dest.y) ) {? ?? ???return f(x, y);? ? }? ? // 2.3).遍歷(x, y)的鄰近節點? ? foreach ((x', y') in neighbor(x, y)) {? ?? ???// 可到達且節點有更優的解? ?? ???g(x',y') = f[(x, y)] + 1;??? ?? ???if ( is_available(x', y') and (cost[(x', y')] > g(x',y') + h(x', y')) ) {? ?? ?? ?? ?priority_queue.push((x', y', f(x',y')=g(x',y')+h(x',y')));? ?? ?? ?? ?// 更新該節點的評估值? ?? ?? ?? ?cost[(x', y')] = f(x',y')=g(x',y')+h(x',y')? ?? ???}? ?? ?? ?? ? }? ?? ?}// 3). 不存在路徑return unavailable 復制代碼
注: 于BFS版相比,這邊使用優先隊列代替FIFO的隊列,并借助代價cost表代替訪問visited表。
2)雙向寬度優先遍歷:
該算法借助起點和終點的雙向寬度遍歷,手機游戲購買平臺來加速最短路徑的求解過程。
算法的流程和代碼就不再具體展開了,讓我們通過畫圖來形象地比較各個算法的優劣。
尋路算法遍歷的節點數量,可用面積來表示。圖中可得BFS是圓型,A*是橢球型,而雙向寬度搜索則是兩個剛好相切的圓形。從圖形面積對比中,我們可以獲取到各個算法優劣的視覺直觀體驗。
難度篇:
之前的場景比較普通,現在讓我們加入小怪獸來攪攪局。
在新的場景中,小怪獸按固定線路在巡視,鼠精靈需要走出迷宮的最少耗時是多少?“最短路徑”是多少?
?
在有不確定的因素的干擾下,使用常規的最短路徑算法就不再可行的。有沒有其他的解法呢?
在迷宮地圖較小時,我們可以借助動態規劃的思想來解決。
?
設opt[n][y][x]為狀態矩陣:n表示步數, (x, y)表示迷宮地圖的位置信息, 而其值表示鼠精靈在該步數后能否到達該節點.初始狀態:? ? opt[0][y][x] = true狀態遷移方程:? ?? ? opt[n+1][y][x] = (opt[n][y'][x']==true && monster[n+1] != (x', y') )==false, {ε(x',y'),adjacency to (x,y)}) ? true : false; 復制代碼
具體的偽碼如下:
// 初始化opt[0][y][x] = true;// 步數遍歷for ( step = 0; ; step++ ) {? ? // 迷宮矩陣遍歷? ? for ( i = 0; i < height; i++ ) {? ?? ???for ( j = 0; j < width; j++ ) {? ?? ?? ?? ?// 當前節點可達? ?? ?? ?? ?if ( opt[step][i][j] == true ) {? ?? ?? ?? ?? ? // 枚舉各個鄰近的可達節點? ?? ?? ?? ?? ? foreach (x', y') adjacency (j, i) {? ?? ?? ?? ?? ?? ???// 小怪獸在步數step + 1, 沒有走到該點? ?? ?? ?? ?? ?? ???if ( monster[step + 1] != (x', y') ) {? ?? ?? ?? ?? ?? ?? ?? ?opt[step + 1][y'][x'] = true;? ?? ?? ?? ?? ?? ???}? ?? ?? ?? ?? ? }? ?? ?? ?? ?}? ?? ???}? ? }? ? // 檢查目標節點是否到達? ? if ( opt[step][dest_y][dest_x] == true ) {? ?? ???return step;? ? }}return Oops; 復制代碼
注: 若該迷宮沒解,必然存在循環節,最外層循環借助滾動數組來優化。
總結:
從迷宮尋路的場景出發,逐步進行基礎知識的深挖掘,還是具備一定的區分度的。
面試這東西,能遇到一個nice的面試官是種幸福。但很多時候,往往是一場鬧劇了。
總結
以上是生活随笔為你收集整理的迷宫寻路系列常用算法逻辑探究的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。