реализация k кратчайших путей в Igraph / networkx (алгоритм Йена)

После тщательного исследования и на основе этого, это и многое другое. Мне было предложено реализовать алгоритм k кратчайших путей, чтобы найти первый, второй, третий ... k-й кратчайший путь в большом неориентированный, циклический, взвешенный граф. Около 2000 узлов.

Псевдокод в Википедии следующий:

function YenKSP(Graph, source, sink, K):
  //Determine the shortest path from the source to the sink.
 A[0] = Dijkstra(Graph, source, sink);
 // Initialize the heap to store the potential kth shortest path.
 B = [];

for k from 1 to K:
   // The spur node ranges from the first node to the next to last node in the shortest path.
   for i from 0 to size(A[i]) − 1:

       // Spur node is retrieved from the previous k-shortest path, k − 1.
       spurNode = A[k-1].node(i);
       // The sequence of nodes from the source to the spur node of the previous k-shortest path.
       rootPath = A[k-1].nodes(0, i);

       for each path p in A:
           if rootPath == p.nodes(0, i):
               // Remove the links that are part of the previous shortest paths which share the same root path.
               remove p.edge(i, i) from Graph;

       // Calculate the spur path from the spur node to the sink.
       spurPath = Dijkstra(Graph, spurNode, sink);

       // Entire path is made up of the root path and spur path.
       totalPath = rootPath + spurPath;
       // Add the potential k-shortest path to the heap.
       B.append(totalPath);

       // Add back the edges that were removed from the graph.
       restore edges to Graph;

   // Sort the potential k-shortest paths by cost.
   B.sort();
   // Add the lowest cost path becomes the k-shortest path.
   A[k] = B[0];
return A;

Основная проблема в том, что я еще не мог написать правильный скрипт на Python для этого (удалить края и правильно установить их на место), поэтому я дошел до этого только с использованием Igraph, как обычно:

def yenksp(graph,source,sink, k):
    global distance
    """Determine the shortest path from the source to the sink."""
    a = graph.get_shortest_paths(source, sink, weights=distance, mode=ALL, output="vpath")[0]
    b = [] #Initialize the heap to store the potential kth shortest path
    #for xk in range(1,k):
    for xk in range(1,k+1):
        #for i in range(0,len(a)-1):
        for i in range(0,len(a)):
            if i != len(a[:-1])-1:
                spurnode = a[i]
                rootpath = a[0:i]
                #I should remove edges part of the previous shortest paths, but...:
                for p in a:
                    if rootpath == p:
                        graph.delete_edges(i) 

            spurpath = graph.get_shortest_paths(spurnode, sink, weights=distance, mode=ALL, output="vpath")[0]
            totalpath = rootpath + spurpath
            b.append(totalpath)
            # should restore the edges
            # graph.add_edges([(0,i)]) <- this is definitely not correct.
            graph.add_edges(i)
        b.sort()
        a[k] = b[0]
    return a

Это действительно плохая попытка, и она возвращает только список в списке

Я уже не очень уверен, что делаю, и я уже очень отчаялся с этой проблемой, и в последние дни моя точка зрения на это была изменена на 180 градусов и даже один раз. Я просто новичок, старающийся изо всех сил. Пожалуйста помоги. Также можно предложить реализацию Networkx.

P.S. Скорее всего, нет других рабочих способов по этому поводу, потому что мы уже исследовали это здесь. Я уже получил много предложений и многим обязан сообществу. DFS или BFS не работают. График огромен.

Изменить: я продолжаю исправлять скрипт Python. Короче говоря, цель этого вопроса - правильный сценарий.


person Laci    schedule 08.04.2013    source источник


Ответы (2)


На Github есть реализация Yen's KSP на Python, YenKSP. Отдавая должное автору, суть алгоритма представлена ​​здесь:

def ksp_yen(graph, node_start, node_end, max_k=2):
    distances, previous = dijkstra(graph, node_start)

    A = [{'cost': distances[node_end], 
          'path': path(previous, node_start, node_end)}]
    B = []

    if not A[0]['path']: return A

    for k in range(1, max_k):
        for i in range(0, len(A[-1]['path']) - 1):
            node_spur = A[-1]['path'][i]
            path_root = A[-1]['path'][:i+1]

            edges_removed = []
            for path_k in A:
                curr_path = path_k['path']
                if len(curr_path) > i and path_root == curr_path[:i+1]:
                    cost = graph.remove_edge(curr_path[i], curr_path[i+1])
                    if cost == -1:
                        continue
                    edges_removed.append([curr_path[i], curr_path[i+1], cost])

            path_spur = dijkstra(graph, node_spur, node_end)

            if path_spur['path']:
                path_total = path_root[:-1] + path_spur['path']
                dist_total = distances[node_spur] + path_spur['cost']
                potential_k = {'cost': dist_total, 'path': path_total}

                if not (potential_k in B):
                    B.append(potential_k)

            for edge in edges_removed:
                graph.add_edge(edge[0], edge[1], edge[2])

        if len(B):
            B = sorted(B, key=itemgetter('cost'))
            A.append(B[0])
            B.pop(0)
        else:
            break

    return A
person Hooked    schedule 08.04.2013
comment
Это действительно приближает меня на один шаг к решению, потому что я могу проанализировать, как он сохранил-удалил края, а затем добавил их обратно. В любом случае очень сложно следовать этому примеру, потому что он вызывает сценарии, которые он написал для DiGraph с участием многих библиотек, которые, надеюсь, не так необходимы в этом случае (включая библиотеки, вызывающие внешние функции, я думаю). Я продолжаю подходить к этому с Igraph и посмотрим. Спасибо. Возможно, это ответ, но я его пока не вижу. Я это анализирую. - person Laci; 08.04.2013
comment
Мне удалось его построить, потратив время на алгоритм в Википедии и на основе этого примера выше. Только при использовании Igraph (и, возможно, других библиотек) нужно только позаботиться о том, чтобы в методе лучше использовать глубокую копию графика. Таким образом, он не испортит исходные идентификаторы ребер графа, когда вы добавите ребра обратно, чтобы вы могли продолжить работу с ними. - person Laci; 10.04.2013

У меня была та же проблема, что и у вас, поэтому я портировал псевдокод Википедии для алгоритма Йена для использования в Python с библиотекой igraph.

Вы можете найти его здесь: https://gist.github.com/ALenfant/5491853

person Aweb    schedule 30.04.2013