標籤:a演算法 a星演算法 尋路演算法 js javascript
前一些天,在群有人問到A*演算法的問題。之前我已經有實現過,並將之放到github上(https://github.com/XJM2013/A_Star);有興趣的可以下載下來看看。
這裡上傳了一個相當好的A*演算法示範程式,大家可以下載下來看看效果:http://download.csdn.net/detail/a374826954/8781185。
下面描述是摘自清華大學出版社《人工智慧》一書:
評價函數的形式如下:
f(n) = g(n) + h(n)
其中,n是被評價的結點。
g*(n):表示從初始結點s到結點n的最短的耗散值;
h*(n):表示從結點n到目標結點g的最短的耗散值;
f*(n)=g*(n)+h*(n):表示從結點s經過結點n到目標結點g的最短路徑的耗散值。
而f(n)、g(n)和h(n)則分別表示是對f*(n)、g*(n)和h*(n)3個函數值的估計值,的一種預測。A演算法就是利用這種預測,來達到搜尋的目的。
當在演算法A的評價函數中,使用的啟發函數h(n)是處於h*(n)的下界範圍,即滿足h(n)≤h*(n)時,則把這個演算法稱為演算法A*。
需要說明的是,程式由於增加了一些調試資訊,稍微會比實際慢一些。
下面看一下程式示範介面:
白格子表示通路,黑格子表示阻擋。
這裡的黑格子是隨機產生,在收下代碼產生:
(function(){map.init(0.3);map.createUI();})();
隨機數為0.3,當設定為0時,則全部都是通路。
說說3個按鈕:
“設定阻擋”:按下這個按鈕後可以通過滑鼠左鍵自訂阻擋地區,方便測試。
“closed”:顯示已經被擴充並放到closed表的節點資訊。顯示資訊以C:開頭;顯示4個資料分別是f,g,h和擴充索引;需要注意的是擴充索引不一定是連續的,因為同一個節點可能被多次擴充。
“open”:顯示剩餘剩餘的open表節點資訊。顯示資訊以O:開頭;顯示3個資料分別是f,g和h。
其實一些人搞不懂A*演算法,主要是搞不懂f,g,h是怎樣取值的。
下面舉個例子:
從廣州到北京(最好開啟百度地圖對比著看,以下距離不等於實際城市間的距離)
換言之,廣州就是起始點,北京就是目標點。
廣州到廣州的距離是0,也就是g=0;廣州到北京的直線距離是5000,也就是h=5000。f=g+h,f=5000。
將廣州放進open表,擴充廣州這個節點,並將廣州從open表刪除,放進closed表;得到重慶和上海。
廣州到重慶的距離是2500,也就是重慶這個節點的g=2500;重慶到北京的直線距離是4500,也就是h=4500;f=7000。
將重慶放進open表。
廣州到上海的距離是3000,也就是上海這個節點的g=3000;上海到北京的直線距離是3000,也就是h=3000;f=6000。
將上海放進open表;由於上海的f小於重慶的f,在open表中上海節點在重慶節點前面。
擴充上海這個節點,發現找到了北京,於是搜尋結束。最後的路徑就是廣州-上海-北京。
本程式的核心代碼是:
search : function(){ while(1){ if (map.openTable.length == 0){ break; } var _fMinNode = map.openTable.shift(); if (_fMinNode.place == 2){ continue; } map.searchAround(_fMinNode); _fMinNode.index = globalIndex; globalIndex++; _fMinNode.place = 2;closedTable.push(_fMinNode); if (map.endNode == _fMinNode){ break; } } }, searchAround : function(node){ var nodeX; var nodeY; for (var x = -1; x <= 1; x++) { nodeX = node.x + x; for (var y = -1, mapNode, _obj, tmpNode; y <= 1; y++) { nodeY = node.y + y; //剔除本身 if (x === 0 && y === 0) continue; if (nodeX >= 0 && nodeY >= 0 && nodeX < map.gridWidth && nodeY < map.gridHeight) { mapNode = map.data[nodeX][nodeY]; if (mapNode.isRoadBlock){ continue; }_obj = map.getFGH(node, mapNode);do{if (mapNode.place == 2){if (_obj.G >= mapNode.obj.G){break;}}else if (mapNode.place == 1){// 單調限制if (mapNode.obj.G <= _obj.G){continue;}} mapNode.obj = _obj; mapNode.front = node; mapNode.place = 1; map.insertOpenTable(mapNode);}while(0) }; }; }; },
工作流程就是迴圈順序遍曆open表(根據f值進行排序),擴充讀取出來的節點,直到找到目標點。
其中getFGH就是計算該節點的f,g,h資訊。
你可以看到代碼裡面有寫著// 單調限制的注釋;再舉同一個例子說明一下。
廣州還擴充出一個節點:武漢。
武漢的f=5900,g=2400,h=3500。這時候是先擴充武漢,於是又找到了上海。
武漢到上海的距離是1900。這時候經過武漢到上海的節點資訊是g=2400+1900=4300,h是不變的也是3000。
上海節點mapNode之前儲存的g=3000,現在算出經過武漢到上海的距離是g=4300。因此經過武漢到上海比直接到上海的距離遠,因此不將f,g,h替換放到open表。
這就是單調限制。
下面是本篇文章代碼及示範程式的:
A*演算法的不足:
當目標點是位於死路地區,則A*演算法會遍曆整個地圖。這明顯是低效率的。
A*尋路演算法(曼哈頓距離)