Python小白的数学建模课-17.条件最短路径算法
- 條件最短路徑問題,指帶有約束條件、限制條件的最短路徑問題。例如: 頂點約束,包括必經點或禁止點的限制; 邊的約束,包括必經路段、禁行路段和單向路段;無權路徑長度的限制,如要求經過幾步或不超過幾步到達終點。
- 本文基于 NetworkX 工具包,建立了一個遍歷簡單路徑、判斷約束條件的通用框架。
- 數模競賽真題案例,詳解禁止點、禁止邊、必經點、必經邊的約束條件處理,進而可以擴展到任何約束條件。
- 『Python小白的數學建模課 @ Youcans』帶你從數模小白成為國賽達人。
1. 帶有條件約束的最短路徑問題
最短路徑問題是圖論中求兩個頂點之間的最短路徑問題,通常是求最短加權路徑。
在數模競賽中,一般不會直接求最短路徑,那就太簡單了,總是要在基本問題中設置各種限制條件,情況就完全不同了。
條件最短路徑問題,指帶有約束條件、限制條件的最短路徑問題。例如: 頂點約束,包括必經點或禁止點的限制; 邊的約束,包括必經路段、禁行路段和單向路段;無權路徑長度的限制,如要求經過幾步或不超過幾步到達終點。
進一步地,還有雙目標限制的最短路徑問題,求最短長度中花費最小的路線;交通限制條件下的最短路徑問題,如轉向限制、道路交叉口的時間延誤的約束。
求解帶有限制條件的最短路徑問題,總體來說可以分為兩類基本方法:
一類是基于不帶限制條件的最短路徑算法,對求解過程中的每一條有效路徑,都用限制條件進行判斷,如果滿足所有限制條件則繼續,如果不滿足限制條件則放棄該路徑;
另一類方法是基于具體問題和選擇算法的特點,將問題轉化為有約束的規劃問題來處理。
但是,如果使用 NetworkX 求解帶有限制條件的最短路徑問題,采用這兩類方法都會有一些困難。原因在于前文所介紹的 NetworkX 提供的 Dijkstra 算法、Bellman-Ford 算法、Floyd 算法和啟發式算法 A* 都是封裝函數,沒有提供設置約束條件的選項和接口,因此用戶不能把條件判斷語句加入這些封裝函數的程序內部。
這個問題不僅存在于 Python 語言的 NetworkX 工具包,對于其它計算機語言的工具包也是類似的。自己編程序費時費力,但可以根據需要修改和擴展;直接調用工具包的算法函數非常方便,但不能進行修改或擴展。
不過,NetworkX 可以生成兩個頂點之間的所有簡單路徑,而且可以獲得所有簡單路徑的邊的列表。利用簡單路徑算法,可以通過對約束條件的判斷來求解帶有頂點約束和邊約束的最短路徑問題。
2. 案例:螞蟻的最優路徑分析
2.1 問題描述
蟻巢有若干個儲藏間(圖中圓圈所示),儲藏間之間有路徑相連(路徑拓撲結構如圖所示)。
該圖為無向圖,路徑通行的花費如圖中線路上的數字所示,路徑正反方向通行的花費相同。
要求從起點 N0 到終點 N17 的最優路徑,并需要滿足條件:
- 必須經過圖中的綠色節點 N7、N12;
- 必須經過圖中的兩段綠色路段 (N2, N4)、(N13, N14);
- 必須避開圖中的紅色路段 (N11, N12);
- 求花費最少的最優路徑。
說明:本案例來自西安郵電大學(XUPT)第12屆數學建模競賽賽題,本文進行了改編。
2.2 圖的創建和可視化
2.2.1 Python 例程(NetworkX)
# mathmodel17_v1.py # Demo17 of mathematical modeling algorithm # Demo of shortest path with constraints with NetworkX # Copyright 2021 YouCans, XUPT # Crated:2021-07-09import numpy as np import matplotlib.pyplot as plt # 導入 Matplotlib 工具包 import networkx as nx # 導入 NetworkX 工具包# 問題:螞蟻的最優路徑分析(西安郵電大學第12屆數學建模競賽B題) gAnt = nx.Graph() # 創建:空的 無向圖 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)]) # 向圖中添加多條賦權邊: (node1,node2,weight)pos={0:(0,8),1:(7,12),2:(6,9),3:(5,6),4:(11,10),5:(14,8), # 指定頂點位置6:(17,6),7:(10,4),8:(19,4),9:(18,12),10:(21,10),11:(28,12),12:(25,8),13:(30,7),14:(24,5),15:(29,4),16:(32,10),17:(37,8)}fig, ax = plt.subplots(figsize=(9, 6)) nx.draw(gAnt, pos, with_labels=True, node_color='cyan', alpha=0.8) labels = nx.get_edge_attributes(gAnt,'weight') # 邊的權值 nx.draw_networkx_edge_labels(gAnt,pos,edge_labels=labels, font_color='m') # 顯示邊的權值 nx.draw_networkx_nodes(gAnt,pos,nodelist=[0,17],node_color='yellow') # 設置頂點顏色:N1,N17 nx.draw_networkx_nodes(gAnt,pos,nodelist=[7,12],node_color='lime') # 設置頂點顏色:N7,N12 nx.draw_networkx_edges(gAnt,pos,edgelist=[(2,4),(13,14)],edge_color='lime',width=3) # 設置指定邊的顏色、寬度 nx.draw_networkx_edges(gAnt,pos,edgelist=[(11,12)],edge_color='r',width=3) # 設置指定邊的顏色、寬度 plt.show()2.2.2 程序說明
本段程序繪制網絡圖,包括頂點、邊、邊的權值,特殊頂點和特殊邊的顏色設置。
3. NetworkX 求解條件最短路徑問題
3.1 無限制條件的最短路徑
Python 例程(NetworkX):
# 1. 無限制條件的最短路徑 # Dijkstra 算法:兩個指定頂點之間的最短加權路徑 minWPath1 = nx.dijkstra_path(gAnt, source=0, target=17) # 頂點 0 到 頂點 17 的最短加權路徑 # Dijkstra 算法:兩個指定頂點之間的最短加權路徑的長度 lMinWPath1 = nx.dijkstra_path_length(gAnt, source=0, target=17) # 最短加權路徑長度 print("\n問題1: 無限制條件") print("N0 到 N17 的最短加權路徑: ", minWPath1) print("N0 到 N17 的最短加權路徑長度: ", lMinWPath1)運行結果:
問題1: 無限制條件 N0 到 N17 的最短加權路徑: [0, 2, 5, 10, 11, 16, 17] N0 到 N17 的最短加權路徑長度: 6程序說明:
3.2 限制條件:禁止點或禁止邊
程序說明:
Python 例程:
# 2. 限制條件:禁止點或禁止邊 # 解決方案:從圖中刪除禁止頂點或禁止邊 gAntF = gAnt.copy() gAntF.remove_node(5) # 通過頂點標簽 5 刪除頂點 gAntF.remove_edges_from([(11,12), (13,17)]) # 刪除多條邊 (11,12), (13,17) minWPath2 = nx.dijkstra_path(gAntF, source=0, target=17) # 頂點 0 到 頂點 17 的最短加權路徑 lMinWPath2 = nx.dijkstra_path_length(gAntF, source=0, target=17) # 最短加權路徑長度 print("\n問題2: 禁止點或禁止邊的約束") # youcans @ XUPT print("N0 到 N17 的最短加權路徑: ", minWPath2) print("N0 到 N17 的最短加權路徑長度: ", lMinWPath2)運行結果:
問題2: 禁止點或禁止邊的約束 N0 到 N17 的最短加權路徑: [0, 3, 6, 12, 16, 17] N0 到 N17 的最短加權路徑長度: 73.3 限制條件:一個必經點
程序說明:
Python 例程:
# 3. 限制條件:一個必經點 # 解決方案:分解為兩個問題,問題 1 為起點N0至必經點N6,問題 2 為必經點N6至終點N17 gAntP = gAnt.copy() minWPath3a = nx.dijkstra_path(gAntP, source=0, target=6) # N0 到 N6 的最短加權路徑 lMinWPath3a = nx.dijkstra_path_length(gAntP, source=0, target=6) # 最短加權路徑長度 minWPath3b = nx.dijkstra_path(gAntP, source=6, target=17) # N6 到 N17 的最短加權路徑 lMinWPath3b = nx.dijkstra_path_length(gAntP, source=6, target=17) # 最短加權路徑長度 minWPath3a.extend(minWPath3b[1:]) # 拼接 minWPath3a、minWPath3b 并去重 N7 print("\n問題3: 一個必經點(N6)的約束") print("N0 經 N6 到 N17 的最短加權路徑: ", minWPath3a) print("N0 經 N6 到 N17 的最短加權路徑長度: ", lMinWPath3a+lMinWPath3b)運行結果:
問題3: 一個必經點(N6)的約束 N0 經 N6 到 N17 的最短加權路徑: [0, 3, 6, 12, 16, 17] N0 經 N6 到 N17 的最短加權路徑長度: 73.4 限制條件:多個必經點(方案一)
程序說明:
- (1)生成指定起點、終點的所有簡單路徑;
- (2)判斷路徑是否滿足包括所有必經點的限制條件;
- (3)在滿足限制條件的簡單路徑中找加權長度最短的路徑;
- (4)求最短路徑的加權路徑長度。
Python 例程:
# 4. 限制條件:多個必經點 (N7,N15) # 解決方案:遍歷從起點到終點的簡單路徑,求滿足必經點條件的最短路徑 gAntM = gAnt.copy() minWPath4 = min([path # 返回 key 為最小值的 pathfor path in nx.all_simple_paths(gAntM, 0, 17) # gAnt 中所有起點為0、終點為17的簡單路徑if all(n in path for n in (7, 15))], # 滿足路徑中包括頂點 N7,N15key=lambda x: sum(gAntM.edges[edge]['weight'] for edge in nx.utils.pairwise(x))) # key 為加權路徑長度 lenPath4 = sum(gAntM.edges[edge]['weight'] for edge in nx.utils.pairwise(minWPath4)) # 求指定路徑的加權路徑長度 print("\n問題4: 多個必經點(N7, N15)的約束") print("N0 經 N7, N15 到 N17 的最短加權路徑: ", minWPath4) print("N0 經 N7, N15 到 N17 的最短加權路徑長度: ", lenPath4)運行結果:
問題4: 多個必經點(N7, N15)的約束 N0 經 N7, N15 到 N17 的最短加權路徑: [0, 3, 7, 8, 14, 15, 13, 17] N0 經 N7, N15 到 N17 的最短加權路徑長度: 83.5 限制條件:多個必經點(方案二)
程序說明
Python 例程
# 5. 限制條件:多個必經點 (N7,N15) # 解決方案:多重循環、判斷結構,遍歷從起點到終點的簡單路徑,求滿足必經點條件的最短路徑 gAntM = gAnt.copy() lMinWPath5 = minWPath5 = 1e9 for path in nx.all_simple_paths(gAntM, 0, 17):if all(n in path for n in (7,15)): # 滿足路徑中包括頂點 N7,N15lenPath = sum(gAntM.edges[edge]['weight'] for edge in nx.utils.pairwise(path))if lenPath < lMinWPath5:lMinWPath5 = lenPathminWPath5 = path print("\n問題5: 多個必經點(N7, N15)的約束") print("N0 經 N7, N15 到 N17 的最短加權路徑: ", minWPath5) print("N0 經 N7, N15 到 N17 的最短加權路徑長度: ", lMinWPath5)運行結果
問題5: 多個必經點(N7, N15)的約束 N0 經 N7, N15 到 N17 的最短加權路徑: [0, 3, 7, 8, 14, 15, 13, 17] N0 經 N7, N15 到 N17 的最短加權路徑長度: 83.6 限制條件:必經邊
程序說明
Python 例程
# 6. 限制條件:必經邊 (N2,N4), (N13,N14) # 解決方案:遍歷從起點到終點的簡單路徑,求滿足必經邊條件的最短路徑 gAntE = gAnt.copy() lMinWPath6 = minWPath6 = 1e9 # 置初值 for path in nx.all_simple_paths(gAntE, 0, 17): # 所有起點為0、終點為17的簡單路徑if all(n in path for n in (2,4,13,14)): # 滿足路徑中包括必經邊的頂點 N2,N4,N13,N14# 檢查 (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(gAntE.edges[edge]['weight'] for edge in nx.utils.pairwise(path))if lenPath < lMinWPath6:lMinWPath6 = lenPathminWPath6 = path print("\n問題6: 必經邊 (N2,N4), (N13,N14) 的約束") print("N0 到 N17 的最短加權路徑: ", minWPath6) print("N0 到 N17 的最短加權路徑長度: ", lMinWPath6)運行結果
問題6: 必經邊 (N2,N4), (N13,N14) 的約束 N0 到 N17 的最短加權路徑: [0, 2, 4, 5, 6, 7, 8, 14, 13, 17] N0 到 N17 的最短加權路徑長度: 104. NetworkX 求解螞蟻最優路徑問題
4.1 程序說明
4.2 Python 例程
# mathmodel17_v1.py # Demo17 of mathematical modeling algorithm # Demo of shortest path with constraints with NetworkX # Copyright 2021 YouCans, XUPT # Crated:2021-07-09import numpy as np import matplotlib.pyplot as plt # 導入 Matplotlib 工具包 import networkx as nx # 導入 NetworkX 工具包# 問題:螞蟻的最優路徑分析(西安郵電大學第12屆數學建模競賽B題) gAnt = nx.Graph() # 創建:空的 無向圖 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)]) # 向圖中添加多條賦權邊: (node1,node2,weight)pos={0:(0,8),1:(7,12),2:(6,9),3:(5,6),4:(11,10),5:(14,8), # 指定頂點位置6:(17,6),7:(10,4),8:(19,4),9:(18,12),10:(21,10),11:(28,12),12:(25,8),13:(30,7),14:(24,5),15:(29,4),16:(32,10),17:(37,8)}fig, ax = plt.subplots(figsize=(9, 6)) nx.draw(gAnt, pos, with_labels=True, node_color='cyan', alpha=0.8) labels = nx.get_edge_attributes(gAnt,'weight') # 邊的權值 nx.draw_networkx_edge_labels(gAnt,pos,edge_labels=labels, font_color='m') # 顯示邊的權值 nx.draw_networkx_nodes(gAnt,pos,nodelist=[0,17],node_color='yellow') # 設置頂點顏色:N1,N17 nx.draw_networkx_nodes(gAnt,pos,nodelist=[7,12],node_color='lime') # 設置頂點顏色:N7,N12 nx.draw_networkx_edges(gAnt,pos,edgelist=[(2,4),(13,14)],edge_color='lime',width=3) # 設置指定邊的顏色、寬度 nx.draw_networkx_edges(gAnt,pos,edgelist=[(11,12)],edge_color='r',width=3) # 設置指定邊的顏色、寬度# 7. 限制條件:必經點 N7,N12,必經邊 (N2,N4), (N13,N14),禁止邊 (11,12) # 解決方案:遍歷從起點到終點的簡單路徑,求滿足必經邊條件的最短路徑 gAntS = gAnt.copy() gAntS.remove_edge(11,12) # 刪除禁止邊 (11,12) lMinWPath = minWPath = 1e9 # 置初值 for path in nx.all_simple_paths(gAntS, 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(gAntS.edges[edge]['weight'] for edge in nx.utils.pairwise(path))if lenPath < lMinWPath:lMinWPath = lenPathminWPath = pathprint("\n螞蟻最優路徑問題(帶有禁止點、禁止邊、必經點、必經邊的約束條件)") print("約束條件:必經點 N7,N12,必經邊 (N2,N4), (N13,N14),禁止邊 (11,12)") print("N0 到 N17 的最短加權路徑: ", minWPath) print("N0 到 N17 的最短加權路徑長度: ", lMinWPath)edgeList = [] for i in range(len(minWPath)-1):edgeList.append((minWPath[i],minWPath[i+1])) nx.draw_networkx_edges(gAnt,pos,edgelist=edgeList,edge_color='b',width=4) # 設置邊的顏色 plt.show()4.3 運行結果
螞蟻最優路徑問題(帶有禁止點、禁止邊、必經點、必經邊的約束條件) 約束條件:必經點 N7,N12,必經邊 (N2,N4), (N13,N14),禁止邊 (11,12) N0 到 N17 的最短加權路徑: [0, 2, 4, 5, 6, 7, 8, 14, 13, 12, 16, 17] N0 到 N17 的最短加權路徑長度: 135. 總結
【本節完】
版權聲明:
歡迎關注『Python小白的數學建模課 @ Youcans』 原創作品
原創作品,轉載必須標注原文鏈接:(https://blog.csdn.net/youcans/article/details/118566373)。
Copyright 2021 Youcans, XUPT
Crated:2021-07-10
歡迎關注 『Python小白的數學建模課 @ Youcans』 系列,持續更新
Python小白的數學建模課-01.新手必讀
Python小白的數學建模課-02.數據導入
Python小白的數學建模課-03.線性規劃
Python小白的數學建模課-04.整數規劃
Python小白的數學建模課-05.0-1規劃
Python小白的數學建模課-06.固定費用問題
Python小白的數學建模課-07.選址問題
Python小白的數學建模課-09.微分方程模型
Python小白的數學建模課-10.微分方程邊值問題
Python小白的數學建模課-12.非線性規劃
Python小白的數學建模課-15.圖論的基本概念
Python小白的數學建模課-16.最短路徑算法
Python小白的數學建模課-17.條件最短路徑算法
Python小白的數學建模課-18.最小生成樹問題
Python小白的數學建模課-19.網絡流優化問題
Python小白的數學建模課-20.網絡流優化案例
Python小白的數學建模課-21.關鍵路徑法
Python小白的數學建模課-A1.國賽賽題類型分析
Python小白的數學建模課-21.關鍵路徑法
Python小白的數學建模課-22.插值方法
Python小白的數學建模課-A1.國賽賽題類型分析
Python小白的數學建模課-A2.2021年數維杯C題探討
Python小白的數學建模課-A3.12個新冠疫情數模競賽賽題及短評
Python小白的數學建模課-B2. 新冠疫情 SI模型
Python小白的數學建模課-B3. 新冠疫情 SIS模型
Python小白的數學建模課-B4. 新冠疫情 SIR模型
Python小白的數學建模課-B5. 新冠疫情 SEIR模型
Python小白的數學建模課-B6. 新冠疫情 SEIR改進模型
總結
以上是生活随笔為你收集整理的Python小白的数学建模课-17.条件最短路径算法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ftp同一主机的多个子进程使用同一个套接
- 下一篇: websocket python爬虫_p