在深度优先搜索中跟踪和返回路径

时间:2012-10-12 17:13:35

标签: python algorithm artificial-intelligence

所以我有一个问题,我想使用深度优先搜索来解决,返回DFS找到的第一个路径。这是我的(不完整的)DFS功能:

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

startState和goalState变量只是x,y坐标的元组。问题是一个有各种方法的类。这里重要的是getSuccessors(以3项元组的列表的形式返回给定状态的子节点。虽然这部分问题只是元组的第一个元素,(child [0]),返回x,y坐标中的子状态,重要)和isGoalState(提供目标状态的x,y坐标)。

所以我认为(在这一点上很难测试),这个函数在给定其他所有内容的正确实现后,一旦达到目标状态就会返回。如果我错过了什么,请告诉我。不过,我最大的问题是什么回归。我希望它按照从开始到结束的顺序输出到达目标状态所需的所有状态的列表。它似乎不是简单地返回我的堆栈将做的技巧,因为堆栈将包括许多未访问的孩子。我访问过的列表也不会产生任何有用的东西,因为可以想象我可以达到死胡同,不得不回溯,但仍然有访问列表中的死胡同。我如何获得我想要的清单?

4 个答案:

答案 0 :(得分:35)

你是对的 - 你不能简单地返回堆栈,它确实包含很多未访问的节点。

但是,通过维护地图(字典):map:Vertex->Vertex使parentMap[v] = the vertex we used to discover v,您可以获得路径。

您需要做的修改几乎在for循环中:

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

稍后,当您找到目标时,您可以获得从源到目标的路径(伪代码):

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

请注意,订单将被颠倒,可以通过将所有元素推送到堆栈然后打印来解决。

我曾经在this thread

中回答了关于在BFS中查找实际路径的类似(虽然不完全相同的IMO)问题

另一种解决方案是使用DFS的递归版本而不是迭代+堆栈,一旦找到目标,就会在递归中打印所有current个节点 - 但是这个解决方案需要重新设计算法递归的。


P.S。请注意,如果图形包含无限分支,则DFS可能无法找到目标的路径(即使保留visited集)。
如果你想要一个完整的(总是找到一个存在的解决方案)和最优(找到最短路径)算法 - 你可能想要使用BFSIterative Deepening DFS甚至A* Algorithm如果你有一些启发式功能

答案 1 :(得分:9)

不是特定于您的问题,但您可以调整此代码并将其应用于不同的场景,事实上,您可以使堆栈也保持路径。

示例:

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

答案 2 :(得分:4)

此链接应该可以帮助你...这是一篇冗长的文章,广泛讨论了返回路径的DFS搜索...我觉得它比我或其他任何人可以发布的任何答案更好

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

答案 3 :(得分:1)

我刚刚在PHP中实现了类似的内容。

背后的基本思想如下:当存在调用堆栈时,为什么要维护另一个堆栈,执行的每个点都反映了从入口点获取的路径。当算法达到目标时,您只需返回当前的调用堆栈,这将导致读取向后的路径。这是修改后的算法。请注意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;
}