重頭戲部分來了,寫到這里我感覺得仔細認真點了,可能在NetworkX中,實現某些算法就一句話的事,但是這個算法是做什么的,用在什么地方,原理是怎么樣的,不清除,所以,我決定先把圖論
中常用算法弄個明白在寫這部分.
圖論常用算法看我的博客:
下面我將使用NetworkX實現上面的算法,建議不清楚的部分打開兩篇博客對照理解.
我將圖論的經典問題及常用算法的總結寫在下面兩篇博客中:
圖論---問題篇
圖論---算法篇
目錄:
注意:如果代碼出現找不庫,請返回第一個教程,把庫文件導入.
11.Graph相關算法
11.1最短路徑
11.1.1無向圖和有向圖
- #定義並畫出該圖
- G = nx.path_graph(5)
- nx.add_path(G,[0,5,2])
- nx.add_path(G,[0,6,4])
- nx.draw(G,with_labels=True)
- plt.title('無向圖',fontproperties=myfont)
- plt.axis('on')
- plt.xticks([])
- plt.yticks([])
- plt.show()
- #計算最短路徑
- print('0節點到4節點最短路徑: ',nx.shortest_path(G, source=0, target=4))
- p1 = nx.shortest_path(G, source=0)
- print('0節點到所有節點最短路徑: ',p1)
- #計算圖中所有的最短路徑
- print('計算圖中節點0到節點2的所有最短路徑: ',[p for p in nx.all_shortest_paths(G, source=0, target=2)])
- #計算最短路徑長度
- p2=nx.shortest_path_length(G, source=0, target=2) #最短路徑長度
- p3=nx.average_shortest_path_length(G) #計算平均最短路徑長度
- print('節點0到節點2的最短路徑長度:',p2,' 平均最短路徑長度: ',p3)
- #檢測是否有路徑
- print('檢測節點0到節點2是否有路徑',nx.has_path(G,0,2))

無向圖和有向圖最短路徑示例
輸出:
- 0節點到4節點最短路徑: [0, 6, 4]
- 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]}
- 計算圖中節點0到節點2的所有最短路徑: [[0, 1, 2], [0, 5, 2]]
- 節點0到節點2的最短路徑長度: 2 平均最短路徑長度: 1.8095238095238095
- 檢測節點0到節點2是否有路徑 True
11.1.2無權圖
- G = nx.path_graph(3)
- nx.draw(G,with_labels=True)
- plt.title('無權圖',fontproperties=myfont)
- plt.axis('on')
- plt.xticks([])
- plt.yticks([])
- plt.show()
- path1 = nx.single_source_shortest_path(G, 0) #計算當前源與所有可達節點的最短路徑
- length1 = nx.single_source_shortest_path_length(G, 0) #計算當前源與所有可達節點的最短路徑的長度
- path2 = dict(nx.all_pairs_shortest_path(G)) #計算graph兩兩節點之間的最短路徑
- length2 = dict(nx.all_pairs_shortest_path_length(G)) #計算graph兩兩節點之間的最短路徑的長度
- prede1=nx.predecessor(G, 0) #返回G中從源到所有節點最短路徑的前驅
- print('當前源與所有可達節點的最短路徑: ',path1,'\n當前源與所有可達節點的最短路徑的長度: ',length1)
- print('\ngraph兩兩節點之間的最短路徑: ',path2,'\ngraph兩兩節點之間的最短路徑的長度: ',length2)
- print('\nG中從源到所有節點最短路徑的前驅: ',prede1)

無權圖
輸出:
- 當前源與所有可達節點的最短路徑: {0: [0], 1: [0, 1], 2: [0, 1, 2]}
- 當前源與所有可達節點的最短路徑的長度: {0: 0, 1: 1, 2: 2}
- 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]}}
- graph兩兩節點之間的最短路徑的長度: {0: {0: 0, 1: 1, 2: 2}, 1: {0: 1, 1: 0, 2: 1}, 2: {0: 2, 1: 1, 2: 0}}
- G中從源到所有節點最短路徑的前驅: {0: [], 1: [0], 2: [1]}
11.1.3有權圖(迪傑斯特拉)
- G = nx.path_graph(5, create_using = nx.DiGraph())
- nx.draw(G,with_labels=True)
- plt.title('有向圖',fontproperties=myfont)
- plt.axis('on')
- plt.xticks([])
- plt.yticks([])
- plt.show()
- #計算加權圖最短路徑長度和前驅
- pred, dist = nx.dijkstra_predecessor_and_distance(G, 0)
- print('\n加權圖最短路徑長度和前驅: ',pred, dist)
- #返回G中從源到目標的最短加權路徑,要求邊權重必須為數值
- print('\nG中從源0到目標4的最短加權路徑: ',nx.dijkstra_path(G,0,4))
- print('\nG中從源0到目標4的最短加權路徑的長度: ',nx.dijkstra_path_length(G,0,4)) #最短路徑長度
- #單源節點最短加權路徑和長度。
- length1, path1 = nx.single_source_dijkstra(G, 0)
- print('\n單源節點最短加權路徑和長度: ',length1, path1)
- #下面兩條和是前面的分解
- # path2=nx.single_source_dijkstra_path(G,0)
- # length2 = nx.single_source_dijkstra_path_length(G, 0)
- #print(length1,'$', path1,'$',length2,'$',path2)
- #多源節點最短加權路徑和長度。
- path1 = nx.multi_source_dijkstra_path(G, {0, 4})
- length1 = nx.multi_source_dijkstra_path_length(G, {0, 4})
- print('\n多源節點最短加權路徑和長度:', path1,length1)
- #兩兩節點之間最短加權路徑和長度。
- path1 = dict(nx.all_pairs_dijkstra_path(G))
- length1 = dict(nx.all_pairs_dijkstra_path_length(G))
- print('\n兩兩節點之間最短加權路徑和長度: ',path1,length1)
- #雙向搜索的迪傑斯特拉
- length, path = nx.bidirectional_dijkstra(G, 0, 4)
- print('\n雙向搜索的迪傑斯特拉:',length, path)

迪傑斯特拉算法使用
輸出:
- 加權圖最短路徑長度和前驅: {0: [], 1: [0], 2: [1], 3: [2], 4: [3]} {0: 0, 1: 1, 2: 2, 3: 3, 4: 4}
- G中從源0到目標4的最短加權路徑: [0, 1, 2, 3, 4]
- G中從源0到目標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]}
- 多源節點最短加權路徑和長度: {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}
- 兩兩節點之間最短加權路徑和長度: {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}}
- 雙向搜索的迪傑斯特拉: 4 [0, 1, 2, 3, 4]
11.1.4貝爾曼-福特(Bellman-Ford)算法
- G = nx.path_graph(5, create_using = nx.DiGraph())
- nx.draw(G,with_labels=True)
- plt.title('有權圖',fontproperties=myfont)
- plt.axis('on')
- plt.xticks([])
- plt.yticks([])
- plt.show()
- print('G中從源到目標的最短加權路徑: ',nx.bellman_ford_path(G, 0, 4))
- print('\nG中從源到目標的最短加權路徑的長度:',nx.bellman_ford_path_length(G,0,4))
- path1=nx.single_source_bellman_ford_path(G,0)
- length1 = dict(nx.single_source_bellman_ford_path_length(G, 0))
- print('\n單源節點最短加權路徑和長度: ',path1,'\n單源節點最短加權路徑和長度: ',length1)
- path2 = dict(nx.all_pairs_bellman_ford_path(G))
- length2 = dict(nx.all_pairs_bellman_ford_path_length(G))
- print('\n兩兩節點之間最短加權路徑和長度: ',path2,length2)
- length, path = nx.single_source_bellman_ford(G, 0)
- pred, dist = nx.bellman_ford_predecessor_and_distance(G, 0)
- print('\n加權圖最短路徑長度和前驅: ',pred,dist)

貝爾曼-福特(Bellman-Ford)算法使用示例
輸出:
- G中從源到目標的最短加權路徑: [0, 1, 2, 3, 4]
- G中從源到目標的最短加權路徑的長度: 4
- 單源節點最短加權路徑和長度: {0: [0], 1: [0, 1], 2: [0, 1, 2], 3: [0, 1, 2, 3], 4: [0, 1, 2, 3, 4]}
- 單源節點最短加權路徑和長度: {0: 0, 1: 1, 2: 2, 3: 3, 4: 4}
- 兩兩節點之間最短加權路徑和長度: {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檢測負權重邊
- #定義並畫出該圖
- G = nx.cycle_graph(5, create_using = nx.DiGraph())
- #添加負權重邊前后
- print(nx.negative_edge_cycle(G))
- G[1][2]['weight'] = -7
- print(nx.negative_edge_cycle(G))
輸出:
- False
- True
11.1.6使用約翰遜(Johnson)的算法
- #生成graph
- G = nx.DiGraph()
- G.add_weighted_edges_from([('0', '3', 3), ('0', '1', -5),('0', '2', 2), ('1', '2', 4), ('2', '3', 1)])
- #邊和節點信息
- edge_labels = nx.get_edge_attributes(G,'weight')
- labels={'0':'0','1':'1','2':'2','3':'3'}
- #生成節點位置
- pos=nx.spring_layout(G)
-
- #把節點畫出來
- nx.draw_networkx_nodes(G,pos,node_color='g',node_size=500,alpha=0.8)
-
- #把邊畫出來
- nx.draw_networkx_edges(G,pos,width=1.0,alpha=0.5,edge_color='b')
-
- #把節點的標簽畫出來
- nx.draw_networkx_labels(G,pos,labels,font_size=16)
-
- #把邊權重畫出來
- nx.draw_networkx_edge_labels(G, pos, edge_labels)
- #顯示graph
- plt.title('有權圖',fontproperties=myfont)
- plt.axis('on')
- plt.xticks([])
- plt.yticks([])
- plt.show()
- #使用johnson算法計算最短路徑
- paths = nx.johnson(G, weight='weight')
- print(paths)

約翰遜(Johnson)的算法使用示例
輸出:
- {'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)
- #使用Floyd算法找到所有對最短路徑長度。
- G = nx.DiGraph()
- G.add_weighted_edges_from([('0', '3', 3), ('0', '1', -5),('0', '2', 2), ('1', '2', 4), ('2', '3', 1)])
- #邊和節點信息
- edge_labels = nx.get_edge_attributes(G,'weight')
- labels={'0':'0','1':'1','2':'2','3':'3'}
- #生成節點位置
- pos=nx.spring_layout(G)
-
- #把節點畫出來
- nx.draw_networkx_nodes(G,pos,node_color='g',node_size=500,alpha=0.8)
-
- #把邊畫出來
- nx.draw_networkx_edges(G,pos,width=1.0,alpha=0.5,edge_color='b')
-
- #把節點的標簽畫出來
- nx.draw_networkx_labels(G,pos,labels,font_size=16)
-
- #把邊權重畫出來
- nx.draw_networkx_edge_labels(G, pos, edge_labels)
- #顯示graph
- plt.title('有權圖',fontproperties=myfont)
- plt.axis('on')
- plt.xticks([])
- plt.yticks([])
- plt.show()
- #計算最短路徑長度
- lenght=nx.floyd_warshall(G, weight='weight')
- #計算最短路徑上的前驅與路徑長度
- predecessor,distance1=nx.floyd_warshall_predecessor_and_distance(G, weight='weight')
- #計算兩兩節點之間的最短距離,並以numpy矩陣形式返回
- distance2=nx.floyd_warshall_numpy(G, weight='weight')
- print(list(lenght))
- print(predecessor)
- print(list(distance1))
- print(distance2)

弗洛伊德算法(Floyd-Warshall)使用示例
輸出:
- ['2', '3', '0', '1']
- {'2': {'3': '2'}, '0': {'2': '1', '3': '2', '1': '0'}, '1': {'2': '1', '3': '2'}}
- ['2', '3', '0', '1']
- [[ 0. 1. inf inf]
- [inf 0. inf inf]
- [-1. 0. 0. -5.]
- [ 4. 5. inf 0.]]
注:輸出中的矩陣不是按照節點0,1,2,3排序,而是2,1,3,0,即如圖:

兩點之間的最短距離
11.1.8A*算法
- G = nx.path_graph(5)
- #顯示graph
- nx.draw(G,with_labels=True)
- plt.title('有x向圖',fontproperties=myfont)
- plt.axis('on')
- plt.xticks([])
- plt.yticks([])
- plt.show()
- #直接輸出路徑和長度
- print(nx.astar_path(G, 0, 4))
- print(nx.astar_path_length(G, 0, 4))

A*算法
輸出:
- [0, 1, 2, 3, 4]
- 4