关于 ruby??:用斐波那契数解释递归

Explanation of Recursion with Fibonacci Numbers

我找到了一个代码,它可以确定一个数字是否是斐波那契数。我希望有人能够更容易地分解它。

1
2
3
4
5
6
7
8
def is_fibonacci?(i, current = 1, before = 0)  
  return true if current == i || i == 0
  return false if current > i
  is_fibonacci?(i, current + before, current)
end

is_fibonacci?(3) # => true
is_fibonacci?(4) # => false

我知道一个方法在递归中调用自己,并且需要有一个基本情况,但是我再次很难想象正在发生的事情。任何帮助将不胜感激。


可视化它的最简单方法是一次通过计算一个方法调用,假设我们想要评估 is_fibonacci?(8),那么 ruby?? 将设置 current = 1, before = 0,因为我没有覆盖默认值。

那么,由于i不是0或者8,所以必须递归,所以会发生如下的方法调用:

1
2
3
4
5
is_fibonacci?(8, 1, 1)
is_fibonacci?(8, 2, 1)
is_fibonacci?(8, 3, 2)
is_fibonacci?(8, 5, 3)
is_fibonacci?(8, 8, 5)

最后,is_fibonacci?(8, 8, 5) 可以从 i == current (8 == 8) 终止,所以它返回 true。

编辑:考虑这种递归的另一种方法是,您的 currentbefore 参数正在"重建"斐波那契数列,如果它们命中 i,那么答案是正确的,但如果它们去过去 i,它是假的。


您可以检查调用跟踪 if is_fibonacci? 是否有两个值 10144。它显然是在计算小于或等于给定值的斐波那契数,然后在递归中进行比较。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
  is_fibonacci?(10, current = 1, before = 0)
   is_fibonacci?(10, current = 1, before = 1)
    is_fibonacci?(10, current = 2, before = 1)
     is_fibonacci?(10, current = 3, before = 2)
      is_fibonacci?(10, current = 5, before = 3)
       is_fibonacci?(10, current = 8, before = 5)
        is_fibonacci?(10, current = 13, before = 8)
  is_fibonacci?(144, current = 1, before = 0)
   is_fibonacci?(144, current = 1, before = 1)
    is_fibonacci?(144, current = 2, before = 1)
     is_fibonacci?(144, current = 3, before = 2)
      is_fibonacci?(144, current = 5, before = 3)
       is_fibonacci?(144, current = 8, before = 5)
        is_fibonacci?(144, current = 13, before = 8)
         is_fibonacci?(144, current = 21, before = 13)
          is_fibonacci?(144, current = 34, before = 21)
           is_fibonacci?(144, current = 55, before = 34)
            is_fibonacci?(144, current = 89, before = 55)
             is_fibonacci?(144, current = 144, before = 89)
10 : false ,  144 : true

这是为前 10 个斐波那契数提供此输出的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
def fib(n)
  if n <= 0 then 1
  else
    fib(n-1) + fib(n - 2)
  end
end


def is_fibonacci?(i, current = 1, before = 0, level=0)  
  puts ("" * level) +"  is_fibonacci?(#{i}, current = #{current}, before = #{before})"
  if current == i || i == 0 then
    return true
  elsif current > i then
    return false
  else
    is_fibonacci?(i, current + before, current, level+1)
  end
end

(0..10).each do |i|
  n = fib(i)
  ifib =is_fibonacci?(i)
  nfib = is_fibonacci?(n)
  puts"#{i} : #{ifib} ,  #{n} : #{nfib}"
end


基本上,它是一个接受 3 参数的函数。

i - 您正在测试的数字是否是序列的一部分。

current - 您正在测试的当前斐波那契数,默认为 1,因此您不需要提供第二个参数。

before - 序列中 current 数之前的斐波那契数,默认为 0,因此您不需要提供第三个参数。

示例值

如果 current5,则 before3

如果 current8,则 before5

该函数比较第一个参数i 的值,并查看它是否等于0current 斐波那契值。如果它等于 current0,则它是一个斐波那契数(因为 0 是序列中的第一个数)。

然后查??看current 是否大于i。如果较大,则不在序列中。这是因为如果 5current 值,则继续检查 4 是否在序列中是没有意义的。

如果这两种情况都不匹配,我们只是再次调用该函数,传入i的值,找到序列current + before中的下一个数字,before的值将是current序列中的数字。

示例:

i15current5before3。再次调用该函数时,会调用

1
is_fibonacci?(i, current + before, current)

将评估为

1
is_fibonacci?(15, 5 + 3, 5

注意

Ruby 有一个很好的语法,其中 if 可以放在你想要发生的事情之后。

1
2
if a < b
    puts"#{a} is greater than #{b}"

完全相同

1
puts"#{a} is greater than #{b}" if a < b

它只是减少了行数,读起来更像英语。


  • 首先它检查i(请求的数字)是否等于传递给current变量的斐波那契计算的数字,或者为零。如果相等则返回 true:

    1
    return true if current == i || i == 0
  • 其次,它检查 i 是否低于 current 变量。如果是,则 i 变量永远不会成为斐波那契数,因此返回 false:

    1
    return false if current > i
  • 至少我们调用来计算下一个斐波那契数,将下一个数字作为当前和先前数字的总和传递给第二个参数,并将当前数字作为下一步的第三个参数。

    1
    is_fibonacci?(i, current + before, current)
  • 请注意,最好的方法是将尾递归(如您的示例中所示)扩展为循环。