廣度優先(bfs)和深度優先(dfs)搜尋
深度優先搜尋,用俗話說就是不見棺材不回頭。演算法會朝乙個方向進發,直到遇到邊界或者障礙物,才回溯。一般在實現的時候,我們採用遞迴的方式來進行,也可以採用模擬壓棧的方式來實現。
如下圖,s代表起點,e代表終點。我們如果按照右、下、左、上這樣的擴充套件順序的話,演算法就會一直往右擴張,直到走到地圖的右邊界,發現沒找到目標點,然後再回溯。
這個演算法的好處就是實現簡單,可能就十幾行**。不過問題也很明顯,就是:
1、路徑可能不是最優解;
2、尋路時間比較長。
廣度優先搜尋,這個用形象的比喻,就像是**波,從起點向外輻射,直到找到目標點。我們在實現的時候,一般採用佇列來實現。
這個演算法的優點:
1、簡單。**也就幾十行;
2、路徑能找到最優解;
不足:1、演算法消耗的時間比較大,遍歷的點會很多。
這裡就引出乙個問題:為什麼廣度優先演算法能找到最優路徑,但是卻很耗時呢?
a*演算法
廣度優先搜尋之所以能找到最優的路徑,原因就是每一次擴充套件的點,都是距離出發點最近、步驟最少的。如此這樣遞推,當擴充套件到目標點的時候,也是距離出發點最近的。這樣的路徑自然形成了最短的路線。
任何事情都有正反兩面。正是由於廣度優先搜尋一層層的擴充套件,雖然讓他找到了最優的路線,但是,他卻很傻的走完了絕大多數格仔,才找到我們的目標點。也就是,他只關注了當前擴充套件點和出發點的關係,而忽略了當前點和目標點的距離。如果,如果,如果……我們每擴充套件乙個點,就踮起腳尖,看看詩和遠方,找找我們要尋找的那個目標,是不是就有可能指引我們快速的去往正確的方向,而不用傻乎乎的一層層的發展了呢?
我們來看看下圖:
同樣是從出發點s走了兩步以後到達的m1和m2兩個點,如果讓你來選擇,你會選擇他們中的誰來做擴充套件點呢?很明顯,只要是眼力不差的人,都會選擇m1。為什麼呢?因為m2需要再走9步,才能到達終點e;而m1只需要7步!!!
注意了!我們的判斷依據,除了考慮了中間這個點同出發點的距離以外,還考慮了這個點同目標點的距離,對吧~
如果你想到了這一點,恭喜你,你已經掌握了a*演算法的秘訣了:a*演算法相對廣度優先搜尋演算法,除了考慮中間某個點同出發點的距離以外,還考慮了這個點同目標點的距離。這就是a*演算法比廣度優先演算法智慧型的地方。也就是所謂的啟發式搜尋。
我們簡單的抽象一下,如果用f(m)表示:從起點s到終點e(經過m點)的距離,那他就可以表示成為兩段距離之和,即:s→m的距離 + m→e的距離。如果我們用符號表示的話,就可以寫成:f(m) = g(m) + h(m)。
怎麼樣,看起來這個公式是否是很簡單呢?
我們擴充套件到m點的時候,s→m的距離就已經知道,所以g(m)是已知的。但是m到e的距離我們還不知道。如果我們能用某種公式,能大概**一下這個距離,而這個**的值又比較精確,我們是不是就能很精確的知道每乙個即將擴充套件的點是否是最優的解路徑上的點呢?這樣找起路來,是不是就很快呢?
所以,接下來最關鍵的問題,就是怎麼計算這個h(m)的值!
這個問題問的很好,但是結論是:既對,又不對。如果按照我們之前的圖來看,這個結論是正確的。但是,如果是下面這張圖呢?
在m和e之間,有一堵藍色的牆,這個時候,m→e的距離,還是橫向的直線距離 + 縱向的直線距離嘛?明顯不是了,他需要繞道!
這個時候,似乎希望破滅了……
前兩天有個朋友給我說,兩口子的相處之道,就是相互包容,不要太較真兒。如果我們將這個思想用到這裡,把h(m)看做乙個估計的值,而不是精確值,那問題是不是就解決了呢?
也就是說,我們盡可能找那些f(m)=g(m)+h(m)小的點(其中h(m)是個估算值),當做我們的路徑經過點,即使實際的h'(m)值可能和h(m)值不等也沒關係,我們就當做乙個參考(總比廣度優先搜尋好吧~)。如果通過這個估算,能乾掉很多明顯很差的點,我們也就節省了很多不必要的花銷,也算賺到了,對吧~
比如,上圖中, m點即使是繞路,也比m'點要強,對吧。在估算的時候,我們就可以將s左邊的點基本上都拋棄掉,從而減少我們擴充套件的點數,節約計算的時間。
說完上面的東東,我們大麵兒上的東西就說的差不多了,接下來就省兩個問題要去解決了:
1、這個估算的函式h(m)怎麼樣去計算?
2、對於不同的估算函式h(m)來講,對於我們的搜尋結果會有什麼樣的影響?
那我麼乙個個的來回答吧。
估算函式h(m)如何計算?
常見的距離計算公式有這麼幾種:
1、曼哈頓距離:這個名字聽起來好高階,說白了,就是上面我們講的橫向格仔數+縱向格仔數;
2、歐式距離:這個名字聽起來也很高階,說白了,就是兩點間的直線距離sqrt((x1-x2)2 + (y1-y2)2)
除了上述的距離計算公式以外,還有一些變種的距離計算公式,如:對角線距離等等。這個就在具體的問題中做具體的優化了。
不同估算函式對於結果的影響
那距離公式選擇不同,對我們的尋路結果有哪些影響呢?
1、當估算的距離h完全等於實際距離h'時,也就是每次擴充套件的那個點我們都準確的知道,如果選他以後,我們的路徑距離是多少,這樣我們就不用亂選了,每次都選最小的那個,一路下去,肯定就是最優的解,而且基本不用擴充套件其他的點。如下圖:
2、如果估算距離h小於實際距離h'時,我們到最後一定能找到一條最短路徑(如果存在另外一條更短的評估路徑,就會選擇更小的那個),但是有可能會經過很多無效的點。極端情況,當h==0的時候,最終的距離函式就變成:
f(m)=g(m)+h(m)
=> f(m)=g(m)+0
=> f(m)=g(m)
這不就是我們的廣度優先搜尋演算法嘛?! 他只考慮和起始點的距離關係,毫無啟發而言。
3、如果估算距離h大於實際距離h'時,有可能就很快找到一條通往目的地的路徑,但是卻不一定是最優的解。
因此,a*演算法最後留給我們的,就是在時間和距離上需要考慮的乙個平衡。如果要求最短距離,則一定選擇h小於等於實際距離;如果不一定求解最優解,而是要速度快,則可以選擇h大於等於實際距離。
dijkstra 演算法理解
求有向圖中乙個源點到其他頂點的最短距離 自己的理解 剛開始的時候相當於有三個集合 v 頂點集合 s 已求得的最短距離頂點集合,假設求a到其他頂點的最短距離 s 剩餘頂點集合 d 儲存的是最短距離值 求一維陣列中的最小值的下標,這個陣列不包含已經求得的頂點 找出最小值下標後,放入到s中,然後求剩餘頂點...
KM演算法理解
二分圖帶權匹配與最佳匹配 什麼是二分圖的帶權匹配?二分圖的帶權匹配就是求出乙個匹配集合,使得集合中邊的權值之和最大或最小。而二分圖的最佳匹配則一定為完備匹配,在此基礎上,才要求匹配的邊權值之和最大或最小。二分圖的帶權匹配與最佳匹配不等價,也不互相包含。我們可以使用km演算法實現求二分圖的最佳匹配。方...
遞迴演算法理解
遞迴演算法看起來比較簡單,當總覺得沒能領會到它的精髓,平常也沒可以使用它。今天看到這篇文章,說的比較透徹 1,遞迴與迴圈之間的關係 看過這樣一道題,問,程式結構化設計的三種基礎結構,順序 選擇 迴圈是不是必須的?當然,你知道這樣乙個論斷,只要有這三種就足夠了 但是能不能更少呢?答案是 可以 原因就是...