深入解析最短路徑演算法
正文第一節 問題的提出及解決方法
所謂最短路徑問題,可以說有兩種情況來描述。
描述一:在圖論中,指的是尋找圖中兩個節點之間的最短距離。如下圖
描述二:在現實生活中,指的是找到從乙個地方到另乙個地方的最近距離。如下圖
上述兩種情況的本質是一樣的,即求乙個點到另乙個點的最短路徑。好了,問題已經提出來了,那怎麼解決呢?解決該問題的方法還是比較多的,不過由於各個路徑演算法所對應的問題條件不同,我們可根據不同的情況,選擇不同的路徑演算法。
本文將介紹三種最短路徑演算法,分別是:戴克斯特拉演算法(dijkstra algorithm),弗洛伊德演算法(floyd algorithm)以及a*搜尋演算法。
第二節 戴克斯特拉演算法(dijkstra algorithm)
該演算法解決的是有向圖中單個源點到其他頂點的最短路徑問題。
戴克斯特拉演算法的實現過程如下:
第一步:用帶權的矩陣weiarcs來表示帶權有向圖,如果圖中的兩個頂點vi和vj是連通的,則用weiarcs[i][j]表示這兩個頂點所形成邊的權值;如果vi和vj不連通,即這條邊不存在,那麼將weiarcs[i][j]置為∞。
第二步:設s為已求得的從某一頂點v始發的最短路徑的終點的集合,且s的初始狀態為空,初始化時,將始發頂點置於s集合中。那麼從v出發到圖中其餘各個頂點vi可能達到的最短路徑長度的初值為d[i]。
第三步:選擇一頂點vj,使得vj就是當前求得的一條從頂點v出發的最短路徑的終點。此時令s = s ∪ 。
第四步:修改從v出發到集合v-s(v為圖頂點的集合)中任一頂點vk可達的最短路徑長度。如果d[j]+weiarcs[j][k] < d[k],則d[k] = d[j] + weiarcs[j][k]。
第五步:重複操作第三步、第四步共n-1次,由此就能求得從v出發到圖中其餘各個頂點的最短路徑。
好了,實現過程就是這樣。不過光有文字描述不行,要更直白的表達這個過程,我認為用影象表述是乙個很好的選擇。如下圖所示
從運算過程表中,我們可知v0到其餘個點的最短路徑,如下圖
上述過程描述的戴克斯特拉演算法的**如下:
int shortpath(mgraph g,int v0,pathmatrix &p,shortpathtable &d) } d[v0] = 0;final[v0] = true; //初始化,v0頂點屬於s集合 //開始主迴圈,每次求得v0到某個頂點v的最短路徑,並將v加到s集合中 for(i = 1; i < g.vexnum; i++) //其餘g.vexnum - 1個頂點 } } final[v] = true; //離v0頂點最近的v加入到s中 for(w = 0;w < g.vexnum;w++) //更新當前最算路徑及距離 } } return 0; }
ok,dijkstra algorithm介紹完了。
第三節 弗洛伊德演算法(floyd algorithm)
該演算法解決的是有向帶權圖中兩頂點之間最短路徑的問題。
弗洛伊德演算法的設計過程如下:
用帶權的矩陣weiarcs來表示帶權有向圖,如果圖中的兩個頂點vi和vj是連通的,則用weiarcs[i][j]表示這兩個頂點所形成邊的權值;如果vi和vj不連通,即這條邊不存在,那麼將weiarcs[i][j]置為∞。
要求:求節點vi到節點vj的最短路徑。
設d(i,j,k)為從節點vi到節點vj的以vk(vk∈(0,1,...k))節點為中間節點的最短路徑的長度。例如:從vi到vj這條路徑上經過節點vm和節點vk,那麼可表示為:vi-->vm-->vk-->vj。
那麼,就有:1.若最短路徑經過節點vk,則d(i,j,k) = d(i,k,k-1) + d(k,j,k-1);
2.若最短路徑不經過節點vk,則d(i,j,k) = d(i,j,k-1)。
所以,求的vi到vj的最短路徑可表示為:
d(i,j,k) = min(d(i,k,k-1) + d(k,j,k-1), d(i,j,k-1))。
老辦法,圖示的過程如下:
求解的過程見下圖:
上述過程描述的弗洛伊德演算法的**如下:
int shortpath(mgraph g,int v0,pathmatrix &p,shortpathtable &d) } for(u = 0;u < g.vexnum;u++) for(v = 0;v < g.vexnum;v++) for(w = 0;w < g.vexnum;w++) return 0; }
第四節 a*搜尋演算法
a*搜尋演算法,俗稱a星演算法。這是一種在圖平面上,有多個節點的路徑,求出最低通過成本的演算法。常用於遊戲中的npc的移動計算,或線上遊戲的bot的移動計算上。該演算法像dijkstra演算法一樣,可以找到一條最短路徑;也像bfs一樣,進行啟發式的搜尋。
a*演算法最核心的部分,就在於它的乙個估值函式的設計上:f(n)=g(n)+h(n)。其中,g(n)表示從起始點到任一點n的實際距離,h(n)表示任意頂點n到目標頂點的估算距離,f(n)是每個可能試探點的估值。這個估值函式遵循以下特性:
•如果h(n)為0,只需求出g(n),即求出起點到任意頂點n的最短路徑,則轉化為單源最短路徑問題,即dijkstra演算法;
•如果h(n)<=「n到目標的實際距離」,則一定可以求出最優解。而且h(n)越小,需要計算的節點越多,演算法效率越低。
我們可以這樣來描述:從出發點(startpoint,縮寫成sp)到終點(endpoint,縮寫成ep)的最短距離是一定的,於是我們可以寫乙個估值函式來估計出發點到終點的最短距離。如果程式嘗試著從出發點沿著某條線路移動到了路徑上的另乙個點(otherpoint,縮寫成op),那麼我們認為這個方案所得到的從sp到ep間的估計距離為:從sp到op實際已走的距離加上估計函式估出的從op到ep的距離。如此,無論我們的程式搜尋展開到哪一步,都會得到乙個估計值,每一次決策後,將評估值和等待處理的方案一起排序,然後挑出待處理的各個方案中最有可能是最短路線的一部分的方案展開到下一步, 一直迴圈直到物件移動到目的地,或所有方案都嘗試過,卻沒有找到一條通向目的地的路徑則結束。
a*搜尋演算法的**過程請看:
第五節 相關說明
各演算法完整測試程式見我的另一篇文章:
第六節 結束語
想想,寫寫,畫畫......
零零散散的筆記
最近聽課的內容,雖然不全面。static void main string args 主函式 裝箱轉換 允許將值型別隱式轉換為引用型別 建立float型別,應使用f字尾 convert 將乙個基本資料型別轉換為另乙個基本資料型別 是求餘數 i 是i 1,先賦值,後運算 i是i 1,先運算,後賦值 i...
零零散散的問題
1.無法從 const char 7 轉換為 lpcwstr ans 工程使用了unicode字符集,把你的 字串 包含的字串修改為 t 字串 或者不使用unicode字符集即可 例如 abc.lpszclassname fb2009 應該改為 abc.lpszclassname t fb2009 ...
零零散散學演算法之再敘字串匹配
出自 字串匹配問題這是個老話題了,而我們也熱衷於學習和 這個問題,並且我們也經常會用到它。比如說,我們用vim開啟乙個文字檔案,要在這個檔案中查詢某乙個字串時,我們只需在底行模式下輸入 string即可 再比如,在linux終端中,我們要把當前目錄下所有的c檔案列印出來,那麼這時候我們就會利用正規表...