1. 程式人生 > >A*尋路演算法的優化與改進

A*尋路演算法的優化與改進

提要

通過對上一篇A*尋路演算法的學習,我們對A*尋路應該有一定的瞭解了,但實際應用中,需要對演算法進行一些改進和優化。

Iterative Deepening Depth-first search- 迭代深化深度優先搜尋

在深度優先搜尋中一個比較坑爹情形就是在搜尋樹的一枝上沒有要搜的結果,但是卻非常深,甚至深不見底,這樣就根本搜尋不到結果。為了防止這種情況出現,就出現了Iterative Deepening的思想。

迭代深化搜尋(Iterative deepening search, IDS)或者(迭代深化深度優先搜尋,Iterative deepening depth-first search)是一種常用的搜尋機制,經常使用在深度優先搜尋中.通過逐漸地提高深度限制搜尋(Depth-limited search)的深度限制(depth limit)——從1開始,然後2,一直到找到目標結點位置為止——迭代深化搜尋能夠找出最好的深度限制.深度限制搜尋指的是在深度優先搜尋中,引入深度限制limit,如果從根結點出發到結點N的深度為limit,那麼N被當做沒有子結點的葉結點那樣處理.

找一棵樹來具體看一下。


需要找ee節點。

首先把深度限制設定為1,則要搜尋的樹為


對其進行深度優先搜尋,沒搜到,增加深度限制為2.


進行DFS還是沒搜尋到,繼續增加深度限制到3.


進行DFS,Bingo,找到。

和直接用DFS一樣啊,看起來傻傻的? No!

這個方法首先規避了最開始說的DFS深度的問題,相對於BFS一般需要儲存產生的所有結點,佔的儲存空間要比深度優先大得多,ID-DFS它的記憶體佔用又少很多。再來看看時間複雜度.


迭代深化搜尋也許看起來比較浪費時間,因為狀態有可能被多次產生.但是事實並非如此,因為大多數的結點處在底層,而底層結點的搜尋次數很少.對於一個深度為d,分支因子為b的樹,.最多需要搜尋的結點個數為:


故此迭代深化搜尋的時間複雜度和深度優先搜尋的一樣是O( bk)

So... 這個演算法是兩種搜尋方法的折中,BFS能搜尋到的,它就一定能搜到,而且不用那麼多的空間,只需犧牲一點點的時間(相同的節點可能要訪問很多次)。

下面是一個一般一些的例子,包含了三種演算法的對比。


Iterative Deepening A*

迭代延伸A*可以簡寫成IDA*,用的也是迭代延伸的思想,這裡的bounding就不再是樹的深度了,而是 f(n) 的值。

首先還是從s出發,,bounding的值就是f(s),接下來就是從s進行深度優先搜尋,f() 值大約閾值的全部不管,沒有找到的話就增大閾值,再進行DFS....直到最後找到最終的節點。

很明顯,IDA*用深度優先搜尋替代了Open List 和Close List, 減少了記憶體上的開銷,也少了List維護的花費。雖然每次搜尋都要從頭開始,這看上去一次又一次去搜索同樣的節點有點不合理,但是這個代價遠遠低於原版本中維護Open List和Close List. 

邊緣搜尋A*

A*演算法最大的效能問題就是Open List和Close List的維護,IDA*最大的問題就是無法記憶維護歷史,會重複搜尋節點,一個新的尋路演算法 - 邊緣搜尋A*,A*和IDA*的折中。

它維護了兩個List,Now和Later來記錄搜尋的邊緣點,同時用IDA*的思想來推進,具體看一下虛擬碼。

now - linked list of search nodes, list order determines order of evaluation
later - linked list of search nodes
root - start node
threshold = root's g()
push root into now

while now not empty
	for each node in now
		if node == goal
			stop
		if node's f() > threshold
			push node onto end of later
		else
			insert children of node into now behind node
		
		remove node from now and discard
		
	push later onto now, clear later
	set threshold = minimum g() found that is higher than current threshold

now中存放的是當先需要被評估的節點,later中存放的是下一次將要評估的節點。

這個過程以比較弱的排序來維護列表,並且以像IDA*的深度優先的方式來有效地擴充套件節點,如果一次完成遍歷now之後沒有找到目標,則增加threshold值,later列表變成now列表,搜尋又從now列表的頂點開始。雖然搜尋過程需要now和later列表的維護,卻沒有排序的開銷,而且記憶體的消耗比A*少太多。

參考

Artificial Intelligence 3.7 - http://www.cs.ubc.ca/~poole/aibook/html/ArtInt_62.html

Iterative Deepening - http://www.comp.lancs.ac.uk/computing/research/aai-aied/people/paulb/old243prolog/subsection3_6_4.html

Game Programming Gems 3.7