參考:坐在馬桶上看算法:只有五行的Floyd最短路算法
http://developer.51cto.com/art/201403/433874.htm
問題描述:
求解各個站點兩兩之間的最短距離
路徑圖
關鍵代碼:
for k in range(length): #可以經過k點
for i in range(length): #[i,j]點遍歷
for j in range(length):
if D[i,j] > (D[i,k]+D[k,j]):
D[i,j] = D[i,k]+D[k,j] #兩個頂點直接較小的間接路徑替換較大的直接路徑
path[i,j]=k #記錄前驅路徑
思路:
- 最外層循環k表示可以經過【0,1,2...k】點時,修正一下最短路
- 中間循環i,j表示點集兩兩之間若經過k點是否能更短
- 如果更短,則更新兩點最短距離,記錄k點。
理解:
可以理解為,允許經過0~k點的情況下的最短距離;
在現有路徑下,從i點到j點,走從k點周轉一下更近;
而i到k、k到j可能不是直達;
搜尋i→j路線時,先找到【i,j】之間的k,再找【i,k】【k,j】的路線,以此類推
完整例子
import numpy as np
D=np.array([0,2,6,4,np.inf,0,3,np.inf,7,np.inf,0,1,5,np.inf,12,0]).reshape([4,4])
print('InitialDis:\n',D)
length=D.shape[0] #一共有多少個點
path=np.zeros((length,length))-1
for k in range(length): #可以經過k點
for i in range(length): #[i,j]點遍歷
for j in range(length):
if D[i,j] > (D[i,k]+D[k,j]):
D[i,j] = D[i,k]+D[k,j] #兩個頂點直接較小的間接路徑替換較大的直接路徑
path[i,j]=k #記錄前驅路徑
print('Distance:\n',D)
print('PathSearch:\n',path)
InitialDis:
[[ 0. 2. 6. 4.]
[ inf 0. 3. inf]
[ 7. inf 0. 1.]
[ 5. inf 12. 0.]]
Distance:
[[ 0. 2. 5. 4.]
[ 9. 0. 3. 4.]
[ 6. 8. 0. 1.]
[ 5. 7. 10. 0.]]
PathSearch:
[[-1. -1. 1. -1.]
[ 3. -1. -1. 2.]
[ 3. 3. -1. -1.]
[-1. 0. 1. -1.]]
以2→1為例,直觀上看路徑為2→3→4→1,長度為3+1+5=9
在Distance矩陣中,D[1,0]=9
在Path矩陣中,D[1,0]=3→D[1,3]=2/D[3,0]=-1→D[1,2]=-1,∴1→2→3→0
#根據距離矩陣獲取路徑
def getPath(i,j,path,out): #只想打印路徑的話不用out,這里out為傳址調用
if i==j:
return
elif path[i,j]==-1:
out.append(j)
print(j)
else:
getPath(i,int(path[i,j]),path,out)
getPath(int(path[i,j]),j,path,out)
out=[1]
getPath(1,0,path,out)
print(out)
2
3
0
[1, 2, 3, 0]