Bellman-Ford 算法 和 动态规划
Floyd算法:
狀態:
d[k][i][j]定義:“只能使用第1號到第k號點作為中間媒介時,點i到點j之間的最短路徑長度?!?/p>
動態轉移方程:
d[k][i][j]=min(d[k?1][i][j],d[k?1][i][k]+d[k?1][k][j])(k,i,j∈[1,n])d[k][i][j] = min(d[k-1][i][j], d[k-1][i][k]+d[k-1][k][j])(k,i,j∈[1,n])d[k][i][j]=min(d[k?1][i][j],d[k?1][i][k]+d[k?1][k][j])(k,i,j∈[1,n])
Bellman-Ford 算法:
狀態:
d[k][u]定義:從源點v出發最多經過不構成負權值回路的k條邊到達終點u的最短路徑的長度
動態轉移方程:
dist[k][u]=min(dist[k?1][u],min(dist[k?1][j]+Edge[j][u])),j=0,1,...,n?1;j!=udist[k][u] = min(dist[k-1][u] ,min(dist[k-1][j]+Edge[j][u])),j = 0,1,...,n-1;j!=udist[k][u]=min(dist[k?1][u],min(dist[k?1][j]+Edge[j][u])),j=0,1,...,n?1;j!=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!=uimport numpy as np def Bellman_Ford_original(graph,start):vertex_num = len(graph)edge_num_ = vertex_num-1list = 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] =0for 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-1list = np.full((vertex_num+1),np.inf)list[start+1] = 0path =[-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 = Truefor 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] + wpath[u-1] = j-1flag = 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"breakbreakreturn 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,表示途中存在從源點可達的權為負的回路。
之所以需要第三部分的原因,是因為,如果存在從源點可達的權為負的回路。則應為無法收斂而導致不能求出最短路徑。
總結
以上是生活随笔為你收集整理的Bellman-Ford 算法 和 动态规划的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: floyd算法和动态规划
- 下一篇: Dijkstra和动态规划