关于递归:避免Python的堆栈
Avoiding Python's Stack
我正在尝试多种搜索算法来解决广义 AI 问题,其中之一是深度优先搜索。我已经将广度优先搜索、贪心搜索和 A* 搜索从它们的自然递归形式转换为迭代形式,但是在使用深度优先搜索
我遇到了 CPython 的 1000 次递归调用限制,即使是一些中型问题。后继状态是延迟生成的(
从使用调用堆栈到显式堆栈的最 Pythonic 方式是什么?堆栈中应该存储多少信息?回溯时(当没有状态返回非空列表时),从堆栈前面弹出死信息的最佳方法是什么?
|
1
2 3 4 5 6 7 8 9 10 |
def dfs(initial, closed_set, goal, capacity):
if initial == goal: return [initial] for state in _generate_states(initial, capacity): |
相关讨论
- 我不知道有一种最 Pythonic 的方式,但我很好奇是否有其他人有比这更好的答案。应该存储多少信息的答案是"不超过算法工作所需的量"。
- 只是一个小小的改进——如果你使用双端队列而不是列表,你可能会得到一个稍微快一点的堆栈。
- @Omnifarious当我优化BF时也是这种情况,我存储了很多冗余路径(因为每个当前层节点都有相同的父路径)。我也很想知道处理这方面的最佳方法。
- DFS 的非递归代码看起来应该与 BFS 几乎相同,除了您将使用堆栈而不是队列。
- 我不得不承认我是多么讨厌"pythonic"这个词。
这里有一个解决方案,可以让生成器保持周围以保留所需的惰性属性:
|
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
def dfs(initial, goal, capacity):
# These three variables form the"stack". closed_set = {initial} stack = [initial] gens = [_generate_states(initial, capacity)] while stack: if state == goal: if state not in closed_set: return None |
注意,路径是定位到目标时的栈,因为栈是到达当前节点所访问的节点的记录。
相关讨论
-
您使用了未定义的
path 。您的意思是改用stack 吗?
我假设你知道如何使用堆栈迭代地实现 DFS(它与 BFS 基本相同,只是 LIFO 而不是 FIFO),所以我将发布一些一般性提示。
-
在迭代实现 DFS 时,您应该使用
collections.deque 作为堆栈,该堆栈针对快速追加和弹出元素进行了优化。 -
您绝对应该为
closed_set 使用集合而不是列表。 (如果您想找到最短路径,也可以使用地图 {state: depth}。) - 为了跟踪路径,您可以创建一个package类,封装您的当前状态和对前一个状态的引用(基本上是状态的链接列表),或者使用前驱状态的映射。
但不确定在这种情况下如何使用生成器,因此您的堆栈将容纳深度 x 个分支因子元素...或者您可以将生成器放在堆栈上,而不是实际的元素?只是一个想法...
相关讨论
-
deque 实际上只有 FIFO 需要。简单的旧列表对于 LIFO 是有效的;append() 或pop() 不需要移动任何元素。
这就是我将如何创建迭代深度优先搜索的方法。它使用
|
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
def reconstruct_path(state, parents):
path = [] while state != None: path.append(state) state = parents[state] path.reverse() return path def dfs(initial, goal): |
相关讨论
- 在编写 BFS 解决方案时,我使用了相同的字典技术。