【问题标题】:Fibonacci sequence in Ruby (recursion)Ruby 中的斐波那契数列(递归)
【发布时间】:2012-08-24 02:31:22
【问题描述】:

我正在尝试实现以下功能,但它一直给我stack level too deep (SystemStackError) 错误。

任何想法可能是什么问题?

def fibonacci( n )
    [ n ] if ( 0..1 ).include? n
    ( fibonacci( n - 1 ) + fibonacci( n - 2 ) ) if n > 1
end

puts fibonacci( 5 )

【问题讨论】:

  • 无论如何都会在您的代码中进行递归调用,因为[n] if ... 在评估一个值时不会中止方法执行。

标签: ruby recursion fibonacci


【解决方案1】:

试试这个

def fibonacci( n )
  return  n  if ( 0..1 ).include? n
  ( fibonacci( n - 1 ) + fibonacci( n - 2 ) )
end
puts fibonacci( 5 )
# => 5

也请查看此帖子Fibonacci One-Liner

还有更多……https://web.archive.org/web/20120427224512/http://en.literateprograms.org/Fibonacci_numbers_(Ruby)

你现在已经被许多解决方案轰炸了 :)

关于你的解决方案中的问题

如果是01,则应返回n

add最后两个数字不是最后一个和下一个

新修改版

def fibonacci( n )
    return  n  if n <= 1 
    fibonacci( n - 1 ) + fibonacci( n - 2 )
end 
puts fibonacci( 10 )
# => 55

一个班轮

def fibonacci(n)
   n <= 1 ? n :  fibonacci( n - 1 ) + fibonacci( n - 2 ) 
end
puts fibonacci( 10 )
# => 55

【讨论】:

  • @Maputo 你在匹配时不会返回n,所以循环运行并运行直到堆栈太深:)
  • 现在可以了,谢谢。谢谢你的澄清。起初我没有意识到 return 实际上应该结束递归。
  • 如果您在此之前根据(0..1).include? n返回,if n &gt; 1 是否多余?
  • @Toby 非常棒,我完全错过了 :)
  • @toby 感谢您的指点,这是一个很好的回归基础的练习,现在学习了更多的 ruby​​ 来重构它:)
【解决方案2】:

这不是您计算斐波那契的方式,您正在创建巨大的递归树,对于相对较小的ns,它将失败。我建议你这样做:

def fib_r(a, b, n)
  n == 0 ? a : fib_r(b, a + b, n - 1)
end

def fib(n)
  fib_r(0, 1, n)
end

p (0..100).map{ |n| fib(n) }

【讨论】:

  • 是的,感谢您指出这一点。我认为对于较大的n 可能会有问题。我在循环中实现了它,但是你的这个解决方案真的很有启发性。
【解决方案3】:

这是我想出来的,我觉得这更直接。

def fib(n)
  n.times.each_with_object([0,1]) { |num, obj| obj << obj[-2] + obj[-1] }
end
fib(10)

【讨论】:

  • 无副作用:10.times.reduce([0,1]){|memo, num|备忘录 + [备忘录[-2] + 备忘录[-1]]}
  • @TylerGillies 你的方法太慢了
  • 这里的问题是,虽然这个答案可能有效,但它不是递归的。
  • 返回值似乎有点出乎意料。如果我发送 fib(5),我要么期望在索引 5 处收到斐波那契数,要么可能是前 5 个斐波那契数,这个答案都没有给出。 fib 5 => [0, 1, 1, 2, 3, 5, 8]
【解决方案4】:

递归很慢,这里有一个更快的方法

a = []; a[0] = 1; a[1] = 1
i = 1
while i < 1000000
    a[i+1] = (a[i] + a[i-1])%1000000007
    i += 1
end

puts a[n]    

它是 O(1),但是你可以使用矩阵求幂,这是我的一个实现,但它在 java => http://pastebin.com/DgbekCJM 中,但是矩阵 exp.的 O(8logn)。这是一个更快的算法,称为快速翻倍。这是一个快速加倍的java实现。

class FD {

    static int mod = 1000000007;

    static long fastDoubling(int n) {
        if(n <= 2) return 1;
        int k = n/2;
        long a = fastDoubling(k+1);
        long b = fastDoubling(k);
        if(n%2 == 1) return (a*a + b*b)%mod;
        else return (b*(2*a - b))%mod;
}

然而,使用 karatsuba 乘法,两个矩阵 exp。并且快速加倍变得更快,但快速加倍击败矩阵 exp。由于一个恒定的因素,我不想在这里非常彻底。但我最近对斐波那契数进行了大量研究,我希望我的研究对任何愿意学习的人都有用,;)。

【讨论】:

    【解决方案5】:

    这可能会对你有所帮助。

    def fib_upto(max)
      i1, i2 = 1, 1
      while i1 <= max
        yield i1
        i1, i2 = i2, i1+i2
      end
    end
    
    fib_upto(5) {|f| print f, " "}
    

    【讨论】:

      【解决方案6】:

      最快和最小的线解决方案:

      fiby = ->(n, prev, i, count, selfy) {
        i < count ? (selfy.call n + prev, n, i + 1, count, selfy) : (puts n)
      }
      fiby.call 0, 1, 0, 1000, fiby
      

      功能性自拍模式 :)

      【讨论】:

        【解决方案7】:

        如果你想为 fib 编写最快的函数算法,它不会是递归的。这是编写解决方案的功能性方式较慢的少数几次之一。因为如果你使用类似的东西,堆栈会重复它自己

        fibonacci( n - 1 ) + fibonacci( n - 2 ) 
        

        最终 n-1 和 n-2 将创建相同的数字,因此将在计算中进行重复。最快的方法是迭代

        def fib(num)
        # first 5 in the sequence 0,1,1,2,3
        fib1 = 1 #3
        fib2 = 2 #4
        i = 5 #start at 5 or 4 depending on wheather you want to include 0 as the first number
        while i <= num
            temp = fib2
            fib2 = fib2 + fib1
            fib1 = temp
            i += 1
        end
        p fib2
        end
        fib(500)
        

        【讨论】:

          【解决方案8】:

          线性

          module Fib
            def self.compute(index)
              first, second = 0, 1
              index.times do
                first, second = second, first + second
              end
              first
            end
          end
          

          递归带缓存

          module Fib
            @@mem = {}
            def self.compute(index)
              return index if index <= 1
          
              @@mem[index] ||= compute(index-1) + compute(index-2)
            end
          end
          

          关闭

          module Fib
            def self.compute(index)
              f = fibonacci
              index.times { f.call }
              f.call
            end
          
            def self.fibonacci
              first, second = 1, 0
              Proc.new {
                first, second = second, first + second
                first
              }
            end
          end
          

          如果您致电Fib.compute(256),这些解决方案都不会导致您的系统崩溃

          【讨论】:

          • 你能解释一下递归解决方案吗?
          • 关闭解决方案的意义何在?在我看来,它是一个迭代解决方案,只是有一些奇怪的抽象。或者你想展示一些迭代器的例子?除此之外,还有更多信息,这个答案是迄今为止最好的恕我直言
          【解决方案9】:
          a = [1, 1]
          while(a.length < max) do a << a.last(2).inject(:+) end
          

          这将使用系列填充a。 (你将不得不考虑 max

          如果只需要第n个元素,你可以使用Hash.new

          fib = Hash.new {|hsh, i| hsh[i] = fib[i-2] + fib[i-1]}.update(0 => 0, 1 => 1)
          
          fib[10]
          # => 55 
          

          【讨论】:

            【解决方案10】:

            利用记忆化计算斐波那契数的另一种方法:

            $FIB_ARRAY = [0,1]
            def fib(n)
              return n if $FIB_ARRAY.include? n
              ($FIB_ARRAY[n-1] ||= fib(n-1)) + ($FIB_ARRAY[n-2] ||= fib(n-2))
            end
            

            这确保每个斐波那契数只计算一次,大大减少了对 fib 方法的调用次数。

            【讨论】:

              【解决方案11】:

              我认为这很容易:

              def fibo(n) a=0 b=1 for i in 0..n c=a+b print "#{c} " a=b b=c end
              
              end
              

              【讨论】:

              • 你需要解释你的解决方案
              • 参数将接受您想要查看的系列的长度。当您调用该方法时,将打印出总的斐波那契数列。如果输入为 5,则它将打印 0、1、1、2、3 等。
              【解决方案12】:

              这是一个更简洁的构建查找表的解决方案:

              fibonacci = Hash.new do |hash, key|
                if key <= 1
                  hash[key] = key
                else
                  hash[key] = hash[key - 1] + hash[key - 2]
                end
              end
              
              fibonacci[10]
              # => 55 
              fibonacci
              # => {1=>1, 0=>0, 2=>1, 3=>2, 4=>3, 5=>5, 6=>8, 7=>13, 8=>21, 9=>34, 10=>55}
              

              【讨论】:

                【解决方案13】:

                我们可以使用以下算法执行列表 fibo 系列

                def fibo(n)
                  n <= 2 ? 1 : fibo(n-1) + fibo(n-2)
                end
                

                我们可以像下面这样生成系列

                p (1..10).map{|x| fibo(x)}
                

                下面是这个的输出

                => [1, 1, 2, 3, 5, 8, 13, 21, 34, 55] 
                

                【讨论】:

                  【解决方案14】:

                  今天有人问我类似的问题,但他想得到一个给定数字的斐波那契数列数组。例如,

                  fibo(5)  => [0, 1, 1, 2, 3, 5]
                  fibo(8)  => [0, 1, 1, 2, 3, 5, 8]
                  fibo(13) => [0, 1, 1, 2, 3, 5, 8, 13]
                  # And so on...
                  

                  这是我的解决方案。它没有使用递归。如果您正在寻找类似的东西,只需另一种解决方案:P

                  def fibo(n)
                    seed = [0, 1]
                    n.zero? ? [0] : seed.each{|i| i + seed[-1] > n ? seed : seed.push(i + seed[-1])}
                  end
                  

                  【讨论】:

                    【解决方案15】:

                    试试这个oneliner

                    def fib (n)
                        n == 0 || n == 1 ? n : fib(n-2) + fib(n-1)
                    end
                    print fib(16)
                    

                    输出:987

                    【讨论】:

                      【解决方案16】:

                      这是 Scala 中的一个:

                      object Fib {
                        def fib(n: Int) {
                          var a = 1: Int
                          var b = 0: Int
                          var i = 0: Int
                          var f = 0: Int
                          while(i < n) {
                            println(s"f(${i+1}) -> $f")
                            f = a+b
                            a = b
                            b = f
                            i += 1
                          }
                        }
                      
                        def main(args: Array[String]) {
                          fib(10)
                        }
                      }
                      

                      【讨论】:

                        【解决方案17】:
                        PHI = 1.6180339887498959
                        TAU = 0.5004471413430931
                        
                        def fibonacci(n)
                          (PHI**n + TAU).to_i
                        end
                        

                        你不需要递归。

                        【讨论】:

                        • 这表示 2 + 3 是 4 ?
                        • 是的,浮点问题...只需将to_i 替换为round
                        • 使用to_i,它会产生2, 3, 4, 7。使用round,它会产生2, 3, 5, 7。两人都错过了8
                        • 确实没有。但是在 CS 中,您应该知道浮点问题会出现。下面 Mike Belyakov 提供的解决方案更适合。 stackoverflow.com/a/55948718/476906
                        【解决方案18】:

                        我认为this is the best answer,这是另一个 SO 帖子提出类似问题的回复。

                        PriteshJ 接受的答案在这里使用了简单的斐波那契递归,这很好,但是一旦超过第 40 个左右元素就会变得非常慢。如果您缓存/记忆以前的值并在递归迭代时传递它们,速度会快得多。

                        【讨论】:

                          【解决方案19】:

                          已经有一段时间了,但是你可以写一个相当优雅和简单的一行函数:

                          def fib(n)
                            n > 1 ? fib(n-1) + fib(n-2) : n
                          end
                          

                          【讨论】:

                          • 简单,是的,但肯定不优雅。当我拨打fib(1000) 时会发生什么?
                          • 自从我真正登录以来已经有一段时间了,但我们可以像其他答案之一那样添加缓存:cache = Hash.new def fib(n, cache) n > 1 ? cache[n] ||= fib(n-1, cache) + fib(n-2, cache) : n end fib(1000, cache) => big number 非常大的数字仍然会使堆栈级别太深(> 5000) 除非您逐步建立缓存。递归解决方案不是最有效的,使用缓存从 0 迭代到 n 会更快。
                          【解决方案20】:

                          这是我在 URI Online Judge 上解决编程挑战时使用的 sn-p,希望对您有所帮助。

                          def fib(n)
                            if n == 1
                              puts 0
                            else
                              fib = [0,1]
                              (n-2).times do
                                fib << fib[-1] + fib[-2]
                              end
                              puts fib.join(' ')
                            end
                          end
                          
                          fib(45)
                          

                          它输出

                          # => 0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765 10946 17711 28657 46368 75025 121393 196418 317811 514229 832040 1346269 2178309 3524578 5702887 9227465 14930352 24157817 39088169 63245986 102334155 165580141 267914296 433494437 701408733
                          

                          【讨论】:

                            【解决方案21】:

                            加入斐波那契火车:

                            常规:

                            def fib(num)
                              return num if (num < 2) else fib(num-1) + fib(num-2)
                            end
                            

                            使用缓存:

                            module Fib
                              @fibs = [0,1]
                              def self.calc(num)
                                return num if (num < 2) else @fibs[num] ||= self.calc(num-1) + self.calc(num-2)
                              end
                            end
                            

                            【讨论】:

                              【解决方案22】:

                              这种方法速度很快,并且利用了记忆:

                              fib = Hash.new {|hash, key| hash[key] = key < 2 ? key : hash[key-1] + hash[key-2] }
                              
                              fib[123] # => 22698374052006863956975682
                              

                              如果您想知道这个哈希初始化是如何工作的,请阅读这里:

                              https://ruby-doc.org/core/Hash.html#method-c-new

                              【讨论】:

                                【解决方案23】:

                                又一个;)

                                def fib(n)
                                  f = Math.sqrt(5)
                                  ((((1+f)/2)**n - ((1-f)/2)**n)/f).to_i
                                end
                                

                                添加一些缓存也会很方便

                                def fibonacci
                                  @fibonacci ||= Hash.new {|h,k| h[k] = fib k }
                                end
                                

                                这样我们就可以得到它了

                                fibonacci[3]  #=> 2
                                fibonacci[10] #=> 55
                                fibonacci[40] #=> 102334155
                                fibonacci     #=> {3=>2, 10=>55, 40=>102334155}
                                

                                【讨论】:

                                  【解决方案24】:

                                  1) 示例,其中最大元素

                                  def fibonachi_to(max_value)
                                    fib = [0, 1]
                                    loop do
                                      value = fib[-1] + fib[-2]
                                      break if value >= max_value
                                      fib << value
                                    end
                                    fib
                                  end
                                  
                                  puts fibonachi_to(100)
                                  

                                  输出:

                                  0
                                  1
                                  1
                                  2
                                  3
                                  5
                                  8
                                  13
                                  21
                                  34
                                  55
                                  89
                                  

                                  2) 示例,其中 10 个元素

                                  def fibonachi_of(numbers)
                                    fib = [0, 1]
                                    (2..numbers-1).each { fib << fib[-1] + fib[-2] }
                                    fib
                                  end
                                  
                                  puts fibonachi_of(10)
                                  

                                  输出:

                                  0
                                  1
                                  1
                                  2
                                  3
                                  5
                                  8
                                  13
                                  21
                                  34
                                  

                                  【讨论】:

                                    【解决方案25】:

                                    Ruby Fiber 的简短介绍 -

                                    def fibs x, y
                                      Fiber.new do
                                        while true do
                                          Fiber.yield x
                                          x, y = y, x + y
                                        end
                                      end
                                    end
                                    

                                    在上面我们创建了一个无限的fibs 数字流,以非常有效的方式计算。一个不简单的puts 无限流,所以我们必须编写一个小函数来从我们的流中收集有限数量的项目,take -

                                    def take t, n
                                      r = []
                                      while n > 0 do
                                        n -= 1
                                        r << t.resume
                                      end
                                      r
                                    end
                                    

                                    最后让我们看看序列中的第一个100数字,从01开始-

                                    puts (take (fibs 0, 1), 100)
                                    
                                    0
                                    1
                                    1
                                    2
                                    3
                                    5
                                    8
                                    13
                                    21
                                    34
                                    55
                                    .
                                    .
                                    .
                                    31940434634990099905
                                    51680708854858323072
                                    83621143489848422977
                                    135301852344706746049
                                    218922995834555169026
                                    

                                    【讨论】:

                                      【解决方案26】:

                                      这个使用记忆和递归:

                                      def fib(num, memo={})
                                        return num if num <= 1
                                      
                                        if memo[num]
                                          return memo[num]
                                        else
                                          memo[num] = fib(num - 2, memo) + fib(num - 1, memo)
                                        end
                                      end
                                      

                                      【讨论】:

                                        猜你喜欢
                                        • 2017-12-12
                                        • 2011-07-27
                                        • 2012-11-19
                                        • 2016-11-07
                                        • 2012-02-16
                                        • 1970-01-01
                                        • 2010-12-03
                                        • 1970-01-01
                                        相关资源
                                        最近更新 更多