Длина кратчайшего пути между соседями связанных вершин (не любые две случайные вершины!)

#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 и других типов алгоритмов).