圖論——networkx——最短路徑算法篇

NetworkX系列教程(10)-算法之一:最短路徑問題

小書匠Graph圖論

重頭戲部分來了,寫到這裏我感覺得仔細認真點了,可能在NetworkX中,實現某些算法就一句話的事,但是這個算法是做什麼的,用在什麼地方,原理是怎麼樣的,不清除,所以,我決定先把圖論中常用算法弄個明白在寫這部分.

圖論常用算法看我的博客:

下面我將使用NetworkX實現上面的算法,建議不清楚的部分打開兩篇博客對照理解.
我將圖論的經典問題及常用算法的總結寫在下面兩篇博客中:
圖論---問題篇
圖論---算法篇

目錄:


注意:如果代碼出現找不庫,請返回第一個教程,把庫文件導入.

11.Graph相關算法

11.1最短路徑

11.1.1無向圖和有向圖

  1. #定義並畫出該圖 
  2. G = nx.path_graph(5) 
  3. nx.add_path(G,[0,5,2]) 
  4. nx.add_path(G,[0,6,4]) 
  5. nx.draw(G,with_labels=True) 
  6. plt.title('無向圖',fontproperties=myfont) 
  7. plt.axis('on') 
  8. plt.xticks([]) 
  9. plt.yticks([]) 
  10. plt.show() 
  11.  
  12. #計算最短路徑 
  13. print('0節點到4節點最短路徑: ',nx.shortest_path(G, source=0, target=4)) 
  14. p1 = nx.shortest_path(G, source=0) 
  15. print('0節點到所有節點最短路徑: ',p1) 
  16.  
  17. #計算圖中所有的最短路徑 
  18. print('計算圖中節點0到節點2的所有最短路徑: ',[p for p in nx.all_shortest_paths(G, source=0, target=2)]) 
  19.  
  20. #計算最短路徑長度 
  21. p2=nx.shortest_path_length(G, source=0, target=2) #最短路徑長度 
  22. p3=nx.average_shortest_path_length(G) #計算平均最短路徑長度 
  23. print('節點0到節點2的最短路徑長度:',p2,' 平均最短路徑長度: ',p3) 
  24.  
  25. #檢測是否有路徑 
  26. print('檢測節點0到節點2是否有路徑',nx.has_path(G,0,2)) 

 

無向圖和有向圖最短路徑示例

無向圖和有向圖最短路徑示例

 

輸出:

  1. 0節點到4節點最短路徑: [0, 6, 4] 
  2. 0節點到所有節點最短路徑: {0: [0], 1: [0, 1], 2: [0, 1, 2], 3: [0, 1, 2, 3], 4: [0, 6, 4], 5: [0, 5], 6: [0, 6]} 
  3. 計算圖中節點0到節點2的所有最短路徑: [[0, 1, 2], [0, 5, 2]] 
  4. 節點0到節點2的最短路徑長度: 2 平均最短路徑長度: 1.8095238095238095 
  5. 檢測節點0到節點2是否有路徑 True 

11.1.2無權圖

  1. G = nx.path_graph(3) 
  2. nx.draw(G,with_labels=True) 
  3. plt.title('無權圖',fontproperties=myfont) 
  4. plt.axis('on') 
  5. plt.xticks([]) 
  6. plt.yticks([]) 
  7. plt.show() 
  8.  
  9. path1 = nx.single_source_shortest_path(G, 0) #計算當前源與所有可達節點的最短路徑 
  10. length1 = nx.single_source_shortest_path_length(G, 0) #計算當前源與所有可達節點的最短路徑的長度 
  11. path2 = dict(nx.all_pairs_shortest_path(G)) #計算graph兩兩節點之間的最短路徑 
  12. length2 = dict(nx.all_pairs_shortest_path_length(G)) #計算graph兩兩節點之間的最短路徑的長度 
  13. prede1=nx.predecessor(G, 0) #返回G中從源到所有節點最短路徑的前驅 
  14.  
  15. print('當前源與所有可達節點的最短路徑: ',path1,'\n當前源與所有可達節點的最短路徑的長度: ',length1) 
  16. print('\ngraph兩兩節點之間的最短路徑: ',path2,'\ngraph兩兩節點之間的最短路徑的長度: ',length2) 
  17. print('\nG中從源到所有節點最短路徑的前驅: ',prede1) 

 

無權圖

無權圖

 

輸出:

  1. 當前源與所有可達節點的最短路徑: {0: [0], 1: [0, 1], 2: [0, 1, 2]}  
  2. 當前源與所有可達節點的最短路徑的長度: {0: 0, 1: 1, 2: 2} 
  3. graph兩兩節點之間的最短路徑: {0: {0: [0], 1: [0, 1], 2: [0, 1, 2]}, 1: {0: [1, 0], 1: [1], 2: [1, 2]}, 2: {0: [2, 1, 0], 1: [2, 1], 2: [2]}}  
  4. graph兩兩節點之間的最短路徑的長度: {0: {0: 0, 1: 1, 2: 2}, 1: {0: 1, 1: 0, 2: 1}, 2: {0: 2, 1: 1, 2: 0}} 
  5. G中從源到所有節點最短路徑的前驅: {0: [], 1: [0], 2: [1]} 

11.1.3有權圖(迪傑斯特拉)

  1. G = nx.path_graph(5, create_using = nx.DiGraph())  
  2. nx.draw(G,with_labels=True) 
  3. plt.title('有向圖',fontproperties=myfont) 
  4. plt.axis('on') 
  5. plt.xticks([]) 
  6. plt.yticks([]) 
  7. plt.show() 
  8.  
  9. #計算加權圖最短路徑長度和前驅 
  10. pred, dist = nx.dijkstra_predecessor_and_distance(G, 0) 
  11. print('\n加權圖最短路徑長度和前驅: ',pred, dist) 
  12.  
  13. #返回G中從源到目標的最短加權路徑,要求邊權重必須爲數值 
  14. print('\nG中從源0到目標4的最短加權路徑: ',nx.dijkstra_path(G,0,4)) 
  15. print('\nG中從源0到目標4的最短加權路徑的長度: ',nx.dijkstra_path_length(G,0,4)) #最短路徑長度 
  16.  
  17. #單源節點最短加權路徑和長度。 
  18. length1, path1 = nx.single_source_dijkstra(G, 0) 
  19. print('\n單源節點最短加權路徑和長度: ',length1, path1) 
  20. #下面兩條和是前面的分解 
  21. # path2=nx.single_source_dijkstra_path(G,0) 
  22. # length2 = nx.single_source_dijkstra_path_length(G, 0) 
  23. #print(length1,'$', path1,'$',length2,'$',path2) 
  24.  
  25. #多源節點最短加權路徑和長度。 
  26. path1 = nx.multi_source_dijkstra_path(G, {0, 4}) 
  27. length1 = nx.multi_source_dijkstra_path_length(G, {0, 4}) 
  28.  
  29. print('\n多源節點最短加權路徑和長度:', path1,length1) 
  30.  
  31. #兩兩節點之間最短加權路徑和長度。 
  32. path1 = dict(nx.all_pairs_dijkstra_path(G)) 
  33. length1 = dict(nx.all_pairs_dijkstra_path_length(G)) 
  34. print('\n兩兩節點之間最短加權路徑和長度: ',path1,length1) 
  35.  
  36. #雙向搜索的迪傑斯特拉 
  37. length, path = nx.bidirectional_dijkstra(G, 0, 4) 
  38. print('\n雙向搜索的迪傑斯特拉:',length, path) 

 

迪傑斯特拉算法使用

迪傑斯特拉算法使用

 

輸出:

  1. 加權圖最短路徑長度和前驅: {0: [], 1: [0], 2: [1], 3: [2], 4: [3]} {0: 0, 1: 1, 2: 2, 3: 3, 4: 4} 
  2. G中從源0到目標4的最短加權路徑: [0, 1, 2, 3, 4] 
  3. G中從源0到目標4的最短加權路徑的長度: 4 
  4. 單源節點最短加權路徑和長度: {0: 0, 1: 1, 2: 2, 3: 3, 4: 4} {0: [0], 1: [0, 1], 2: [0, 1, 2], 3: [0, 1, 2, 3], 4: [0, 1, 2, 3, 4]} 
  5. 多源節點最短加權路徑和長度: {0: [0], 1: [0, 1], 2: [0, 1, 2], 3: [0, 1, 2, 3], 4: [4]} {0: 0, 1: 1, 2: 2, 3: 3, 4: 0} 
  6. 兩兩節點之間最短加權路徑和長度: {0: {0: [0], 1: [0, 1], 2: [0, 1, 2], 3: [0, 1, 2, 3], 4: [0, 1, 2, 3, 4]}, 1: {1: [1], 2: [1, 2], 3: [1, 2, 3], 4: [1, 2, 3, 4]}, 2: {2: [2], 3: [2, 3], 4: [2, 3, 4]}, 3: {3: [3], 4: [3, 4]}, 4: {4: [4]}} {0: {0: 0, 1: 1, 2: 2, 3: 3, 4: 4}, 1: {1: 0, 2:1, 3: 2, 4: 3}, 2: {2: 0, 3: 1, 4: 2}, 3: {3: 0, 4: 1}, 4: {4: 0}} 
  7. 雙向搜索的迪傑斯特拉: 4 [0, 1, 2, 3, 4] 

11.1.4貝爾曼-福特(Bellman-Ford)算法

  1. G = nx.path_graph(5, create_using = nx.DiGraph())  
  2. nx.draw(G,with_labels=True) 
  3. plt.title('有權圖',fontproperties=myfont) 
  4. plt.axis('on') 
  5. plt.xticks([]) 
  6. plt.yticks([]) 
  7. plt.show() 
  8.  
  9. print('G中從源到目標的最短加權路徑: ',nx.bellman_ford_path(G, 0, 4)) 
  10. print('\nG中從源到目標的最短加權路徑的長度:',nx.bellman_ford_path_length(G,0,4)) 
  11.  
  12. path1=nx.single_source_bellman_ford_path(G,0) 
  13. length1 = dict(nx.single_source_bellman_ford_path_length(G, 0)) 
  14. print('\n單源節點最短加權路徑和長度: ',path1,'\n單源節點最短加權路徑和長度: ',length1) 
  15.  
  16. path2 = dict(nx.all_pairs_bellman_ford_path(G)) 
  17. length2 = dict(nx.all_pairs_bellman_ford_path_length(G)) 
  18. print('\n兩兩節點之間最短加權路徑和長度: ',path2,length2) 
  19.  
  20. length, path = nx.single_source_bellman_ford(G, 0) 
  21. pred, dist = nx.bellman_ford_predecessor_and_distance(G, 0) 
  22. print('\n加權圖最短路徑長度和前驅: ',pred,dist) 

 

貝爾曼-福特(Bellman-Ford)算法使用示例

貝爾曼-福特(Bellman-Ford)算法使用示例

 

輸出:

  1. G中從源到目標的最短加權路徑: [0, 1, 2, 3, 4] 
  2. G中從源到目標的最短加權路徑的長度: 4 
  3. 單源節點最短加權路徑和長度: {0: [0], 1: [0, 1], 2: [0, 1, 2], 3: [0, 1, 2, 3], 4: [0, 1, 2, 3, 4]}  
  4. 單源節點最短加權路徑和長度: {0: 0, 1: 1, 2: 2, 3: 3, 4: 4} 
  5. 兩兩節點之間最短加權路徑和長度: {0: {0: [0], 1: [0, 1], 2: [0, 1, 2], 3: [0, 1, 2, 3], 4: [0, 1, 2, 3, 4]}, 1: {1: [1], 2: [1, 2], 3: [1, 2, 3], 4: 

[1, 2, 3, 4]}, 2: {2: [2], 3: [2, 3], 4: [2, 3, 4]}, 3: {3: [3], 4:
[3, 4]}, 4: {4: [4]}} {0: {0: 0, 1: 1, 2: 2, 3: 3, 4: 4}, 1: {1: 0, 2:
1, 3: 2, 4: 3}, 2: {2: 0, 3: 1, 4: 2}, 3: {3: 0, 4: 1}, 4: {4: 0}}
加權圖最短路徑長度和前驅: {0: [None], 1: [0], 2: [1], 3: [2], 4: [3]} {0: 0, 1: 1, 2: 2, 3: 3, 4: 4}


11.1.5檢測負權重邊

  1. #定義並畫出該圖 
  2. G = nx.cycle_graph(5, create_using = nx.DiGraph()) 
  3.  
  4. #添加負權重邊前後 
  5. print(nx.negative_edge_cycle(G)) 
  6. G[1][2]['weight'] = -7 
  7. print(nx.negative_edge_cycle(G)) 

輸出:

  1. False 
  2. True 

11.1.6使用約翰遜(Johnson)的算法

  1. #生成graph 
  2. G = nx.DiGraph() 
  3. G.add_weighted_edges_from([('0', '3', 3), ('0', '1', -5),('0', '2', 2), ('1', '2', 4), ('2', '3', 1)]) 
  4.  
  5. #邊和節點信息 
  6. edge_labels = nx.get_edge_attributes(G,'weight')  
  7. labels={'0':'0','1':'1','2':'2','3':'3'} 
  8.  
  9. #生成節點位置  
  10. pos=nx.spring_layout(G)  
  11.  
  12. #把節點畫出來  
  13. nx.draw_networkx_nodes(G,pos,node_color='g',node_size=500,alpha=0.8)  
  14.  
  15. #把邊畫出來  
  16. nx.draw_networkx_edges(G,pos,width=1.0,alpha=0.5,edge_color='b')  
  17.  
  18. #把節點的標籤畫出來  
  19. nx.draw_networkx_labels(G,pos,labels,font_size=16)  
  20.  
  21. #把邊權重畫出來  
  22. nx.draw_networkx_edge_labels(G, pos, edge_labels)  
  23.  
  24. #顯示graph 
  25. plt.title('有權圖',fontproperties=myfont) 
  26. plt.axis('on') 
  27. plt.xticks([]) 
  28. plt.yticks([]) 
  29. plt.show() 
  30.  
  31. #使用johnson算法計算最短路徑 
  32. paths = nx.johnson(G, weight='weight') 
  33.  
  34. print(paths) 

 

約翰遜(Johnson)的算法使用示例

約翰遜(Johnson)的算法使用示例

 

輸出:

  1. {'2': {'2': ['2'], '3': ['2', '3']}, '3': {'3': ['3']}, '0': {'2': ['0', '1', '2'], '3': ['0', '1', '2', '3'], '0': ['0'], '1': ['0','1']}, '1': {'2': ['1', '2'], '3': ['1', '2', '3'], '1': ['1']}} 

11.1.7弗洛伊德算法(Floyd-Warshall)

  1. #使用Floyd算法找到所有對最短路徑長度。 
  2. G = nx.DiGraph() 
  3. G.add_weighted_edges_from([('0', '3', 3), ('0', '1', -5),('0', '2', 2), ('1', '2', 4), ('2', '3', 1)]) 
  4.  
  5. #邊和節點信息 
  6. edge_labels = nx.get_edge_attributes(G,'weight')  
  7. labels={'0':'0','1':'1','2':'2','3':'3'} 
  8.  
  9. #生成節點位置  
  10. pos=nx.spring_layout(G)  
  11.  
  12. #把節點畫出來  
  13. nx.draw_networkx_nodes(G,pos,node_color='g',node_size=500,alpha=0.8)  
  14.  
  15. #把邊畫出來  
  16. nx.draw_networkx_edges(G,pos,width=1.0,alpha=0.5,edge_color='b')  
  17.  
  18. #把節點的標籤畫出來  
  19. nx.draw_networkx_labels(G,pos,labels,font_size=16)  
  20.  
  21. #把邊權重畫出來  
  22. nx.draw_networkx_edge_labels(G, pos, edge_labels)  
  23.  
  24. #顯示graph 
  25. plt.title('有權圖',fontproperties=myfont) 
  26. plt.axis('on') 
  27. plt.xticks([]) 
  28. plt.yticks([]) 
  29. plt.show() 
  30.  
  31. #計算最短路徑長度 
  32. lenght=nx.floyd_warshall(G, weight='weight') 
  33.  
  34. #計算最短路徑上的前驅與路徑長度 
  35. predecessor,distance1=nx.floyd_warshall_predecessor_and_distance(G, weight='weight') 
  36.  
  37. #計算兩兩節點之間的最短距離,並以numpy矩陣形式返回 
  38. distance2=nx.floyd_warshall_numpy(G, weight='weight') 
  39.  
  40. print(list(lenght)) 
  41. print(predecessor) 
  42. print(list(distance1)) 
  43. print(distance2) 

 

弗洛伊德算法(Floyd-Warshall)使用示例

弗洛伊德算法(Floyd-Warshall)使用示例

 

輸出:

  1. ['2', '3', '0', '1'] 
  2. {'2': {'3': '2'}, '0': {'2': '1', '3': '2', '1': '0'}, '1': {'2': '1', '3': '2'}} 
  3. ['2', '3', '0', '1'] 
  4. [[ 0. 1. inf inf] 
  5. [inf 0. inf inf] 
  6. [-1. 0. 0. -5.] 
  7. [ 4. 5. inf 0.]] 

注:輸出中的矩陣不是按照節點0,1,2,3排序,而是2,1,3,0,即如圖:

 

兩點之間的最短距離

 


11.1.8A*算法

  1. G = nx.path_graph(5) 
  2.  
  3. #顯示graph 
  4. nx.draw(G,with_labels=True) 
  5. plt.title('有x向圖',fontproperties=myfont) 
  6. plt.axis('on') 
  7. plt.xticks([]) 
  8. plt.yticks([]) 
  9. plt.show() 
  10.  
  11. #直接輸出路徑和長度 
  12. print(nx.astar_path(G, 0, 4)) 
  13. print(nx.astar_path_length(G, 0, 4)) 

 

A*算法

A*算法

 

輸出:

  1. [0, 1, 2, 3, 4] 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章