1. 程式人生 > >圖論模型(Dijkstra演算法和Floyd演算法)

圖論模型(Dijkstra演算法和Floyd演算法)

圖論模型

Dijkstra演算法

概念

Dijkstra演算法能求一個頂點到另一頂點最短路徑。它是由Dijkstra於1959年提出的。實際它能給出從起始點到其他所有頂點的最短路徑。(具體理論不在此贅述,如有需要請查閱相關文獻)1

帶權鄰接矩陣

帶權鄰接矩陣是表示頂點之間相鄰關係的矩陣。

矩陣中每個元素數值的確定遵從一下規則:

  • xab表示從a地到b地的距離
  • 如果a地與b地間雙向通行,則xab=xba
  • 如果a地到b地為單向通行,b地到a地無法直達,則xba=inf
  • 如果a地與b地兩地間無任何直達方法,則xab=
    xba=inf

程式碼

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檔案,並在同一資料夾中新建指令碼,構造帶權鄰接矩陣,並在指令碼介面下執行指令碼程式(注意不是執行函式程式)。

下面舉個例子:

Dijkstra演算法例項

要計算從v1v11的最短路徑,可以構造含帶權鄰接矩陣的指令碼如下:

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 矩陣中的元素a111=13得到最短距離為13。

對於 path 矩陣,先讀取元素b111=2,這代表需要途徑頂點 2 。接下來讀取元素b211=5,這代表接下來要途徑頂點 5 ……最後讀取元素b911=11,結束操作,得到路徑:1→2→5→6→3→7→10→9→11

以上兩結果與通過Dijkstra演算法得到的結果一致。這就體現雙演算法處理問題的優越性:如果兩演算法得到結果一致,則可以相互印證;如果結果不一致,則可以及時發現問題以查詢原因。

  1. DIjkstra演算法是一種標號法:給賦權圖的每一個頂點記一個數,稱為頂點的標號(臨時標號,稱 T 標號,或者固定標號,稱為 P 標號)。T 標號表示從起始頂點到該標點的最短路長的上界;P 標號則是從起始頂點到該頂點的最短路長。
  2. 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 的最短路徑的距離。