【问题标题】:Scala for expressions in tail recursive formScala 用于尾递归形式的表达式
【发布时间】:2017-03-19 10:11:27
【问题描述】:

我在尝试提出一种有效的方法来将 for - 表达式 N 皇后解决方案转换为尾递归形式并仍然保留使用 for 语法实现的惯用性质时遇到了一点问题。任何想法都非常受欢迎。

def place(boardSize: Int, n: Int): Solutions = n match {
  case 0 => List(Nil)
  case _ =>
    for {
      queens <- place(boardSize, n - 1)
      y <- 1 to boardSize
      queen = (n, y)
      if (isSafe(queen, queens))
    } yield queen :: queens
}

def isSafe(queen: Queen, others: List[Queen]) = {...}

【问题讨论】:

  • 我认为像这样的回溯解决方案本质上是非尾递归的。除非您自己管理堆栈,否则代码显然会变得不那么优雅。
  • @Jasper-M 我同意管理堆栈是一项义务,如果我们要在没有外部可变 var 的情况下进行尾递归。虽然它确实变得有点复杂,但也没有那么难看。

标签: scala tail-recursion for-comprehension


【解决方案1】:

您所写的内容基本上对应于所谓的深度优先搜索 (DFS)。

虽然 DFS 的递归实现很容易编写,但它不是尾递归的。这是一个尾递归的建议。请注意,我没有测试过这段代码,但它至少应该让您知道如何继续。

def solve(): List[List[Int]] = {
  @tailrec def solver(fringe: List[List[Int]], solutions: List[List[Int]]): List[List[Int]] = fringe match {
    case Nil => solutions
    case potentialSol :: fringeTail =>
      if(potentialSol.length == n) // We found a solution
        solver(fringe.tail, potentialSol.reverse :: solutions)
      else { // Keep looking
        val unused = (1 to n).toList filterNot potentialSol.contains
        val children = for(u <- unused ; partial = u :: fringe.head if isValid(partial)) yield partial
        solver(children ++ fringe.tail, solutions)
      }
  }
  solver((1 to n).toList.map(List(_)), Nil).map(_.reverse)
}

如果您担心性能,请注意此解决方案非常差,因为它在不可变数据结构上使用缓慢的操作,并且因为在 JVM 上,您最好在性能很重要的地方使用迭代.随着 n 的增加,这将很快开始失败。从算法上讲,解决 NQueens 的方法比使用 DFS 好得多。

【讨论】:

  • 事实上我很关心性能。是否有更好的方法仍然使用不可变结构。此外,您的解决方案是尾递归的,因此这意味着它实际上直接由编译器转换为迭代解决方案。
  • 由于该方法被标记为tail-rec,编译器确实应该能够对其进行优化。但是,您不会获得与使用可变集合相同的性能。例如,您可以将边缘维护为单个 mutable.Stack,这样可以节省一些操作。随着n 的增长,即使像children ++ fringe.tail 这样连接两个列表也会变得毫无用处。
  • 在仍然使用 DFS 的同时,还有一些技术可以遍历 DFS“树”,同时避免在每个节点上重新计算诸如 unused 之类的东西。相反,您维护一组可逆的使用值,这些值在回溯时会自动更新。但这又是使用非不可变数据结构。如果您对这种方法感兴趣,您应该查看约束规划,这是解决许多组合优化问题的巧妙方法。
猜你喜欢
  • 2020-12-28
  • 1970-01-01
  • 1970-01-01
  • 2018-03-17
  • 2018-03-08
  • 1970-01-01
  • 1970-01-01
  • 2018-04-24
  • 1970-01-01
相关资源
最近更新 更多