【问题标题】:java.lang.StackOverflowError /// Pascal's trianglejava.lang.StackOverflowError /// 帕斯卡三角形
【发布时间】:2013-09-24 10:03:30
【问题描述】:

我正在尝试定义一个过程,该过程采用一列 c 和一行 r,从 0 开始计数,并返回三角形中该点的数字:

def pascal(c: Int, r: Int): Int = {
    if (c <= 0) 1
    else
        println(pascal(r, c-1) * (r-c)/c)
        pascal(r, c-1) * (r-c)/c
}

当我运行我的代码时:

>>>pascal(1,3) 

我有以下错误:

pascal: (c: Int, r: Int)Int
java.lang.StackOverflowError

我该如何解决这个问题?

谢谢。

【问题讨论】:

    标签: scala


    【解决方案1】:

    要获取帕斯卡三角形的一个元素,您只需添加上一行的 2 个元素。

    下面的代码将正确地做到这一点。

    def pascal(c: Int, r: Int): Int = {
        if (c <= 0 || c == r) 1
        else pascal(c, r-1) + pascal(c-1, r-1)
    }  
    

    【讨论】:

      【解决方案2】:

      您可以试试这个计算二项式系数的版本,它与帕斯卡三角形的条目相同:

        def pascal(col: Int, row: Int): Int = {
      
          val l = if (col > row / 2) col else row - col
          @tailrec
          def go(i: Int, acc: Int): Int = {
            if (i == l + 1) acc
            else go(i + 1, acc * (row - l + i) / i)
          }
          return go(1, 1);
        }
      

      此解决方案使用尾递归,因此 StackOverflowError 在这里不是威胁。

      【讨论】:

      • 非常好。我不希望帕斯卡三角形在没有记住最后一行的情况下有解决方案。
      • @ajozwik 你错了。可以做出一个有效的尾递归解决方案,它只构建三角形的必要部分。请参阅此处的第四个代码示例:stackoverflow.com/questions/18945141/…
      • @itsbruce 请参阅上述解决方案。您只需要记住 i 和 acc。两个数字(可以是 BigInt 或其他)。因此该解决方案不需要任何额外的内存。而且需要 O(n) 时间。
      • 我链接到的解决方案也不需要额外的内存。尾递归,初始化。它还解决了这个问题,同时只计算三角形的必要部分,而不是每一行,因此它比上述解决方案更快,用于查找单个点。
      【解决方案3】:

      我认为您不应该在您的println 中再次调用pascal 函数,您还应该使用花括号。我稍微改写了一下:

      def pascal(c: Int, r: Int): Int = {
        var p = 1
        if (c > 0) {
          p = pascal(r, c-1) * (r-c)/c 
          println(p)
        }
        p
      }
      

      这打印为pascal(1,3)

      -1
      -2
      

      更新:我很想写一个正确的解决方案,这就是我最终的结果:

        def fact(n: Int, r: Int=1): Int = if (n==0) r else fact(n-1, n*r)
      
        def pascal(c: Int, r: Int): Int = {
          fact(c) / (fact(r) * fact(c-r))
        }
      
        def main(a: Array[String]) { 
          println(pascal(3, 2))
        }
      

      唯一的问题(据我所知)是坐标必须正确,索引从 0 开始。任何超出帕斯卡三角形边界的东西都会导致除以零,这包括 OP 给出的 pascal(1,3) 示例- 第 1 行只有两个数字。

      【讨论】:

      • 而且它甚至不是帕斯卡三角形 :-) 我会重写。
      • 我不知道 :-/ 猜它在短期内修复了 StackOverflowError。希望 OP 可以不接受它。
      • 这是它做不到的一件事。在他的原始代码中,这两个调用是顺序的,而不是并行的。如果第一个完成没有溢出,那么第二个也将完成。如果第一个溢出,第二个也会做同样的事情 - 所以删除一个调用不会解决任何问题(但在非溢出情况下会节省一些时间)。
      • println 无关紧要,但“else 语句缺少花括号”是正确答案。该函数的计算结果始终为pascal(r, c-1) * (r-c)/c,因为这是最后一个表达式,因此它无限循环。而 OP 希望它评估为 if/else 表达式。
      • 是的,添加花括号会将堆栈溢出从确定变为可能。
      【解决方案4】:

      你的帕斯卡三角算法是错误的,但可能你还没写完,所以我不会告诉你如何写一个正确的算法,只是告诉你如何阻止堆栈溢出。

      编辑:maksimov 是对的,因为忘记将 else 序列括在大括号中,您只是使第一个表达式有条件,这意味着第二个表达式总是被评估并无限循环到堆栈溢出。但是,添加大括号只会将堆栈溢出的 100% 概率更改为堆栈溢出的可能性,因为对 pascal 函数的调用不在尾部位置。要消除堆栈溢出的风险,您必须进行进一步的修改,如下所述...

      递归函数可以通过使用tail recursion 来避免堆栈溢出。但是,要使其正常工作,递归调用必须在tail position 中。也就是说,递归调用必须是函数返回之前要执行的最终表达式,并且必须不加修改地返回它的值,一直返回堆栈到第一次调用。由于您的计算使用pascal(r, c-1) * (r-c)/c,因此您总是在返回修改后的结果之前修改返回的值,因此调用not 在尾部位置。您需要修改您的函数,以便当它最终返回时,它无需修改即可给出正确的结果。最终的结果应该是这个基本的形状...

      def pascal(c: Int, r: Int): Int = {
        ...
        if (c <= 0) 1 else pascal(..., ...)
      }
      

      你会发现这样做的唯一方法是在你的 pascal 函数中定义一个本地函数并让 that 进行递归......

      def pascal(c: Int, r: Int): Int = {
        def loop(...): Int = ... match {
          case ... => 1
          case ... => x + y
          case ... => loop(...)
          case _ => loop(...)
        }
        ...
        if (c <= 0) 1 else loop(...)
      }
      

      这是因为递归函数必须检查状态以确定它是否解决了最终问题或问题中的子任务。状态可以是传递给loop 的额外参数或在pascal 顶层定义的本地变量

      只有将递归安全地移动到本地函数中,才能安全地执行 maksimov 建议的操作

      var p = if (c <= 0) 1 loop(...)
      println(p)
      p
      

      您可以安全地执行此操作,因为 递归 调用都发生在 loop 内部并且都(如果您使用我的模板)在尾部位置。

      需要明确:连续两次调用您的 pascal 函数并不是导致溢出的原因。代码中的两个顶级调用是连续的,不会相互干扰;如果一个完成,另一个也将完成。如果第一个失败,第二个也会失败(删除第一个不会阻止第二个失败)。

      【讨论】:

      • 也许反对者可以解释这个答案中的错误或缺失。
      • 我没有投反对票,但我认为您的回答是错误的,因为问题与尾随电话无关。如果它有一个尾调用,它只会进入一个无限循环。
      • 你说得对,Luigi - 我错过了。添加大括号后会有风险堆栈溢出,而没有大括号总是堆栈溢出。
      【解决方案5】:

      这里是整个代码的示例:

      object Main {
      
        def main(args: Array[String]) {
        println("Pascal's Triangle")
      
       for (row <- 0 to 10) {
        for (col <- 0 to row)
          print(pascal(col, row) + " ")
      
          println()
        }
       }
      
      
      def pascal(c: Int, r: Int): Int = {
       if ( c <= 0 || c == r ){
          return 1
        }
      
        return pascal(c, r - 1) + pascal(c - 1, r -1)
      }
      
      }
      

      【讨论】:

        【解决方案6】:

        纯函数式编程

        def printPascalsTriangle(n: Int): Unit ={
        
            // n=1 print 1
            // n=2 print 1 1
            // n=3 print 1 2 1   List(prev(0),prev(0+1),prev(1))
            // n=4 print 1 3 3 1 List(prev.head,prev(0+1),prev(1+2),prev.reverse.head)
        
            def combine(oldList: List[Int], newList: List[Int]) = List(oldList.head) ::: newList ::: List(oldList.reverse.head)
        
            def g(prevList:List[Int],v:Int) = List(prevList(v-1), prevList(v))
        
            def pascalLine(prevList: List[Int], n: Int): List[Int] =
            {
              if(n>0)
              prevList match {
                case Nil => {println(List(1));pascalLine(List(1),n-1)}
                case List(1) => {println(List(1,1));pascalLine(List(1,1),n-1)}
                case _ => {
                  val newList=combine(prevList,(1 to prevList.length-1).map(x => g(prevList,x)).map(y=>y.sum).toList)
                  println(newList)
                  pascalLine(newList,n-1)
                }
        
              }
              else
                Nil
            }
            pascalLine(Nil,n)
        }
        

        【讨论】:

          猜你喜欢
          • 2012-10-24
          • 1970-01-01
          • 2014-11-12
          • 2015-01-29
          • 2011-05-07
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多