web-dev-qa-db-fra.com

Trouver les chemins entre deux nœuds donnés?

Disons que j'ai des nœuds connectés de la manière ci-dessous, comment puis-je arriver au nombre de chemins qui existent entre des points donnés et aux détails du chemin?

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

Trouvez les chemins de 1 à 7:

Réponse: 2 chemins trouvés et ils sont

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

alt text

mise en œuvre trouvée ici est Nice, je vais utiliser le même

Voici l'extrait du lien ci-dessus en 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

Recherche en largeur d'abord parcourt un graphe et trouve en fait tous les chemins à partir d'un nœud de départ. Cependant, BFS ne conserve généralement pas tous les chemins. Au lieu de cela, il met à jour une fonction prédécesseur π pour enregistrer le chemin le plus court. Vous pouvez facilement modifier l'algorithme pour que π(n) ne stocke pas seulement un prédécesseur mais une liste de prédécesseurs possibles.

Ensuite, tous les chemins possibles sont codés dans cette fonction, et en parcourant π récursivement, vous obtenez toutes les combinaisons de chemins possibles.

Un bon pseudocode qui utilise cette notation se trouve dans Introduction to Algorithms by Cormen et al. et a ensuite été utilisé dans de nombreux scripts universitaires sur le sujet. Une recherche Google pour "dérivation du pseudocode BFS π" déracine ce succès sur Stack Exchange .

33
Konrad Rudolph

Pour ceux qui ne sont pas PYTHON expert, le même code en 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

L'algorithme de Dijkstra s'applique davantage aux chemins pondérés et il semble que l'affiche voulait trouver tous les chemins, pas seulement les plus courts.

Pour cette application, je construirais un graphique (votre application semble ne pas avoir besoin d'être dirigée) et j'utiliserais votre méthode de recherche préférée. Il semble que vous souhaitiez tous les chemins, pas seulement une estimation de la plus courte, alors utilisez un algorithme récursif simple de votre choix.

Le seul problème est que le graphique peut être cyclique.

Avec les connexions:

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

En recherchant un chemin de 1-> 4, vous pouvez avoir un cycle de 1 -> 2 -> 3 -> 1.

Dans ce cas, je garderais une pile comme traversant les nœuds. Voici une liste avec les étapes pour ce graphique et la pile résultante (désolé pour le formatage - pas d'option de tableau):

nœud actuel (nœuds suivants possibles moins d'où nous venons) [pile]

  1. 1 (2, 3) [1]
  2. 2 (3, 4) [1, 2]
  3. (1) [1, 2, 3]
  4. 1 (2, 3) [1, 2, 3, 1] // erreur - numéro en double sur la pile - cycle détecté
  5. () [1, 2, 3] // est revenu en arrière au nœud trois et a sauté 1 de la pile. Plus de nœuds à explorer d'ici
  6. 2 (4) [1, 2] // est revenu en arrière au nœud 2 et a sauté 1 de la pile.
  7. 4 () [1, 2, 4] // Nœud cible trouvé - pile d'enregistrement pour un chemin. Plus de nœuds à explorer d'ici
  8. 2 () [1, 2] // est revenu en arrière au nœud 2 et a sauté 4 de la pile. Plus de nœuds à explorer d'ici
  9. 1 (3) [1] // est revenu en arrière au nœud 1 et a sauté 2 de la pile.
  10. (2) [1, 3]
  11. 2 (1, 4) [1, 3, 2]
  12. 1 (2, 3) [1, 3, 2, 1] // erreur - numéro en double sur la pile - cycle détecté
  13. 2 (4) [1, 3, 2] // est revenu au nœud 2 et a sauté 1 de la pile
  14. 4 () [1, 3, 2, 4] Noeud cible trouvé - pile d'enregistrement pour un chemin. Plus de nœuds à explorer d'ici
  15. 2 () [1, 3, 2] // est revenu au nœud 2 et a sauté 4 de la pile. Plus de nœuds
  16. () [1, 3] // est revenu en arrière au nœud 3 et a sauté 2 de la pile. Plus de nœuds
  17. 1 () [1] // est revenu en arrière au nœud 1 et a sauté 3 de la pile. Plus de nœuds
  18. Fait avec 2 chemins enregistrés de [1, 2, 4] et [1, 3, 2, 4]
7
Marc

Le code d'origine est un peu lourd et vous pouvez utiliser le collections.deque à la place si vous souhaitez utiliser BFS pour rechercher s'il existe un chemin entre 2 points sur le graphique. Voici une solution rapide que j'ai piratée:

Remarque: cette méthode peut continuer indéfiniment s'il n'existe aucun chemin entre les deux nœuds. Je n'ai pas testé tous les cas, 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
Niranjan Tulpule

Dans Prolog (spécifiquement, 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).

tester:

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

étant donné la matrice d'adjacence:

{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}

le code Wolfram Mathematica suivant résout le problème pour trouver tous les chemins simples entre deux nœuds d'un graphe. J'ai utilisé une récursivité simple et deux var globales pour garder une trace des cycles et pour stocker la sortie souhaitée. le code n'a pas été optimisé juste pour la clarté du code. l '"impression" devrait être utile pour clarifier son fonctionnement.

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]]];
    ];
];

pour appeler le code: initNode = 1; endNode = 6; lcycle = {}; arbre = {{initNode}}; builtTree [initNode, matrice];

chemins: {{1}} racine: 1 --- nœuds: {2,3,4}

chemins: {{1,2}, {1,3}, {1,4}} racine: 2 --- nœuds: {3,4,5}

chemins: {{1,3}, {1,4}, {1,2,3}, {1,2,4}, {1,2,5}} racine: 3 --- nœuds: {2, 4}

chemins: {{1,4}, {1,2,4}, {1,2,5}, {1,3,4}, {1,2,3,4}, {1,3,2, 4}, {1,3,2,5}} racine: 4 --- nœuds: {2,3,6}

chemins: {{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}} racine: 5 --- nœuds: {6}

RÉSULTATS: {{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}}

... Malheureusement, je ne peux pas télécharger d'images pour afficher les résultats d'une meilleure façon :(

http://textanddatamining.blogspot.com

2
user1619876

Si vous voulez tous les chemins, utilisez la récursivité.

En utilisant une liste d'adjacence, de préférence, créez une fonction f() qui tente de remplir une liste actuelle des sommets visités. Comme ceci:

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);
}

En raison du fait que le vecteur est passé par valeur (et donc toutes les modifications apportées plus bas dans la procédure récursive ne sont pas permanentes), toutes les combinaisons possibles sont énumérées.

Vous pouvez gagner un peu d'efficacité en passant le vecteur précédent par référence (et donc ne pas avoir besoin de copier le vecteur encore et encore), mais vous devrez vous assurer que les choses deviennent popped_back () manuellement .

Une dernière chose: si le graphique a des cycles, cela ne fonctionnera pas. (Je suppose que dans ce cas, vous voudrez trouver tous les chemins simples, puis) ​​Avant d'ajouter quelque chose dans le vecteur précédent, vérifiez d'abord s'il est déjà là.

Si vous voulez tous les chemins les plus courts, utilisez la suggestion de Konrad avec cet algorithme.

2
v3.