Возможный дубликат:
[python]: путь между двумя узлами < / а>
Может ли кто-нибудь указать мне на ресурсы о том, как это сделать? Я использую networkx
в качестве библиотеки Python.
Спасибо!
Возможный дубликат:
[python]: путь между двумя узлами < / а>
Может ли кто-нибудь указать мне на ресурсы о том, как это сделать? Я использую networkx
в качестве библиотеки Python.
Спасибо!
Это основано на ответе Алекса Мартелли, но он должен работать. Это зависит от выражения source_node.children
, дающего итерацию, которая будет перебирать все дочерние элементы source_node
. Он также полагается на то, что у оператора ==
есть рабочий способ сравнения двух узлов, чтобы убедиться, что они одинаковы. Использование is
может быть лучшим выбором. По-видимому, в используемой вами библиотеке синтаксис для получения итерации по всем дочерним элементам равен graph[source_node]
, поэтому вам нужно будет соответствующим образом скорректировать код.
def allpaths(source_node, sink_node):
if source_node == sink_node: # Handle trivial case
return frozenset([(source_node,)])
else:
result = set()
for new_source in source_node.children:
paths = allpaths(new_source, sink_node, memo_dict)
for path in paths:
path = (source_node,) + path
result.add(path)
result = frozenset(result)
return result
Меня больше всего беспокоит то, что при этом выполняется поиск в глубину, это приведет к потере усилий, когда есть несколько путей от источника к узлу, являющемуся внуком, правнуком и т. Д., Все источники, но не обязательно родительские приемники. Если бы он запомнил ответ для данного узла источника и приемника, можно было бы избежать дополнительных усилий.
Вот пример того, как это будет работать:
def allpaths(source_node, sink_node, memo_dict = None):
if memo_dict is None:
# putting {}, or any other mutable object
# as the default argument is wrong
memo_dict = dict()
if source_node == sink_node: # Don't memoize trivial case
return frozenset([(source_node,)])
else:
pair = (source_node, sink_node)
if pair in memo_dict: # Is answer memoized already?
return memo_dict[pair]
else:
result = set()
for new_source in source_node.children:
paths = allpaths(new_source, sink_node, memo_dict)
for path in paths:
path = (source_node,) + path
result.add(path)
result = frozenset(result)
# Memoize answer
memo_dict[(source_node, sink_node)] = result
return result
Это также позволяет вам сохранять словарь мемоизации между вызовами, поэтому, если вам нужно вычислить ответ для нескольких узлов источника и приемника, вы можете избежать больших дополнительных усилий.
TypeError: can only concatenate tuple (not "instance") to tuple
с path = (source_node,) + path
- person Tyler; 19.07.2010
allpaths
, что может испортить ситуацию, если вы вызовете функцию более одного раза (рекурсия не учитывается) в той же программе. Исправлено.
- person Boris Gorelik; 30.08.2011
Это действительно работает с networkx, и это нерекурсивно, что может быть хорошо для больших графов.
def find_all_paths(graph, start, end):
path = []
paths = []
queue = [(start, end, path)]
while queue:
start, end, path = queue.pop()
print 'PATH', path
path = path + [start]
if start == end:
paths.append(path)
for node in set(graph[start]).difference(path):
queue.append((node, end, path))
return paths
Я не уверен, доступны ли специальные оптимизации - прежде чем искать какую-либо из них, я бы сделал простое рекурсивное решение, что-то вроде (использование networkx только той функции, что индексирование графа узлом дает итерацию, дающую его соседние узлы [[dict, в случае networkx, но я не использую его в частности]]) ...:
def allpaths(G, source_nodes, set_of_sink_nodes, path_prefix=()):
set_of_result_paths = set()
for n in source_nodes:
next_from_n = []
for an in G[n]:
if an in set_of_sink_nodes:
set_of_result_paths.add(path_prefix + (n, an))
else:
next_from_n.append(an)
if next_from_n:
set_of_result_paths.update(
allpaths(G, next_from_n, set_of_sink_nodes, path_prefix + (n,)))
return set_of_result_paths
Это должно быть доказуемо правильным (но я не собираюсь делать доказательства, потому что уже очень поздно, и я устал и нечетко ;-) и пригоден для проверки любых дальнейших оптимизаций ;-).
Первая оптимизация, которую я бы попробовал, была бы своего рода простым запоминанием: если я уже вычислил набор путей от некоторого узла N к любому целевому узлу (независимо от префикса, ведущего к N, когда я выполнял это вычисление), я могу спрятать это в dict под ключом N и избежать дальнейших перерасчетов, если и когда я снова доберусь до N другим маршрутом ;-).