我正在使用networkx来解决最短路径问题。我主要使用shortest_path。我想知道,使用当前版本的networkx,是否可以限制最短路径计算?
下面的示例生成了 A 和 G 之间的两条最短路径:
左图显示了最小化“长度”属性时的最佳路线,右图显示了最小化“高度”属性时的最佳路线。
如果我们计算这些路线的统计数据,我们得到:
Best route by length: ['A', 'B', 'E', 'F', 'D', 'G']
Best route by height: ['A', 'C', 'E', 'G']
Stats best routes: {
'by_length': {'length': 13.7, 'height': 373.0},
'by_height': {'length': 24.8, 'height': 115.0}
}
有没有办法在计算最短路径时添加约束? (例如,通过最小化长度属性但同时保持
height<300
来计算最短路径
计算图网络的代码:
import matplotlib.pyplot as plt
from itertools import combinations, groupby
import os
import numpy as np
import networkx as nx
import random
# 1. Define test network
MG = nx.MultiDiGraph()
MG.add_edges_from([("B", "A", {"length": 0.8}), ("A", "B", {"length": 1.}), ("D", "G", {"length": 3.5}),
("B", "D", {"length": 20.8}), ("A", "C", {"length": 9.7}), ("D", "C", {"length": 0.3}),
("B", "E", {"length": 4.8}), ("D", "E", {"length": 0.05}), ("C", "E", {"length": 0.1}),
("E", "C", {"length": 0.7}), ("E", "F", {"length": 0.4}), ("E", "G", {"length": 15.}),
("F", "C", {"length": 0.9}), ("F", "D", {"length": 4.}),
])
attrs = {'B': {"x": -20., "y": 60.}, 'A': {"x": 28., "y":55.},
'C': {"x": -12., "y": 40.}, 'D': {"x": 40., "y":45.},
'E': {"x": 8., "y": 35.}, 'F': {"x": -8., "y":15.},
'G': {"x": 21., "y":5.},
}
for num, (k,v) in enumerate(attrs.items()):
attrs[k]={**v,
}
nx.set_node_attributes(MG, attrs)
rng = np.random.default_rng(seed=42)
random_height = list(rng.uniform(low=0, high=100, size=len(MG.edges)).round(0))
attrs={}
for num, edge in enumerate(MG.edges):
attrs[edge]={'height': random_height[num]}
nx.set_edge_attributes(MG, attrs)
# 2. Calculate shortest route
best_route_by_length = nx.shortest_path(MG, "A", "G",weight="length")
print(f"Best route by length: {best_route_by_length}")
best_route_by_height = nx.shortest_path(MG, "A", "G",weight="height")
print(f"Best route by height: {best_route_by_height}")
# 3. Get stats for each route
def get_route_edge_attributes(
G, route, attribute=None, minimize_key="length", retrieve_default=None
):
"""
"""
attribute_values = []
for u, v in zip(route[:-1], route[1:]):
# if there are parallel edges between two nodes, select the one with the
# lowest value of minimize_key
data = min(G.get_edge_data(u, v).values(), key=lambda x: x[minimize_key])
if attribute is None:
attribute_value = data
elif retrieve_default is not None:
attribute_value = data.get(attribute, retrieve_default(u, v))
else:
attribute_value = data[attribute]
attribute_values.append(attribute_value)
return attribute_values
stats_best_route={}
stats_best_route['by_length']={'length': sum(get_route_edge_attributes(MG,
best_route_by_length,
'length')),
'height': sum(get_route_edge_attributes(MG,
best_route_by_length,
'height'))}
stats_best_route['by_height']={'length': sum(get_route_edge_attributes(MG,
best_route_by_height,
'length')),
'height': sum(get_route_edge_attributes(MG,
best_route_by_height,
'height'))}
print(f"Stats best routes: {stats_best_route}")
我的问题可能太大,无法使用暴力解决方案。
我还在networkx讨论页面中发表了一篇文章,似乎可以实现一个在
single_source_dijkstra
中执行数字操作的特殊类。
然而,这一解决方案并不完美:如果有两条可接受的短路径(高度),算法将找到较短的一条,无论其高度如何。当存在允许的路径时,这可能会使到达目标节点看起来不可能......(更多详细信息请参见:https://github.com/networkx/networkx/discussions/4832#discussioncomment-778358)
大致解决方案:
障碍:
如果最大高度的路径链接至关重要,删除它将使目的地无法到达。 如果发生这种情况,则有必要返回,替换最高的链接并删除第二高的链接......
不能保证找到最佳路径。
all_simple_paths()
?
from networkx.algorithms.simple_paths import all_simple_paths
shortest_paths_constrained = []
for path in all_simple_paths(MG, "A", "G"):
if sum(get_route_edge_attributes(MG, path, 'height')) < 300:
path_length = sum(get_route_edge_attributes(MG, path, 'length'))
result = (path, path_length)
if not shortest_paths_constrained:
shortest_paths_constrained.append(result)
elif path_length == shortest_paths_constrained[0][1]:
shortest_paths_constrained.append(result)
elif path_length < shortest_paths_constrained[0][1]:
shortest_paths_constrained = [result]
给出
shortest_paths_constrained
作为
[(['A', 'C', 'E', 'F', 'D', 'G'], 17.7)]
效率低下——但是,如果可行的话,它肯定会给出正确的解决方案。 请注意,如果它对您的问题很重要,它还应该在
length
约束内返回所有最短的 height
关系。 如果需要,您想如何打破可能的联系取决于您。
好消息是
all_simple_paths()
返回一个生成器,而不是尝试一次查找所有路径,因此无需担心将所有路径拟合到内存中。然而,完成计算需要多长时间是一个更大的谜......
这可能是一个幼稚的答案,因为我实际上刚刚安装了networkx并且面临着类似的问题......但是复制图表并删除所有不满足约束的边是否可行?
import networkx as nx
MG = nx.MultiDiGraph()
MG.add_edge('a', 'b')
MG.add_edge('b', 'c')
MG.add_edge('c', 'd')
MG.add_edge('b', 'd', aura="orange")
assert nx.shortest_path(MG, 'a', 'd') == ['a', 'b', 'd']
MG_no_orange = MG.copy()
MG_no_orange.remove_edges_from(e for e in MG.edges if MG.edges[e].get('aura') == 'orange')
assert nx.shortest_path(MG_no_orange, 'a', 'd') == ['a', 'b', 'c', 'd']