#python #algorithm #networkx #igraph #shortest-path
#python #алгоритм #networkx #igraph #кратчайший путь
Вопрос:
Я задавал ранее аналогичный вопрос, но, как я думаю, это было неясно. У меня есть неориентированный и невзвешенный граф 10000
вершин и вокруг 5000000
ребер, которые я считываю в python как список ребер.
В моей работе я пытаюсь построить функцию из каждого ребра, которая зависит от расстояний между соседними вершинами на каждом ребре. Предположим, у нас есть две связанные вершины v1, v2 представляют собой ребро, для v1 есть n1 подключенных соседей, а также n2 соседей, подключенных к v2. Чтобы построить мою функцию, мне нужно получить расстояния между соседями n1 и n2. Для всех ребер в графе функция выглядит как:
e_1*d_1 e_1*d_2 ... e_1*d_n ...e_m*d_1 ...e_m*d_n
где n — количество соседей для обеих вершин на каждом ребре, d_n — расстояние между этими вершинами, m — количество ребер в графе, а e_m — последнее ребро в этом графе.
Обычно, если мы хотим получить длину кратчайшего пути, мы думаем о обходе графа, таком как алгоритм Дейкстры или Bfs, особенно если мой график невзвешен. Я использовал много функций, уже написанных в пакетах, таких как networkx
и igraph
, но эти функции неэффективны и отнимают много времени для моего графика. Например shortest_paths_dijkstra()
, для определения расстояния функции требуется около 6,9 часов, потому что мне нужно вызывать ее много-много раз. Кроме того, функция all_pairs_shortest_path _length
занимает около 13 минут (фиксируя длину пути, известную как cutoff, равной 3) и еще 16 минут для вызова расстояния для каждой пары соседей на графике!
Как написано в вопросе, нам нужно получить расстояние между соседями v1, v2, чтобы максимальное расстояние было равно 3, поскольку v1, v2 соединены. Я чувствую, что есть более умное решение для уменьшения временной сложности, используя тот факт, что возможные расстояния для пути (в моем случае) таковы 0, 1, 2, 3
, что мне не нужно проходить весь график для каждого пути между источником и целью! просто я ищу умный способ получить расстояние между соседями (не любые две случайные вершины)!
Я написал этот код, но это занимает много времени, около 54 минут, поэтому он также неэффективен!
neighbor1 = {}
neighbor2 = {}
distance = {}
for i in list(edges.values()):
list_of_distances = []
neighbor1 = tuple(graph.vs[graph.neighbors(i[0], mode="all")]["name"])
neighbor2 = tuple(graph.vs[graph.neighbors(i[1], mode="all")]["name"])
for n2 in neighbor2:
for n1 in neighbor1:
if n1 == n2:
list_of_distances.append(0)
elif (n1 != n2) and not graph.are_connected(n1,n2):
if ( graph.are_connected(i[0],n2) ) or ( graph.are_connected(n1,i[1]) ):
list_of_distances.append(2)
elif ( not graph.are_connected(i[0],n2) ) or ( not graph.are_connected(n1,i[1]) ):
list_of_distances.append(3)
else:
list_of_distances.append(1)
distance[tuple(i)] = list_of_distances
Я хотел бы знать, есть ли другой способ, который не требует много памяти и времени для получения этих расстояний, или если возможно изменить один из методов обхода графа, таких как Bfs или Dijkstra, чтобы не было необходимости искать весь граф на каждой итерации и просто делать что-то локальное (еслиможно сказать). Спасибо за любую помощь
Комментарии:
1. Определите функцию, которую вы ищете: аргументы, возвращаемое значение.
2. Если
Vx
иVy
(напрямую) связаны, каково расстояние между ними? Как получить нулевое расстояние?3. @wwii
Vx==Vy
.4. @wwii расстояние между любыми двумя вершинами, представляющими ребро в графе, фиксируется равным 1, поскольку они соединены. На самом деле я не вычисляю расстояние между этими вершинами, а вместо этого мне нужно получить расстояние между их соседями, поэтому, если обе вершины имеют общего соседа, тогда расстояние равно 0
5. Ваша функция немного неясна, каково значение e_1 и тому подобное? Возможно, добавление кода, который вы используете для его вычисления, поможет
Ответ №1:
У вас чрезвычайно сложная задача, поэтому нормально, что ваш скрипт работает часами. Вы можете попытаться распараллелить его с помощью CUDA или чего-то подобного, или вы можете попытаться создать большой кеш (GBs). Но если вы не можете или не хотите, я предлагаю вам не использовать функции networkx / igraph, потому что они очень медленные для вас. Вы можете решить свою проблему без 1000000 запусков DFS. Вот одно из возможных решений с использованием наборов Python (я думаю, это будет быстрее, чем у вас, может быть, не очень).
import networkx as nx
# Create a graph like yours
G = nx.fast_gnp_random_graph(1000, 0.05)
# Create neighbours dict
G_adj = dict(G.adjacency())
nbrs_dict = {node: {n for n in G_adj[node]} for node in G_adj}
# Result dict
distances = {}
# For every edge:
for e in G.edges:
# Start value
dist_value = 0
# Get N1 and N2 neighbours
n1_nbrs = nbrs_dict[e[0]]
n2_nbrs = nbrs_dict[e[1]]
# Triangles - nodes that connected to both N1 and N2
# Every triangle value = 0
tri = n1_nbrs amp; n2_nbrs
for t in tri:
# Get neighbours to find nodes with distance length = 2
t_nbrs = nbrs_dict[t]
t_in_n1 = n1_nbrs amp; t_nbrs
t_in_n2 = n2_nbrs amp; t_nbrs
t_not_in_n1 = n1_nbrs - t_nbrs
t_not_in_n2 = n2_nbrs - t_nbrs
dist_value = len(t_in_n1) len(t_in_n2)
dist_value = (2 * len(t_not_in_n1)) (2 * len(t_not_in_n2))
# Exclude all triangle nodes because we processed them all
n1nt_nbrs = n1_nbrs - tri
n2nt_nbrs = n2_nbrs - tri
# Select squares - nodes with length = 1
direct = set([])
for n1 in n1nt_nbrs:
nbrs = nbrs_dict[n1]
d = nbrs amp; n2nt_nbrs
for node in d:
direct.add((n1, node))
dist_value = len(direct)
# Exclude squares so we have only nodes with length = 3
n1final = n1nt_nbrs - set(e[0] for e in direct)
n2final = n2nt_nbrs - set(e[1] for e in direct)
dist_value = 3 * len(n1final) * len(n2final)
# Distance for an edge
distances[e] = dist_value
В любом случае, ваша проблема O(n^3)
сложна, поэтому я настоятельно рекомендую вам попытаться разделить ваш график. Может быть, у вас есть мосты или просто несколько связанных компонентов. Если вы будете обрабатывать их по отдельности, вы невероятно увеличите свою скорость.
Комментарии:
1. Спасибо за вашу помощь. Я еще не анализировал ваш алгоритм, но в качестве первого шага я проверил полученные расстояния, и это было странно! Я применил код th к этому небольшому графу: 1 2 2 3 2 4 3 4 4 5 Полученный словарь расстояний между соседями вершин на каждом ребре должен быть таким {(‘1’, ‘2’): [1, 1, 1], (‘2′,’3’): [1, 1, 1, 2, 1, 0], (‘2’, ‘4’): [1, 1, 1, 2, 0, 1, 3, 2, 1], (‘3’, ‘4’): [0, 1, 1, 1, 2, 1], (‘4’, ‘5’): [1, 1, 1]} в то время как ваши расстояния {(‘1’, ‘2’): 9, (‘2’, ‘3’): 10,(‘2’, ‘4’): 13, (‘3’, ‘4’): 10, (‘4’, ‘5’): 9}? Я не понял, что происходит!
2. Этот алгоритм не вычисляет каждую возможную длину отдельно, поэтому я надеюсь, что это будет быстрее. Он создает группы пар узлов с каждой длиной, умножает на ее длину и суммирует ее. Как я понимаю, вам не нужны эти массивы, вам нужна их сумма, поэтому применим такой алгоритм.
3. Плохие новости: если вам нужны массивы, мой алгоритм бесполезен, вам следует использовать свой (возможно, с моими изменениями в отношении neighbours dict , я думаю, это будет работать немного быстрее). В этом случае я настоятельно рекомендую вам попытаться разделить ваш график, потому что ваш алгоритм почти оптимален для вашей задачи. Если вам нужна большая скорость, вам следует вместо этого использовать C .
4. 1. Я был не очень корректен, это не O (N ^ 3), это просто кубический. У вас есть N узлов и E ребер. Для каждого ребра (E) вы проверяете все пары соседей, которые есть
avg(nbrs)^2
.avg(nbrs)
может быть представлено как (E / N), поэтому мы имеемO(E^3/N^2)
сE >> N
.5. 2. Я рекомендую вам использовать программное обеспечение Gephi для этого. Вы можете использовать принудительные макеты, чтобы внимательно посмотреть на свой график, Вы можете использовать множество функций анализа и т. Д. Вы также можете анализировать свой график с помощью networkx (
bridges
connectivity
и других типов алгоритмов).