1. 程式人生 > >Floyd最短路徑演算法

Floyd最短路徑演算法

使用帶權圖的鄰接矩陣方法表示圖並且不能有負週期。如:

g = [
    [0,1,float('inf'),1,5],
    [9,0,3,2,float('inf')],
    [float('inf'),float('inf'),0,4,float('inf')],
    [float('inf'),float('inf'),2,0,3],
    [3,float('inf'),float('inf'),float('inf'),0]
]

其中g[i][j]表示i到j邊的權重。

Floyd方法指的是如果要從N個結點中找一條i,j兩點之間的最短路徑,首先對比兩點之間的距離與經過剩餘N-2個結點中任意一個結點k1的距離:

g[i][j]=min(g[i][j],g[i][k1]+g[k1][j])

即從i到j中途經過點k1的距離與g[i][j]進行對比,選取較小的值作為經過中途經過一個點的最短路徑。

接下來計算中途經過k1和k2兩個點時的情況,由於已經有了經過一個點時的結果,就可以在此基礎上計算經過兩個點時的最短路徑。經過兩個點時我們比較的是以下路徑中的最小值:

g[i][j]
g[i][k1] + g[k1][j]
g[i][k2] + g[k2][j]
g[i][k1] + g[k1][k2] + g[k2][j]
g[i][k2] + g[k2][k1] + g[k1][j]

由於我們有了以下經過第一個點k1時的結論:

g[i][j]  = min(g[i][j] 或g[i][k1]  + g[k1][j])   ···········1
g[i][k2] = min(g[i][k2]或g[i][k1]  + g[k1][k2])  ···········2 
g[k2][j] = min(g[k2][j]或g[k2][k1] + g[k1][j])   ···········3

經過兩個點時:

g[i][j] = min(g[i][j]或g[i][k2] + g[k2][j])
由1、2、3得:
g[i][j] = min(g[i][j]
              g[i][k1] + g[k1][j]
              g[i][k2] + g[k2][j]
              g[i][k1]  + g[k1][k2] + g[k2][j]
              g[i][k2] + g[k2][k1] + g[k1][j])

因此我們得到一般性規律,找圖中兩點i,j的最短路徑時,可先考慮兩點加任一點k1,找出圖中任意兩點經過點k1的最短路徑並生成經過k1後的圖的最短路徑矩陣。之後引入除i,j,k1點外的任一點k2,由於在經過k1的最短路徑矩陣的基礎上計算經過兩點的最短路徑,可以直接重複第一步的過程。

完整Python程式碼如下:

graph = [
    [0,1,float('inf'),1,5],
    [9,0,3,2,float('inf')],
    [float('inf'),float('inf'),0,4,float('inf')],
    [float('inf'),float('inf'),2,0,3],
    [3,float('inf'),float('inf'),float('inf'),0]
]
n = len(graph)

for k in range(n):            //經過的每個點
    for i in range(n):        
        for j in range(n):
            if graph[i][j] > graph[i][k] + graph[k][j]:  //判斷點i,j經過第0到第k-1個點時的最短路徑
                    graph[i][j] = graph[i][k] + graph[k][j]

print(graph)

其中核心程式碼只有五行三重迴圈,時間複雜度為n³,n為輸入圖的結點數。如果資料量很小或對時間要求不高時,可以使用此簡單方法生成最短路徑。