web-dev-qa-db-fra.com

Traçage et retour d'un chemin en profondeur Première recherche

J'ai donc un problème que je souhaite utiliser pour résoudre la première recherche en profondeur, en renvoyant le premier chemin trouvé par DFS. Voici ma fonction DFS (incomplète):

    start = problem.getStartState()
    stack = Stack()
    visited = []
    stack.Push(start)
    if problem.isGoalState(problem.getStartState):
        return something
    while stack:
        parent = stack.pop()
        if parent in visited: continue
        if problem.isGoalState(parent):
            return something
        visited.append(parent)
        children = problem.getSuccessors(parent)
        for child in children:
            stack.Push(child[0])

Les variables startState et goalState sont simplement un tuple de coordonnées x, y. problème est une classe avec une variété de méthodes. Les plus importants ici sont getSuccessors (qui renvoie les enfants d’un état donné sous la forme d’une liste de 3 tuples d’éléments. Pour cette partie du problème, seul le premier élément du Tuple, (enfant [0]), renvoie l'état de l'enfant aux coordonnées x, y, est important) et isGoalState (qui fournit les coordonnées x, y de l'état de l'objectif). 

Donc, je pense (difficile à tester à ce stade), que cette fonction, étant donnée la mise en œuvre appropriée de tout le reste, reviendra une fois qu'elle aura atteint un état objectif. S'il vous plaît laissez-moi savoir si je manque quelque chose. Mon plus gros problème, cependant, est de savoir QUOI revenir. Je veux qu'il produise une liste de tous les états nécessaires pour atteindre l'état de l'objectif, dans l'ordre, du début à la fin. Il ne semble pas que simplement retourner ma pile fera l'affaire, car la pile comprendra de nombreux enfants non visités. Ma liste de visites ne donnera rien d’utile non plus, puisqu’il est concevable que je puisse atteindre des impasses, avoir à revenir en arrière, mais que les tuples restants figurent dans la liste des visites. Comment pourrais-je obtenir la liste que je désire? 

27
user1427661

Vous avez raison - vous ne pouvez pas simplement renvoyer la pile, elle contient en effet de nombreux nœuds non visités.

Cependant, en maintenant une carte (dictionnaire): map:Vertex->Vertex tel que parentMap[v] = the vertex we used to discover v, vous pouvez obtenir votre chemin.

La modification que vous devrez faire est à peu près dans la boucle:

    for child in children:
        stack.Push(child[0])
        parentMap[child] = parent #this line was added

Plus tard, lorsque vous aurez trouvé votre cible, vous pourrez obtenir le chemin de la source à la cible (pseudo-code):

curr = target
while (curr != None):
  print curr
  curr = parentMap[curr]

Notez que l'ordre sera inversé, il peut être résolu en plaçant tous les éléments sur une pile, puis en les imprimant.

Une fois, j'ai répondu à une question similaire (mais pas identique à l'OMI) concernant la recherche du chemin d'accès réel dans BFS dans ce fil de discussion

Une autre solution consiste à utiliser une version récursive de DFS plutôt que itérative + pile et, une fois la cible trouvée, affiche tous les noeuds current de la sauvegarde de la récursion - mais cette solution nécessite une refonte de l'algorithme en un modèle récursif.


P.S. Notez que DFS peut échouer dans la recherche d'un chemin d'accès à la cible (même si vous maintenez un ensemble visited) si le graphique contient une branche infinie .
Si vous voulez un algorithme complet (trouve toujours une solution, s'il en existe) et optimal (trouve le chemin le plus court), vous pouvez utiliser BFS ou Iterative Deepening DFS ou même A * Algorithme si vous avez une fonction heuristique

34
amit

Non spécifique à votre problème, mais vous pouvez modifier ce code et l'appliquer à différents scénarios. En fait, vous pouvez également que la pile contienne le chemin.

Exemple:

     A
   /    \
  C      B
  \     / \
   \    D E
    \    /
       F


graph = {'A': set(['B', 'C']),
         'B': set(['A', 'D', 'E']),
         'C': set(['A', 'F']),
         'D': set(['B']),
         'E': set(['B', 'F']),
         'F': set(['C', 'E'])}




def dfs_paths(graph, start, goal):
    stack = [(start, [start])]
    visited = set()
    while stack:
        (vertex, path) = stack.pop()
        if vertex not in visited:
            if vertex == goal:
                return path
            visited.add(vertex)
            for neighbor in graph[vertex]:
                stack.append((neighbor, path + [neighbor]))

print (dfs_paths(graph, 'A', 'F'))   #['A', 'B', 'E', 'F']
9
XueYu

ce lien devrait vous aider beaucoup ... C'est un long article qui parle longuement d'une recherche DFS qui renvoie un chemin ...

http://www.python.org/doc/essays/graphs/

4
Joran Beasley

Je viens d'implémenter quelque chose de similaire dans PHP.

L'idée de base derrière est la suivante: Pourquoi devrais-je conserver une autre pile, lorsqu'il existe la call stack , qui, à chaque point de l'exécution, reflète le chemin emprunté à partir du point d'entrée. Lorsque l'algorithme atteint l'objectif, vous devez simplement revenir sur la pile d'appels en cours, ce qui entraîne la lecture du chemin emprunté. Voici l'algorithme modifié. Notez les sections return immediately.

/**
 * Depth-first path
 * 
 * @param Node $node        Currently evaluated node of the graph
 * @param Node $goal        The node we want to find
 *
 * @return The path as an array of Nodes, or false if there was no mach.
 */
function depthFirstPath (Node $node, Node $goal)
{
    // mark node as visited
    $node->visited = true;

    // If the goal is found, return immediately
    if ($node == $goal) {
        return array($node);
    }

    foreach ($node->outgoing as $Edge) {

        // We inspect the neighbours which are not yet visited
        if (!$Edge->outgoing->visited) {

            $result = $this->depthFirstPath($Edge->outgoing, $goal);

            // If the goal is found, return immediately
            if ($result) {
                // Insert the current node to the beginning of the result set
                array_unshift($result, $node);
                return $result;
            }
        }
    }

    return false;
}
1
user2555221