如何在 Scala 中停止回溯?

Posted

技术标签:

【中文标题】如何在 Scala 中停止回溯?【英文标题】:How to stop backtracking in Scala? 【发布时间】:2012-01-19 17:54:26 【问题描述】:

假设我正在使用回溯解决一个问题(例如N-Queen)。如果我想找到唯一一个(第一个)解决方案而不是所有解决方案怎么办。

我想我可以强制(例如,使用可变布尔标志)。我想知道如何在功能上做到这一点。

【问题讨论】:

告诉函数你想要多少个解。 【参考方案1】:

虽然 Scala 的 Option 将在这里工作,但正如其他两个答案所指出的那样,更惯用的功能方法是使用“惰性列表”(或 Scala 中的 Stream)来表示解决方案集.

我发现自己在编写这样的代码,例如:

trait Node[A] 
  def children: Stream[A with Node[A]]

  def dfs(f: A => Boolean): Stream[A] = this.children.flatMap 
    child => if (f(child)) Stream(child) else child.dfs(f)
  

现在假设我有一个扩展 Node[Board]Board 类,并且它实现了 children 方法,该方法返回所有有效的板和一个附加块。假设它还有一些其他有用的东西,比如size 方法、带有empty 的伴生对象等等。

然后我可以编写以下内容以获得Stream 的解决方案:

val solutions = Board.empty.dfs(_.size == 8)

Stream 是懒惰的,只评估它的头部,所以现在我们只搜索了足够远的树来找到第一个解决方案。我们可以使用head获得这个解决方案:

scala> solutions.head
res1: Board = 
o . . . . . . .
. . . . o . . .
. . . . . . . o
. . . . . o . .
. . o . . . . .
. . . . . . o .
. o . . . . . .
. . . o . . . .

或其他。但如果我想要它们,我也可以获得其他结果:

scala> solutions(10)
res2: Board = 
. o . . . . . .
. . . . . . o .
. . . . o . . .
. . . . . . . o
o . . . . . . .
. . . o . . . .
. . . . . o . .
. . o . . . . .

这会搜索足够多的树以找到第十个解决方案,然后停止。

Stream 相对于Option 方法的最大优势在于,如果需要,我可以获得额外的结果,而无需为第一次支付更多费用。

【讨论】:

我无语了。真的很棒的解决方案。【参考方案2】:

这是一个简单的案例,深度优先搜索在找到所需内容时停止。它利用了Option,正如 Chris K 的回答中提到的那样。

case class Tree[A](v: A, subtrees: Tree[A]*) 
  def dfs(s: A): Option[A] = 
    println("visiting " + v)
    subtrees.foldLeft(if(v == s) Some(v) else None)((r, t) => 
      if(r.isDefined) 
        r 
      else 
        t.dfs(s)
    )
  
  override def toString() = "Tree(%s%s%s)".format(v, if(subtrees.nonEmpty) ", " else "", subtrees.mkString(", "))

用法:

scala> val t = Tree(1, Tree(2, Tree(3), Tree(4)), Tree(5, Tree(6), Tree(7)))
t: Tree[Int] = Tree(1, Tree(2, Tree(3), Tree(4)), Tree(5, Tree(6), Tree(7)))

t 的树看起来像

     1
   /   \
  2     5
 / \   / \
3   4 6   7    

所以我们可以搜索一个元素并跟踪它访问的节点:

scala> t.dfs(6)
visiting 1
visiting 2
visiting 3
visiting 4
visiting 5
visiting 6
res42: Option[Int] = Some(6)

请注意,在找到所需内容后,我们不会再访问任何节点。

【讨论】:

【参考方案3】:

假设您使用的是递归搜索函数,您的函数应该返回一个结果(即皇后的定位)或指示找不到该分支的结果。 Scala 可能有一个选项/也许类型,你可以使用它。该建议同样适用于任何函数式语言。

【讨论】:

是的,Scala 确实有 Option[T] 类型。您使用子类型Some(value) 表示一个值,或使用None 表示没有值。

以上是关于如何在 Scala 中停止回溯?的主要内容,如果未能解决你的问题,请参考以下文章

如何在scala play框架中停止执行或抛出错误

找到并回答后如何停止回溯算法?

如何在没有回溯的情况下退出 Python?

n皇后问题中的回溯和递归(Python)

在深度优先遍历中如何使用回溯? [复制]

如何在深度优先搜索算法中正确回溯?