Python数模笔记-NetworkX(3)条件最短路径
1、帶有條件約束的最短路徑問題
最短路徑問題是圖論中求兩個頂點之間的最短路徑問題,通常是求最短加權(quán)路徑。
條件最短路徑,指帶有約束條件、限制條件的最短路徑。例如,頂點約束,包括必經(jīng)點或禁止點的限制;邊的約束,包括必經(jīng)路段或禁止路段;還包括無權(quán)路徑長度的限制,即經(jīng)過幾步到達終點。進一步地,還有雙目標限制的最短路徑問題,求最短距離中花費最小的路線;交通限制條件下的最短路徑問題,需要考慮轉(zhuǎn)向限制和延誤的約束。
求解帶有限制條件的最短路徑問題,總體來說可以分為兩類基本方法:一類是基于不帶限制條件的最短路徑算法,對求解過程中的每一條有效路徑,都用限制條件進行判斷,如果滿足所有限制條件則繼續(xù),如果不滿足限制條件則放棄該路徑;另一類方法是基于具體問題和選擇算法的特點,將問題轉(zhuǎn)化為有約束的規(guī)劃問題來處理。
但是,如果使用 NetworkX 求解帶有限制條件的最短路徑問題,采用這兩類方法都會有一些困難。原因在于前文所介紹的 NetworkX 提供的 Dijkstra 算法、Bellman-Ford 算法、Floyd 算法和啟發(fā)式算法 A* 都是封裝函數(shù),沒有提供設(shè)置約束條件的選項和接口,因此用戶不能把條件判斷語句加入這些封裝函數(shù)的程序內(nèi)部。這種問題不僅存在于 Python 語言的 Network 工具包,對于其它計算機語言的工具包也是類似的:自己編程序費時費力,但可以根據(jù)需要修改和擴展;直接調(diào)用工具包的算法函數(shù)非常方便,但不能進行修改或擴展。
不過,NetworkX 可以生成兩個頂點之間的所有簡單路徑,而且可以獲得所有簡單路徑的邊的列表。利用簡單路徑算法,可以通過對約束條件的判斷來求解帶有頂點約束和邊約束的最短路徑問題。
歡迎關(guān)注 Youcans 原創(chuàng)系列,數(shù)模筆記每周更新
Python數(shù)模筆記-PuLP庫
Python數(shù)模筆記-StatsModels統(tǒng)計回歸
Python數(shù)模筆記-Sklearn
Python數(shù)模筆記-NetworkX
Python數(shù)模筆記-模擬退火算法
2、問題案例:螞蟻的最優(yōu)路徑分析
蟻巢有若干個儲藏間(圖中圓圈表示),儲藏間之間有路徑相連(路徑拓撲結(jié)構(gòu)如圖所示)。該圖為無向圖,路徑通行的花費如圖中線路上的數(shù)字所示,路徑正反方向通行的花費相同。要求從起點 N0 到終點 N17 的最優(yōu)路徑,并需要滿足限制條件:
- 需要盡可能以最少的花費到達終點;
- 必須經(jīng)過圖中的綠色節(jié)點;
- 必須經(jīng)過圖中的兩段綠色路段;
- 必須避開圖中的紅色路段。
說明:本案例出自西安郵電大學(xué)第12屆數(shù)學(xué)建模競賽賽題,本文進行了改編。
3、NetworkX 求解帶有條件約束的最短路徑問題
3.1 圖的創(chuàng)建和可視化
Python 例程(NetworkX)
# networkX_E3.py # Demo of shortest path with NetworkX # Copyright 2021 YouCans, XUPT # Crated:2021-05-20import matplotlib.pyplot as plt # 導(dǎo)入 Matplotlib 工具包 import networkx as nx # 導(dǎo)入 NetworkX 工具包# 問題 1:螞蟻的最優(yōu)路徑分析(西安郵電大學(xué)第12屆數(shù)學(xué)建模競賽B題)gAnt = nx.Graph() # 創(chuàng)建:空的 無向圖 gAnt.add_weighted_edges_from([(0,1,3),(0,2,1),(0,3,1),(1,2,1),(1,4,1),(1,9,4),(2,3,1),(2,4,2),(2,5,1),(3,5,2),(3,6,2),(3,7,1),(4,5,1),(4,9,1),(5,6,1),(5,9,3),(5,10,1),(5,12,3),(6,7,1),(6,8,2),(6,12,2),(6,13,4),(6,14,3),(7,8,1),(8,14,1),(8,15,3),(9,10,1),(9,11,1),(10,11,1),(10,12,2),(11,12,1),(11,16,1),(12,13,2),(12,16,1),(13,14,1),(13,15,2),(13,16,2),(13,17,1),(14,15,1),(15,17,4),(16,17,1)]) # 向圖中添加多條賦權(quán)邊: (node1,node2,weight)pos={0:(1,8),1:(4,12),2:(4,9),3:(4,6),4:(8,11),5:(9,8), # 指定頂點位置6:(11,6),7:(8,4),8:(12,2),9:(12,13),10:(15,11),11:(18,13),12:(19,9),13:(22,6),14:(18,4),15:(21,2),16:(22,11),17:(28,8)} nx.draw(gAnt, pos, with_labels=True, alpha=0.8) labels = nx.get_edge_attributes(gAnt,'weight') nx.draw_networkx_edge_labels(gAnt,pos,edge_labels=labels, font_color='c') # 顯示權(quán)值 nx.draw_networkx_nodes(gAnt,pos,nodelist=[0,17],node_color='yellow') # 設(shè)置頂點顏色 nx.draw_networkx_nodes(gAnt,pos,nodelist=[7,12],node_color='lime') # 設(shè)置頂點顏色 nx.draw_networkx_edges(gAnt,pos,edgelist=[(2,4),(13,14)],edge_color='lime',width=2.5) # 設(shè)置邊的顏色 nx.draw_networkx_edges(gAnt,pos,edgelist=[(11,12)],edge_color='r',width=2.5) # 設(shè)置邊的顏色 plt.show()運行結(jié)果
本段程序繪制網(wǎng)絡(luò)圖,包括頂點、邊、邊的權(quán)值,特殊頂點和特殊邊的顏色設(shè)置。
程序說明
3.2 無限制條件的最短路徑
程序說明
Python 例程(NetworkX)
# 兩個指定頂點之間的最短加權(quán)路徑 minWPath1 = nx.dijkstra_path(gAnt, source=0, target=17) # 頂點 0 到 頂點 17 的最短加權(quán)路徑 # 兩個指定頂點之間的最短加權(quán)路徑的長度 lMinWPath1 = nx.dijkstra_path_length(gAnt, source=0, target=17) #最短加權(quán)路徑長度 print("\n問題1: 無限制條件") print("S 到 E 的最短加權(quán)路徑: ", minWPath1) print("S 到 E 的最短加權(quán)路徑長度: ", lMinWPath1)運行結(jié)果
問題1: 無限制條件 S 到 E 的最短加權(quán)路徑: [0, 2, 5, 10, 11, 16, 17] S 到 E 的最短加權(quán)路徑長度: 63.3 限制條件:禁止點或禁止邊
程序說明
Python 例程
# 2. 限制條件:禁止點或禁止邊 # 解決方案:從圖中刪除禁止頂點或禁止邊 gAnt.remove_nodes_from([5]) # 通過頂點標簽 5 刪除頂點 gAnt.remove_edge(13,17) # 刪除邊 (13,17) minWPath2 = nx.dijkstra_path(gAnt, source=0, target=17) # 頂點 0 到 頂點 17 的最短加權(quán)路徑 lMinWPath2 = nx.dijkstra_path_length(gAnt, source=0, target=17) #最短加權(quán)路徑長度 print("\n問題2: 禁止點或禁止邊的約束") print("S 到 E 的最短加權(quán)路徑: ", minWPath2) print("S 到 E 的最短加權(quán)路徑長度: ", lMinWPath2)運行結(jié)果
問題2: 禁止點或禁止邊的約束 S 到 E 的最短加權(quán)路徑: [0, 3, 6, 12, 16, 17] S 到 E 的最短加權(quán)路徑長度: 73.4 限制條件:一個必經(jīng)點
程序說明
Python 例程
# 3. 限制條件:一個必經(jīng)點 # 解決方案:分解為兩個問題,問題 1 為起點N0至必經(jīng)點N6,問題 2 為必經(jīng)點N6至終點N17 minWPath3a = nx.dijkstra_path(gAnt, source=0, target=6) # N0 到 N6 的最短加權(quán)路徑 lMinWPath3a = nx.dijkstra_path_length(gAnt, source=0, target=6) # 最短加權(quán)路徑長度 minWPath3b = nx.dijkstra_path(gAnt, source=6, target=17) # N6 到 N17 的最短加權(quán)路徑 lMinWPath3b = nx.dijkstra_path_length(gAnt, source=6, target=17) # 最短加權(quán)路徑長度 minWPath3a.extend(minWPath3b[1:]) # 拼接 minWPath3a、minWPath3b 并去重 N7 print("\n問題3: 一個必經(jīng)點的約束") print("S 到 E 的最短加權(quán)路徑: ", minWPath3a) print("S 到 E 的最短加權(quán)路徑長度: ", lMinWPath3a+lMinWPath3b)運行結(jié)果
問題3: 一個必經(jīng)點的約束 S 到 E 的最短加權(quán)路徑: [0, 3, 6, 12, 16, 17] S 到 E 的最短加權(quán)路徑長度: 73.5 限制條件:多個必經(jīng)點(方案一)
程序說明
Python 例程
# 4. 限制條件:多個必經(jīng)點 (N7,N15) # 解決方案:遍歷從起點到終點的簡單路徑,求滿足必經(jīng)點條件的最短路徑 minWPath4 = min([path # 返回 key 為最小值的 pathfor path in nx.all_simple_paths(gAnt, 0, 17) # gAnt 中所有起點為0、終點為17的簡單路徑if all(n in path for n in (7, 15))], # 滿足路徑中包括頂點 N7,N15key=lambda x: sum(gAnt.edges[edge]['weight'] for edge in nx.utils.pairwise(x))) # key 為加權(quán)路徑長度 lenPath = sum(gAnt.edges[edge]['weight'] for edge in nx.utils.pairwise(minWPath4)) # 求指定路徑的加權(quán)路徑長度 print("\n問題4: 多個必經(jīng)點的約束") print("S 到 E 的最短加權(quán)路徑: ", minWPath4) print("S 到 E 的最短加權(quán)路徑長度: ", lenPath)運行結(jié)果
問題4: 多個必經(jīng)點的約束 S 到 E 的最短加權(quán)路徑: [0, 3, 7, 8, 14, 15, 13, 17] S 到 E 的最短加權(quán)路徑長度: 83.6 限制條件:多個必經(jīng)點(方案二)
程序說明
Python 例程
# 5. 限制條件:多個必經(jīng)點 (N7,N15) # 解決方案:遍歷從起點到終點的簡單路徑,求滿足必經(jīng)點條件的最短路徑 lMinWPath5 = minWPath5 = 1e9 for path in nx.all_simple_paths(gAnt, 0, 17):if all(n in path for n in (7,15)): # 滿足路徑中包括頂點 N7,N15lenPath = sum(gAnt.edges[edge]['weight'] for edge in nx.utils.pairwise(path))if lenPath < lMinWPath5:lMinWPath5 = lenPathminWPath5 = path print("\n問題5: 多個必經(jīng)點的約束") print("S 到 E 的最短加權(quán)路徑: ", minWPath5) print("S 到 E 的最短加權(quán)路徑長度: ", lMinWPath5)運行結(jié)果
問題5: 多個必經(jīng)點的約束 S 到 E 的最短加權(quán)路徑: [0, 3, 7, 8, 14, 15, 13, 17] S 到 E 的最短加權(quán)路徑長度: 83.7 限制條件:必經(jīng)邊
程序說明
Python 例程
# 6. 限制條件:必經(jīng)邊 (N2,N4), (N13,N14),必經(jīng)點 N7,N12 # 解決方案:遍歷從起點到終點的簡單路徑,求滿足必經(jīng)邊條件的最短路徑 gAnt.remove_edge(11,12) # 禁止邊 (11,12) lMinWPath6 = minWPath6 = 1e9 # 置初值 for path in nx.all_simple_paths(gAnt, 0, 17): # 所有起點為0、終點為17的簡單路徑if all(n in path for n in (2,4,7,12,13,14)): # 滿足路徑中包括頂點 N7,N12# 檢查 (N2,N4)p1 = path.index(2) # N2 的位置if (path[p1-1]!=4 and path[p1+1]!=4): continue # 判斷 N2~N4 是否相鄰# 檢查 (N13,N14)p2 = path.index(13) # # N13 的位置if (path[p2-1]!=14 and path[p2+1]!=14): continue # 判斷 N13~N14 是否相鄰lenPath = sum(gAnt.edges[edge]['weight'] for edge in nx.utils.pairwise(path))if lenPath < lMinWPath6:lMinWPath6 = lenPathminWPath6 = pathprint("\n問題6: 多個必經(jīng)邊、必經(jīng)點的約束") print("S 到 E 的最短加權(quán)路徑: ", minWPath6) print("S 到 E 的最短加權(quán)路徑長度: ", lMinWPath6)edgeList = [] for i in range(len(minWPath6)-1):edgeList.append((minWPath6[i],minWPath6[i+1])) nx.draw_networkx_edges(gAnt,pos,edgelist=edgeList,edge_color='m',width=4) # 設(shè)置邊的顏色 plt.show() # YouCans, XUPT運行結(jié)果
問題6: 多個必經(jīng)邊、必經(jīng)點的約束 S 到 E 的最短加權(quán)路徑: [0, 2, 4, 5, 6, 7, 8, 14, 13, 12, 16, 17] S 到 E 的最短加權(quán)路徑長度: 13版權(quán)說明:
本文內(nèi)容及例程為作者原創(chuàng),并非轉(zhuǎn)載書籍或網(wǎng)絡(luò)內(nèi)容。
本文中案例問題來自:
YouCans 原創(chuàng)作品,如轉(zhuǎn)載需標注原始鏈接。
Copyright 2021 YouCans, XUPT
Crated:2021-05-20
歡迎關(guān)注 Youcans 原創(chuàng)系列,數(shù)模筆記每周更新
Python數(shù)模筆記-PuLP庫(1)線性規(guī)劃入門
Python數(shù)模筆記-PuLP庫(2)線性規(guī)劃進階
Python數(shù)模筆記-PuLP庫(3)線性規(guī)劃實例
Python數(shù)模筆記-StatsModels 統(tǒng)計回歸(1)簡介
Python數(shù)模筆記-StatsModels 統(tǒng)計回歸(2)線性回歸
Python數(shù)模筆記-StatsModels 統(tǒng)計回歸(3)模型數(shù)據(jù)的準備
Python數(shù)模筆記-StatsModels 統(tǒng)計回歸(4)可視化
Python數(shù)模筆記-Sklearn (1)介紹
Python數(shù)模筆記-Sklearn (2)聚類分析
Python數(shù)模筆記-Sklearn (3)主成分分析
Python數(shù)模筆記-Sklearn (4)線性回歸
Python數(shù)模筆記-Sklearn (5)支持向量機
Python數(shù)模筆記-NetworkX(1)圖的操作
Python數(shù)模筆記-NetworkX(2)最短路徑
Python數(shù)模筆記-NetworkX(3)條件最短路徑
Python數(shù)模筆記-模擬退火算法(1)多變量函數(shù)優(yōu)化
Python數(shù)模筆記-模擬退火算法(2)約束條件的處理
Python數(shù)模筆記-模擬退火算法(3)整數(shù)規(guī)劃問題
Python數(shù)模筆記-模擬退火算法(4)旅行商問題
總結(jié)
以上是生活随笔為你收集整理的Python数模笔记-NetworkX(3)条件最短路径的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【OpenCV 例程200篇】25. 图
- 下一篇: python里面的dict和set