圖論模型(Dijkstra演算法和Floyd演算法)
圖論模型
Dijkstra演算法
概念
Dijkstra演算法能求一個頂點到另一頂點最短路徑。它是由Dijkstra於1959年提出的。實際它能給出從起始點到其他所有頂點的最短路徑。(具體理論不在此贅述,如有需要請查閱相關文獻)1
帶權鄰接矩陣
帶權鄰接矩陣是表示頂點之間相鄰關係的矩陣。
矩陣中每個元素數值的確定遵從一下規則:
- 表示從a地到b地的距離
- 如果a地與b地間雙向通行,則
- 如果a地到b地為單向通行,b地到a地無法直達,則
- 如果a地與b地兩地間無任何直達方法,則
程式碼
function [min, path] = dijkstra(w, start, terminal)
n = size(w, 1); label(start) = 0; f(start) = start;
for i = 1 : n
if i ~= start
label(i) = inf;
end, end
s(1) = start; u = start;
while length(s) < n
for i = 1 : n
ins = 0;
for j = 1 : length(s)
if i == s(j)
ins = 1;
end,
end
if ins == 0
v = i;
if label(v) > (label(u) + w(u, v))
label(v) = (label(u) + w(u, v));
f(v) = u;
end,
end,
end
v1 = 0;
k = inf;
for i = 1 : n
ins = 0 ;
for j = 1 : length(s)
if i == s(j)
ins = 1;
end,
end
if ins == 0
v = i;
if k > label(v)
k = label(v); v1 = v;
end,
end,
end
s(length(s) + 1) = v1;
u = v1;
end
min = label(terminal); path(1) = terminal;
i = 1;
while path(i) ~= start
path(i + 1) = f(path(i));
i = i + 1;
end
path(i) = start;
L = length(path);
path = path(L : -1 : 1);
end
操作
在一個資料夾中放入上述程式碼構成的m檔案,並在同一資料夾中新建指令碼,構造帶權鄰接矩陣,並在指令碼介面下執行指令碼程式(注意不是執行函式程式)。
下面舉個例子:
要計算從到的最短路徑,可以構造含帶權鄰接矩陣的指令碼如下:
weight = [ 0 2 8 1 inf inf inf inf inf inf inf;
2 0 6 inf 1 inf inf inf inf inf inf
8 6 0 7 5 1 2 inf inf inf inf;
1 inf 7 0 inf inf 9 inf inf inf inf;
inf 1 5 inf 0 3 inf 2 9 inf inf;
inf inf 1 inf 3 0 4 inf 6 inf inf;
inf inf 2 9 inf 4 0 inf 3 1 inf;
inf inf inf inf 2 inf inf 0 7 inf 9;
inf inf inf inf 9 6 3 7 0 1 2;
inf inf inf inf inf inf 1 inf 1 0 4;
inf inf inf inf inf inf inf 9 2 4 0;];
[dis, path] = dijkstra(weight, 1, 11) %1和11為始末點
執行後即可得到最短路徑長為13,步驟為:1→2→5→6→3→7→10→9→11
Floyd演算法
概念
Floyd演算法是一個經典的動態規劃演算法。用通俗的語言來描述的話,首先我們的目標是尋找從點 i 到點 j 的最短路徑。從動態規劃的角度看問題,我們需要為這個目標重新做一個詮釋(這個詮釋正是動態規劃最富創造力的精華所在)(同樣的,具體理論不在此贅述,如有需要請查閱相關文獻)2
程式碼
function [D, path, min1, path1] = floyd(a, start, terminal)
D = a; n = size(D, 1); path = zeros(n, n);
for i = 1 : n
for j = 1 : n
if D(i, j) ~= inf
path(i, j) = j;
end,
end,
end
for k = 1 : n
for i = 1 : n
for j = 1 : n
if D(i, k) + D(k, j) < D(i, j)
D(i, j) = D(i, k) + D(k, j);
path(i, j) = path(i, k);
end,
end,
end,
end
if nargin == 3
min1 = D(start, terminal);
m(1) = start;
i = 1;
path1 = [ ];
while path(m(1), terminal) ~= terminal
k = i + 1;
m(k) = path(m(i), terminal);
i = i + 1;
end
m(i + 1) = terminal;
path1 = m;
end
操作
與Dijkstra演算法一樣,需要構造帶權鄰接矩陣。指令碼檔案和上述程式碼構成的m檔案也應在同一目錄下。構造帶權鄰接矩陣遵循的規則同Dijkstra演算法。
但與Dijkstra演算法不同的是,指令碼執行後的結果不同。運用Dijkstra演算法進行計算能夠直接得到最短路程長和具體步驟;而用Floyd演算法計算後會得到兩個矩陣。一個是 D 矩陣,一個是 path 矩陣,接下來詳細說明一下:
同樣是對於Dijkstra演算法中的例項,我們通過Floyd演算法來解。構造指令碼如下:
weight = [ 0 2 8 1 inf inf inf inf inf inf inf;
2 0 6 inf 1 inf inf inf inf inf inf
8 6 0 7 5 1 2 inf inf inf inf;
1 inf 7 0 inf inf 9 inf inf inf inf;
inf 1 5 inf 0 3 inf 2 9 inf inf;
inf inf 1 inf 3 0 4 inf 6 inf inf;
inf inf 2 9 inf 4 0 inf 3 1 inf;
inf inf inf inf 2 inf inf 0 7 inf 9;
inf inf inf inf 9 6 3 7 0 1 2;
inf inf inf inf inf inf 1 inf 1 0 4;
inf inf inf inf inf inf inf 9 2 4 0;];
[D, path] = floyd(weight)
執行後得到:
D =
0 2 7 1 3 6 9 5 11 10 13
2 0 5 3 1 4 7 3 9 8 11
7 5 0 7 4 1 2 6 4 3 6
1 3 7 0 4 7 9 6 11 10 13
3 1 4 4 0 3 6 2 8 7 10
6 4 1 7 3 0 3 5 5 4 7
9 7 2 9 6 3 0 8 2 1 4
5 3 6 6 2 5 8 0 7 8 9
11 9 4 11 8 5 2 7 0 1 2
10 8 3 10 7 4 1 8 1 0 3
13 11 6 13 10 7 4 9 2 3 0
path =
1 2 2 4 2 2 2 2 2 2 2
1 2 5 1 5 5 5 5 5 5 5
6 6 3 4 6 6 7 6 7 7 7
1 1 3 4 1 1 7 1 7 7 7
2 2 6 2 5 6 6 8 6 6 6
5 5 3 5 5 6 3 5 3 3 3
3 3 3 4 3 3 7 3 10 10 10
5 5 5 5 5 5 5 8 9 9 11
10 10 10 10 10 10 10 8 9 10 11
7 7 7 7 7 7 7 9 9 10 9
9 9 9 9 9 9 9 8 9 9 11
對於 D 矩陣,比如例項要求我們得到 1 到 11 的最短路徑,那麼我們讀取 D 矩陣中的元素得到最短距離為13。
對於 path 矩陣,先讀取元素,這代表需要途徑頂點 2 。接下來讀取元素,這代表接下來要途徑頂點 5 ……最後讀取元素,結束操作,得到路徑:1→2→5→6→3→7→10→9→11
以上兩結果與通過Dijkstra演算法得到的結果一致。這就體現雙演算法處理問題的優越性:如果兩演算法得到結果一致,則可以相互印證;如果結果不一致,則可以及時發現問題以查詢原因。
- DIjkstra演算法是一種標號法:給賦權圖的每一個頂點記一個數,稱為頂點的標號(臨時標號,稱 T 標號,或者固定標號,稱為 P 標號)。T 標號表示從起始頂點到該標點的最短路長的上界;P 標號則是從起始頂點到該頂點的最短路長。 ↩
- Floyd演算法思想原理:
從任意節點 i 到任意節點 j 的最短路徑無外乎兩種可能:1、直接從 i 到 j ;2、從 i 經過若干個節點 k 到 j 。所以,我們假設 Dis(i, j)為節點 u 到節點 v 的最短路徑的距離,對於每一個節點 k ,我們檢查Dis(i, k) + Dis(k, j) Dis(i, j)是否成立,如果成立,證明從 i 到 k 再到 j 的路徑比 i 直接到 j 的路徑短,我們便設定DIs(i, j) = Dis(i, k) + Dis(k, j),這樣一來,當我們遍歷完所有節點 k ,Dis(i, j)中記錄的便是 i 到 j 的最短路徑的距離。 ↩