web-dev-qa-db-de.com

Finden Sie die Pfade zwischen zwei gegebenen Knoten?

Angenommen, ich habe Knoten wie folgt verbunden. Wie erreiche ich die Anzahl der Pfade, die zwischen bestimmten Punkten bestehen, und die Pfadangaben?

1,2 //node 1 and 2 are connected
2,3
2,5
4,2
5,11
11,12
6,7
5,6
3,6
6,8
8,10
8,9

Finde die Pfade von 1 bis 7:

Antwort: 2 Pfade gefunden und sie sind

1,2,3,6,7
1,2,5,6,7

alt text

implementierung gefunden hier ist nett, ich werde das gleiche verwenden

Hier ist der Ausschnitt aus dem obigen Link in Python

# a sample graph
graph = {'A': ['B', 'C','E'],
             'B': ['A','C', 'D'],
             'C': ['D'],
             'D': ['C'],
             'E': ['F','D'],
             'F': ['C']}

class MyQUEUE: # just an implementation of a queue

    def __init__(self):
        self.holder = []

    def enqueue(self,val):
        self.holder.append(val)

    def dequeue(self):
        val = None
        try:
            val = self.holder[0]
            if len(self.holder) == 1:
                self.holder = []
            else:
                self.holder = self.holder[1:]   
        except:
            pass

        return val  

    def IsEmpty(self):
        result = False
        if len(self.holder) == 0:
            result = True
        return result


path_queue = MyQUEUE() # now we make a queue


def BFS(graph,start,end,q):

    temp_path = [start]

    q.enqueue(temp_path)

    while q.IsEmpty() == False:
        tmp_path = q.dequeue()
        last_node = tmp_path[len(tmp_path)-1]
        print tmp_path
        if last_node == end:
            print "VALID_PATH : ",tmp_path
        for link_node in graph[last_node]:
            if link_node not in tmp_path:
                #new_path = []
                new_path = tmp_path + [link_node]
                q.enqueue(new_path)

BFS(graph,"A","D",path_queue)

-------------results-------------------
['A']
['A', 'B']
['A', 'C']
['A', 'E']
['A', 'B', 'C']
['A', 'B', 'D']
VALID_PATH :  ['A', 'B', 'D']
['A', 'C', 'D']
VALID_PATH :  ['A', 'C', 'D']
['A', 'E', 'F']
['A', 'E', 'D']
VALID_PATH :  ['A', 'E', 'D']
['A', 'B', 'C', 'D']
VALID_PATH :  ['A', 'B', 'C', 'D']
['A', 'E', 'F', 'C']
['A', 'E', 'F', 'C', 'D']
VALID_PATH :  ['A', 'E', 'F', 'C', 'D']
43
yesraaj

Breitensuche durchläuft einen Graphen und findet tatsächlich alle Pfade von einem Startknoten. Normalerweise behält BFS jedoch nicht alle Pfade bei. Stattdessen wird eine Vorgängerfunktion π aktualisiert, um den kürzesten Pfad zu speichern. Sie können den Algorithmus einfach so ändern, dass π(n) nicht nur one Vorgänger speichert, sondern eine Liste möglicher Vorgänger.

In dieser Funktion werden dann alle möglichen Pfade codiert und durch rekursives Durchlaufen von π erhalten Sie alle möglichen Pfadkombinationen.

Ein guter Pseudocode, der diese Notation verwendet, ist in Introduction to Algorithms von Cormen et al. zu finden und wurde später in vielen Skripten der Universität zu diesem Thema verwendet. Eine Google-Suche nach "BFS-Pseudocode-Vorgänger π" entwurzelt dieser Treffer bei Stack Exchange .

33
Konrad Rudolph

Für diejenigen, die nicht PYTHON Experte sind, der gleiche Code in C++

//@Author :Ritesh Kumar Gupta
#include <stdio.h>
#include <vector>
#include <algorithm>
#include <vector>
#include <queue>
#include <iostream>
using namespace std;
vector<vector<int> >GRAPH(100);
inline void print_path(vector<int>path)
{
    cout<<"[ ";
    for(int i=0;i<path.size();++i)
    {
        cout<<path[i]<<" ";
    }
    cout<<"]"<<endl;
}
bool isadjacency_node_not_present_in_current_path(int node,vector<int>path)
{
    for(int i=0;i<path.size();++i)
    {
        if(path[i]==node)
        return false;
    }
    return true;
}
int findpaths(int source ,int target ,int totalnode,int totaledge )
{
    vector<int>path;
    path.Push_back(source);
    queue<vector<int> >q;
    q.Push(path);

    while(!q.empty())
    {
        path=q.front();
        q.pop();

        int last_nodeof_path=path[path.size()-1];
        if(last_nodeof_path==target)
        {
            cout<<"The Required path is:: ";
            print_path(path);
        }
        else
        {
            print_path(path);
        }

        for(int i=0;i<GRAPH[last_nodeof_path].size();++i)
        {
            if(isadjacency_node_not_present_in_current_path(GRAPH[last_nodeof_path][i],path))
            {

                vector<int>new_path(path.begin(),path.end());
                new_path.Push_back(GRAPH[last_nodeof_path][i]);
                q.Push(new_path);
            }
        }




    }
    return 1;
}
int main()
{
    //freopen("out.txt","w",stdout);
    int T,N,M,u,v,source,target;
    scanf("%d",&T);
    while(T--)
    {
        printf("Enter Total Nodes & Total Edges\n");
        scanf("%d%d",&N,&M);
        for(int i=1;i<=M;++i)
        {
            scanf("%d%d",&u,&v);
            GRAPH[u].Push_back(v);
        }
        printf("(Source, target)\n");
        scanf("%d%d",&source,&target);
        findpaths(source,target,N,M);
    }
    //system("pause");
    return 0;
}

/*
Input::
1
6 11
1 2 
1 3
1 5
2 1
2 3
2 4
3 4
4 3
5 6
5 4
6 3
1 4

output:
[ 1 ]
[ 1 2 ]
[ 1 3 ]
[ 1 5 ]
[ 1 2 3 ]
The Required path is:: [ 1 2 4 ]
The Required path is:: [ 1 3 4 ]
[ 1 5 6 ]
The Required path is:: [ 1 5 4 ]
The Required path is:: [ 1 2 3 4 ]
[ 1 2 4 3 ]
[ 1 5 6 3 ]
[ 1 5 4 3 ]
The Required path is:: [ 1 5 6 3 4 ]


*/
23
ritesh_NITW

Der Dijkstra-Algorithmus wendet mehr auf gewichtete Pfade an, und es scheint, als wollte das Poster alle Pfade finden, nicht nur die kürzesten.

Für diese Anwendung würde ich ein Diagramm erstellen (Ihre Anwendung hört sich so an, als müsste sie nicht weitergeleitet werden) und Ihre bevorzugte Suchmethode verwenden. Es hört sich so an, als ob Sie alle Pfade wollen, nicht nur den kürzesten, sondern einen einfachen rekursiven Algorithmus Ihrer Wahl.

Das einzige Problem dabei ist, ob der Graph zyklisch sein kann.

Mit den Anschlüssen:

  • 1, 2
  • 1, 3
  • 2, 3
  • 2, 4

Wenn Sie nach einem Pfad von 1 bis 4 suchen, können Sie einen Zyklus von 1 bis 2 bis 3 bis 1 haben.

In diesem Fall würde ich einen Stapel behalten, der die Knoten durchläuft. Hier ist eine Liste mit den Schritten für dieses Diagramm und dem daraus resultierenden Stapel (Entschuldigung für die Formatierung - keine Tabellenoption):

aktueller Knoten (mögliche nächste Knoten minus woher wir kamen) [Stapel]

  1. 1 (2, 3) [1]
  2. 2 (3, 4) [1, 2]
  3. (1) [1, 2, 3]
  4. 1 (2, 3) [1, 2, 3, 1] // Fehler - doppelte Nummer im Stapelzyklus erkannt
  5. () [1, 2, 3] // Auf Knoten 3 zurückgetreten und 1 vom Stapel genommen. Von hier aus sind keine Knoten mehr zu erkunden
  6. 2 (4) [1, 2] // Auf Knoten 2 zurückgetreten und 1 vom Stapel gesprungen.
  7. 4 () [1, 2, 4] // Zielknoten gefunden - Datensatzstapel für einen Pfad. Von hier aus sind keine Knoten mehr zu erkunden
  8. 2 () [1, 2] // trat zu Knoten 2 zurück und warf 4 vom Stapel. Von hier aus sind keine Knoten mehr zu erkunden
  9. 1 (3) [1] // trat zu Knoten 1 zurück und warf 2 vom Stapel.
  10. (2) [1, 3]
  11. 2 (1, 4) [1, 3, 2]
  12. 1 (2, 3) [1, 3, 2, 1] // Fehler - doppelte Nummer im Stapelzyklus erkannt
  13. 2 (4) [1, 3, 2] // Auf Knoten 2 zurückgetreten und 1 vom Stapel genommen
  14. 4 () [1, 3, 2, 4] Zielknoten gefunden - Datensatzstapel für einen Pfad. Von hier aus sind keine Knoten mehr zu erkunden
  15. 2 () [1, 3, 2] // trat zu Knoten 2 zurück und warf 4 vom Stapel. Keine weiteren Knoten
  16. () [1, 3] // trat zu Knoten 3 zurück und warf 2 vom Stapel. Keine weiteren Knoten
  17. 1 () [1] // ist zu Knoten 1 zurückgetreten und hat 3 vom Stapel genommen. Keine weiteren Knoten
  18. Fertig mit 2 aufgezeichneten Pfaden von [1, 2, 4] und [1, 3, 2, 4]
7
Marc

Der ursprüngliche Code ist etwas umständlich und Sie können stattdessen "collections.deque" verwenden, wenn Sie mithilfe von BFS ermitteln möchten, ob ein Pfad zwischen zwei Punkten im Diagramm vorhanden ist. Hier ist eine schnelle Lösung, die ich gehackt habe:

Hinweis: Diese Methode wird möglicherweise unendlich fortgesetzt, wenn zwischen den beiden Knoten kein Pfad vorhanden ist. Ich habe nicht alle Fälle getestet, YMMV.

from collections import deque

# a sample graph
  graph = {'A': ['B', 'C','E'],
           'B': ['A','C', 'D'],
           'C': ['D'],
           'D': ['C'],
           'E': ['F','D'],
           'F': ['C']}

   def BFS(start, end):
    """ Method to determine if a pair of vertices are connected using BFS

    Args:
      start, end: vertices for the traversal.

    Returns:
      [start, v1, v2, ... end]
    """
    path = []
    q = deque()
    q.append(start)
    while len(q):
      tmp_vertex = q.popleft()
      if tmp_vertex not in path:
        path.append(tmp_vertex)

      if tmp_vertex == end:
        return path

      for vertex in graph[tmp_vertex]:
        if vertex not in path:
          q.append(vertex)
3

In Prolog (speziell SWI-Prolog)

:- use_module(library(tabling)).

% path(+Graph,?Source,?Target,?Path)
:- table path/4.

path(_,N,N,[N]).
path(G,S,T,[S|Path]) :-
    dif(S,T),
    member(S-I, G), % directed graph
    path(G,I,T,Path).

prüfung:

paths :- Graph =
    [ 1- 2  % node 1 and 2 are connected
    , 2- 3 
    , 2- 5 
    , 4- 2 
    , 5-11
    ,11-12
    , 6- 7 
    , 5- 6 
    , 3- 6 
    , 6- 8 
    , 8-10
    , 8- 9
    ],
    findall(Path, path(Graph,1,7,Path), Paths),
    maplist(writeln, Paths).

?- paths.
[1,2,3,6,7]
[1,2,5,6,7]
true.
3
CapelliC

gegeben die Adjazenzmatrix:

{0, 1, 3, 4, 0, 0}

{0, 0, 2, 1, 2, 0}

{0, 1, 0, 3, 0, 0}

{0, 1, 1, 0, 0, 1}

{0, 0, 0, 0, 0, 6}

{0, 1, 0, 1, 0, 0}

der folgende Wolfram Mathematica-Code löst das Problem, indem alle einfachen Pfade zwischen zwei Knoten eines Graphen gefunden werden. Ich benutzte eine einfache Rekursion und zwei globale Variablen, um die Zyklen zu verfolgen und die gewünschte Ausgabe zu speichern. Der Code wurde nicht nur aus Gründen der Code-Klarheit optimiert. Das "Drucken" sollte hilfreich sein, um zu verdeutlichen, wie es funktioniert.

cycleQ[l_]:=If[Length[DeleteDuplicates[l]] == Length[l], False, True];
getNode[matrix_, node_]:=Complement[Range[Length[matrix]],Flatten[Position[matrix[[node]], 0]]];

builtTree[node_, matrix_]:=Block[{nodes, posAndNodes, root, pos},
    If[{node} != {} && node != endNode ,
        root = node;
        nodes = getNode[matrix, node];
        (*Print["root:",root,"---nodes:",nodes];*)

        AppendTo[lcycle, Flatten[{root, nodes}]];
        If[cycleQ[lcycle] == True,
            lcycle = Most[lcycle]; appendToTree[root, nodes];,
            Print["paths: ", tree, "\n", "root:", root, "---nodes:",nodes];
            appendToTree[root, nodes];

        ];
    ];

appendToTree[root_, nodes_] := Block[{pos, toAdd},
    pos = Flatten[Position[tree[[All, -1]], root]];
    For[i = 1, i <= Length[pos], i++,
        toAdd = Flatten[Thread[{tree[[pos[[i]]]], {#}}]] & /@ nodes;
        (* check cycles!*)            
        If[cycleQ[#] != True, AppendTo[tree, #]] & /@ toAdd;
    ];
    tree = Delete[tree, {#} & /@ pos];
    builtTree[#, matrix] & /@ Union[tree[[All, -1]]];
    ];
];

um den Code aufzurufen: initNode = 1; endNode = 6; lcycle = {}; tree = {{initNode}}; builtTree [initNode, matrix];

pfade: {{1}} Wurzel: 1 --- Knoten: {2,3,4}

pfade: {{1,2}, {1,3}, {1,4}} Wurzel: 2 --- Knoten: {3,4,5}

pfade: {{1,3}, {1,4}, {1,2,3}, {1,2,4}, {1,2,5}} Wurzel: 3 --- Knoten: {2, 4}

pfade: {{1,4}, {1,2,4}, {1,2,5}, {1,3,4}, {1,2,3,4}, {1,3,2} 4}, {1,3,2,5}} Wurzel: 4 --- Knoten: {2,3,6}

pfade: {{1,2,5}, {1,3,2,5}, {1,4,6}, {1,2,4,6}, {1,3,4,6}, { 1,2,3,4,6}, {1,3,2,4,6}, {1,4,2,5}, {1,3,4,2,5}, {1,4, 3,2,5}} root: 5 --- node: {6}

ERGEBNISSE: {{1, 4, 6}, {1, 2, 4, 6}, {1, 2, 5, 6}, {1, 3, 4, 6}, {1, 2, 3, 4, 6}, {1, 3, 2, 4, 6}, {1, 3, 2, 5, 6}, {1, 4, 2, 5, 6}, {1, 3, 4, 2, 5, 6}, {1, 4, 3, 2, 5, 6}}

... Leider kann ich keine Bilder hochladen, um die Ergebnisse besser darzustellen :(

http://textanddatamining.blogspot.com

2
user1619876

Wenn Sie alle Pfade verwenden möchten, verwenden Sie die Rekursion.

Erstellen Sie vorzugsweise mithilfe einer Adjazenzliste eine Funktion f(), die versucht, eine aktuelle Liste der besuchten Scheitelpunkte auszufüllen.

void allPaths(vector<int> previous, int current, int destination)
{
    previous.Push_back(current);

    if (current == destination)
        //output all elements of previous, and return

    for (int i = 0; i < neighbors[current].size(); i++)
        allPaths(previous, neighbors[current][i], destination);
}

int main()
{
    //...input
    allPaths(vector<int>(), start, end);
}

Aufgrund der Tatsache, dass der Vektor als Wert übergeben wird (und daher alle Änderungen, die weiter unten in der rekursiven Prozedur vorgenommen werden, nicht permanent sind), werden alle möglichen Kombinationen aufgelistet.

Sie können ein wenig Effizienz erzielen, indem Sie den vorherigen -Vektor als Referenz übergeben (und den Vektor daher nicht immer wieder kopieren müssen), aber Sie müssen sicherstellen, dass die Dinge manuell popped_back () erhalten .

Eine weitere Sache: Wenn der Graph Zyklen hat, funktioniert dies nicht. (Ich gehe davon aus, dass Sie in diesem Fall alle einfachen Pfade finden möchten.) Bevor Sie etwas in den vorherigen Vektor einfügen, überprüfen Sie zunächst, ob es bereits vorhanden ist.

Wenn Sie alle kürzesten Pfade möchten, verwenden Sie den Vorschlag von Konrad mit diesem Algorithmus.

2
v3.