4

我正在尝试多种搜索算法来解决广义 AI 问题,其中之一是深度优先搜索。我已经将广度优先搜索、贪心搜索和 A* 搜索从它们的自然递归形式转换为迭代形式,但是在使用深度优先搜索时遇到了更多麻烦cleanly(尽管这并不超出我的能力,我我不确定这样做的最pythonic方式,因此问题)。

即使是一些中型问题,我也遇到了 CPython 的 1000 次递归调用限制。后继状态是惰性生成的(_generate_states是生成器,不是列表),并且需要从初始状态开始的路径。

从使用调用堆栈到显式堆栈的最 Pythonic 方式是什么?堆栈中应该存储多少信息?回溯时(当没有状态返回非空列表时),从堆栈前面弹出死信息的最佳方法是什么?

def dfs(initial, closed_set, goal, capacity):
    if initial == goal:
        return [initial]

    for state in _generate_states(initial, capacity):
        if state not in closed_set:
            result = dfs(state, [initial] + closed_set, goal, capacity)
            if result:
                return [state] + result
    return []
4

3 回答 3

4

这是一个让生成器保持在周围以保留所需惰性属性的解决方案:

def dfs(initial, goal, capacity):
    # These three variables form the "stack".
    closed_set = {initial}
    stack = [initial]
    gens = [_generate_states(initial, capacity)]

    while stack:
        cur = stack[-1]
        gen = gens[-1]
        try:
            state = next(gen)
        except StopIteration:
            # This node is done
            closed_set.discard(cur)
            gens.pop()
            stack.pop()
            continue

        if state == goal:
            return stack

        if state not in closed_set:
            closed_set.add(state)
            stack.append(state)
            gens.append(_generate_states(state, capacity))

    return None

Note that the path is the stack when the target is located, because the stack is a record of the nodes visited to get to the current node.

于 2012-10-02T16:20:43.497 回答
3

我假设您知道如何使用堆栈迭代地实现 DFS(它与 BFS 基本相同,只是 LIFO 而不是 FIFO),所以我将仅发布一些一般提示。

  • 在迭代实现 DFS 时,您应该使用collections.deque堆栈,它针对快速追加和弹出元素进行了优化。
  • 您绝对应该使用集合closed_set而不是列表。(如果您想找到最短路径,也可以使用地图 {state: depth}。)
  • 为了跟踪路径,您可以创建一个包装类,封装您的当前状态和对前一个状态的引用(基本上是状态的链接列表),或者使用前驱状态的映射。

但是,不确定在这种情况下如何使用生成器,因此您的堆栈将容纳深度 x 个分支因子元素......或者您可以将生成器放在堆栈上,而不是实际的元素?只是一个想法...

于 2012-10-02T15:50:55.700 回答
1

以下是我将如何创建迭代深度优先搜索。它candidate_states用作接下来应该探索的状态堆栈。parents您可以使用字典重建从任何访问节点到初始节点的路径。

def reconstruct_path(state, parents):
    path = []
    while state != None:
        path.append(state)
        state = parents[state]
    path.reverse()
    return path

def dfs(initial, goal):
    visited_states = set()
    candidate_states = [initial]
    parents = {initial: None}
    while len(candidate_states) > 0:
        cur_state = candidate_states.pop()
        if cur_state in visited_states: continue
        if cur_state == goal:
            return reconstruct_path(cur_state, parents)
        for state in _generate_states(cur_state):
            parents[state] = cur_state
            candidate_states.append(state)
    return None
于 2012-10-02T15:49:27.227 回答