【问题标题】:Depth first search algorithm in pythonpython中的深度优先搜索算法
【发布时间】:2015-10-12 05:35:29
【问题描述】:

(这个问题在这里更详细地跟进:python-search-algorithm-from-implied-graphs

假设我有一个函数,它接受一个输入 ($x_i$),然后通过一个循环并产生一系列输出 ($x_{i, j}$)。然后每个输出可以再次成为相同函数的输入,从而产生更多输出($x_{i, j, k}$)。我试图通过这个函数找到一组步骤到一个特定的最终状态。

这是一个普遍的问题,我的问题是python中有什么好的代码结构来处理这个问题。

这里有一些元代码作为示例(尽管在实践中可能更复杂):

def f(x):
  for i in range(n):
    if some_condition:
      yield g(x,i) 
  yield false

然后对于一些 $x_0$ 和一些 $y$,我们正在寻找一个序列 $x_0,x_1,\ldots,x_k$,使得 $x_k=y$,并且 $x_{j+1}= g(x_j,i_j)$ 为 $j\in{0,\ldots,k-1}$。

要使用深度优先搜索来做到这一点,您将首先计算 $f(f(\ldots f(x) \ldots))$ 直到它产生目标结果或错误。然后后退一步并从 $f$ 产生第二个结果并重复(粗略的描述,但你明白了:基本上是深度优先搜索)。

在我看来,yield 关键字处理这个问题的效率很低。您还必须以一种允许您回溯的方式处理 $(x, f(x), f(f(x)),\ldots)$ 的堆栈(我认为这是正确的术语)你走到了死胡同。

这个一般性问题是我不时遇到的,我有点临时解决,但我想知道是否有一个很好的一般结构来解决这个问题,它可以自然而有效地处理堆栈并探索python中可能的解决方案树。

我希望这个问题足够清楚。我欢迎任何想法、cmets、澄清或答案。

【问题讨论】:

  • 是什么让你认为这个问题可以比暴力破解更有效?您是否有时会遇到以前已经遇到过的问题(在这种情况下可能会查看动态编程/记忆),还是每条路径都是独一无二的?您可能需要提供一个具体示例,并指出您现有解决方案效率低下的地方。
  • 我不希望它比蛮力更快。我只是想找到一种有效的方法来在 python 中进行蛮力

标签: python python-2.7 python-3.x depth-first-search


【解决方案1】:

我认为仅对当前路径和递归使用显式堆栈更简单:

def search(start_state, neighbors, goal):
    path = [start_state]

    class PathFound(RuntimeError):
        pass

    def rsearch(x):
        if goal(x):
            raise PathFound
        for y in neighbors(x):
            path.append(y)
            rsearch(y)
            path.pop()

    try:
        rsearch(start_state)
    except PathFound:
        return path

    return None # No path exists

Python 的递归限制较低,但对于深度优先搜索,这通常不是问题(无论如何都可以通过sys.setrecursionlimit 对其进行扩展)。

【讨论】:

  • 缩进是否正确?我以前没有见过类和函数是函数的一部分吗?
  • @DrXorile:是的。在 Python 中,您可以在函数中声明类和函数。它们只是函数的局部变量。
  • 好的,我明白了。非常感谢。现在 +1,在我测试后打勾!
  • 所以目标和邻居是任何给定状态的函数,对吧。然后你只需调用搜索来解决问题
  • @DrXorile:完全正确。异常技巧用于在(如果)我们达到目标时退出所有嵌套调用。
【解决方案2】:
class Tree:
def __init__(self, value, children = None):
    self.value = value
    self.children = []
    if children:
        self.children = list(children)

def get_children(self):
    return list(self.children)

def get_value(self):
    return self.value

def has_children(self):
    if self.children:
        return True

node9 = Tree(9)
node5 = Tree(5, [node9])
node6 = Tree(6)
node3 = Tree(3, [node5, node6])
node7 = Tree(7)
node8 = Tree(8)
node4 = Tree(4, [node7, node8])
node2 = Tree(2)
node1 = Tree(1, [node2, node4, node3])

def iterate_child(child):
    global level
    print ' ' * level * 2, child.get_value()
    if child.has_children():
        level += 1
        for s_child in child.get_children():
            iterate_child(s_child)
        level -= 1

level = 1
print node1.get_value()
for child in node1.get_children():
    iterate_child(child)

如上图所示,我已经遍历了 node1 的子节点并递归地遍历子节点的子节点,首先,然后处理父节点的第二个子节点。

【讨论】:

  • 这是否需要预先知道整棵树?
  • 不,您可以从任何节点开始,并首先从该节点开始进行深度。
猜你喜欢
  • 2017-04-13
  • 2015-12-12
  • 1970-01-01
  • 1970-01-01
  • 2011-01-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多