Bellman-Ford 演算法 和 動態規劃
阿新 • • 發佈:2018-11-27
Floyd演算法:
狀態:
d[k][i][j]定義:“只能使用第1號到第k號點作為中間媒介時,點i到點j之間的最短路徑長度。”
動態轉移方程:
Bellman-Ford 演算法:
狀態:
d[k][u]定義:從源點v出發最多經過不構成負權值迴路的k條邊到達終點u的最短路徑的長度
動態轉移方程:
從上面可以看出這兩種演算法,對於子問題考慮前者考慮的是基於點的中轉,後者考慮是基於邊的中轉,基於邊的中轉需要考慮不形成迴路,求最小的話,非負數情況下,肯定是沒有迴路的,存在負權迴路,就沒有最小值,需要檢測是否有負權迴路。
Bellman-Ford 演算法基於動態規劃的原始實現:
#%%
# dist[k][u] = min{ dist[k-1][u] ,min{dist[k-1][j]+Edge[j][u]} },j = 0,1,...,n-1;j!=u
import numpy as np
def Bellman_Ford_original(graph,start):
vertex_num = len(graph)
edge_num_ = vertex_num-1
list = np.full((edge_num_+1,vertex_num+1),np.inf)
# for i in range(1,vertex_num+1):
# list[1,i] = graph[start,i-1]
list[:,start+1] =0
for k in range(1,edge_num_+1):
for u in range(1,vertex_num+1):
result = list[k-1,u]
for j in range(1,vertex_num+1):
if graph[j-1,u-1]>0 and graph[j-1,u-1]<10000 and result > list[k-1,j] + graph[j-1,u-1]:
result = list[k-1,j] + graph[j-1,u-1]
list[k,u] =result
return list[k,1:]
使用滾動陣列加入空間優化:list[k-1,u],list[k-1,j],假如使用一維陣列的話,list[k-1,u]是未更新的主句沒問題,list[k-1,j]就不好說了,既有可能是更新過的資料,也有可能是未更新的資料,理論上應該不能用滾動陣列優化,但是:只要資料可以被更新,就代表v到u的距離可以邊更小,是不影響整體資料向更小方向推進的。
使用一維陣列優化版本,加入了追蹤解實現:
def Bellman_Ford(graph,start):
vertex_num = len(graph)
edge_num_ = vertex_num-1
list = np.full((vertex_num+1),np.inf)
list[start+1] = 0
path =[-1] * vertex_num
for i in range(vertex_num):
if graph[start,i] < 10000:
path[i] = start
path[start] = None
# for i in range(1,vertex_num+1):
# list[i] = graph[start,i-1]
for k in range(1,edge_num_+1):
flag = True
for u in range(1,vertex_num+1):
for j in range(1,vertex_num+1):
w = graph[j-1,u-1]
if w>0 and w <10000 and list[u] > list[j] + w :
list[u] = list[j] + w
path[u-1] = j-1
flag = False
# 提前退出,並檢查是否有負迴路
if flag == True:
for u in range(1,vertex_num+1):
for j in range(1,vertex_num+1):
if list[u] > list[j] + graph[j-1,u-1] :
print "there is a - cycle"
break
break
return list[1:],path
執行結果:
graph = np.full((7,7),np.inf)
graph[0,:3] = [0,-1,2]
graph[1,:4] = [-1,0,3,4]
graph[2,:5] = [2,3,0,5,6]
graph[3,1:6] = [4,5,0,7,8]
graph[4,2:6] = [6,7,0,9]
graph[5,3:7] = [8,9,0,10]
graph[6,5:7] = [10,0]
print Bellman_Ford_original(graph,6)
value,path = Bellman_Ford(graph,6)
print value
print path
[25. 22. 23. 18. 19. 10. 0.]
there is a - cycle
[25. 22. 23. 18. 19. 10. 0.]
[2, 3, 3, 5, 5, 6, None]
另外一種理解,從鬆弛的角度:
第一,初始化所有點。每一個點儲存一個值,表示從原點到達這個點的距離,將原點的值設為0,其它的點的值設為無窮大(表示不可達)。
第二,進行迴圈,迴圈下標為從1到n-1(n等於圖中點的個數)。在迴圈內部,遍歷所有的邊,進行鬆弛計算。
第三,遍歷途中所有的邊(edge(u,v)),判斷是否存在這樣情況:
d(v)>d(u) + w(u,v)
則返回false,表示途中存在從源點可達的權為負的迴路。
之所以需要第三部分的原因,是因為,如果存在從源點可達的權為負的迴路。則應為無法收斂而導致不能求出最短路徑。