斐波那契递归函数如何“起作用”?

时间:2012-01-13 02:25:36

标签: recursion fibonacci

当我来到一个描述函数递归的章节时,我是Javascript的新手并正在阅读它。它使用示例函数来查找第n个Fibonacci序列。代码如下:

function fibonacci(n) {
   if (n < 2){
     return 1;
   }else{
     return fibonacci(n-2) + fibonacci(n-1);
   }
}

console.log(fibonacci(7));
//Returns 21

我无法准确掌握这个功能正在做什么。有人能解释一下这里发生了什么吗?我被困在第5行,函数调用自己。这里发生了什么?

11 个答案:

答案 0 :(得分:86)

您根据自身定义了一个功能。一般来说,fibonnaci(n) = fibonnaci(n - 2) + fibonnaci(n - 1)。我们只是在代码中表示这种关系。因此,对于fibonnaci(7)我们可以观察到:

  • fibonacci(7)等于fibonacci(6) + fibonacci(5)
  • fibonacci(6)等于fibonacci(5) + fibonacci(4)
  • fibonacci(5)等于fibonacci(4) + fibonacci(3)
  • fibonacci(4)等于fibonacci(3) + fibonacci(2)
  • fibonacci(3)等于fibonacci(2) + fibonacci(1)
  • fibonacci(2)等于fibonacci(1) + fibonacci(0)
  • fibonacci(1)等于1
  • fibonacci(0)等于1

我们现在拥有评估fibonacci(7)所需的所有部分,这是我们最初的目标。请注意,基本情况 - return 1n < 2 - 是使这成为可能的原因。这就是停止递归的原因,这样我们就可以开始展开堆栈的过程,并总结我们在每一步返回的值。如果没有这一步,我们会继续在较小和较小的值上调用fibonacci,直到程序最终崩溃。

添加一些说明这一点的日志语句可能会有所帮助:

function fibonacci(n, c) {
    var indent = "";
    for (var i = 0; i < c; i++) {
        indent += " ";
    }
    console.log(indent + "fibonacci(" + n + ")");
    if (n < 2) {
        return 1;
    } else {
        return fibonacci(n - 2, c + 4) + fibonacci(n - 1, c + 4);
    }
}

console.log(fibonacci(7, 0));

输出:

fibonacci(7)
    fibonacci(5)
        fibonacci(3)
            fibonacci(1)
            fibonacci(2)
                fibonacci(0)
                fibonacci(1)
        fibonacci(4)
            fibonacci(2)
                fibonacci(0)
                fibonacci(1)
            fibonacci(3)
                fibonacci(1)
                fibonacci(2)
                    fibonacci(0)
                    fibonacci(1)
    fibonacci(6)
        fibonacci(4)
            fibonacci(2)
                fibonacci(0)
                fibonacci(1)
            fibonacci(3)
                fibonacci(1)
                fibonacci(2)
                    fibonacci(0)
                    fibonacci(1)
        fibonacci(5)
            fibonacci(3)
                fibonacci(1)
                fibonacci(2)
                    fibonacci(0)
                    fibonacci(1)
            fibonacci(4)
                fibonacci(2)
                    fibonacci(0)
                    fibonacci(1)
                fibonacci(3)
                    fibonacci(1)
                    fibonacci(2)
                        fibonacci(0)
                        fibonacci(1)

将相同缩进级别的值相加以产生前一级别缩进的结果。

答案 1 :(得分:27)

这里有很多好的答案,但我制作了这个图表,有助于更好地解释函数的结果。将返回的唯一值是1或0(对于n <2,您的示例返回1,但应返回n)。

这意味着每次递归调用最终都会返回0或1.最终会被缓存&#34;在堆栈中&#34;进行了#34;进入原始调用并加在一起。

因此,如果您要为&#39; n&#39;的每个值绘制相同的图表。你可以手动找到答案。

此图大致说明了如何为fib(5)返回每个函数。

![Fibonacci Javascript Tree Diagram

这显示了控制流程,即功能的执行顺序。请记住,代码始终是左 - >右,右 - &gt;执行。底部。因此,无论何时调用新函数,它都会暂停,然后进行下一次调用。

以下说明了基于原始帖子的实际控制流程。请注意,基本条件为1. fib(5) { return fib(4) + fib(3); 2. fib(4) { return fib(3) + fib(2); 3. fib(3) { return fib(2) + fib(1); 4. fib(2) { A= return 1; }; 5. fib(1) { B= return 1; }; C= return 2; // (1 + 1) }; 6. fib(2) { D= return 1; }; E= return 3; // (2 + 1) }; 7. fib(3) { return fib(2) + fib(1); 8. fib(2) { F= return 1; }; 9. fib(1) { G= return 1; }; H= return 2; // (1 + 1) }; I= return 5; // (3 + 2) }; 以简化:

def change_get_to_post(request, **kwargs):
# This is useful when we need to change a potentially large GET request
# into a POST with x-www-form-urlencoded encoding.
if request.method == 'GET' and '?' in request.url:
    request.headers['Content-Type'] = 'application/x-www-form-urlencoded'
    request.method = 'POST'
    request.url, request.data = request.url.split('?', 1)

答案 2 :(得分:20)

步骤1)调用fibonacci(7)时想象以下内容(注意我如何将所有n更改为7):

function fibonacci(7) {
    if (7 < 2){
        return 1;
    }else{
        return fibonacci(7-2) + fibonacci(7-1);
    }
}

第2步)由于(7 < 2)显然是假的,我们转到fibonacci(7-2) + fibonacci(7-1);,转换为fibonacci(5) + fibonacci(6);因为fibonacci(5)首先出现,所以被调用(将n更改为5)这一次):

function fibonacci(5) {
    if (5 < 2){
        return 1;
    }else{
        return fibonacci(5-2) + fibonacci(5-1);
    }
}

步骤3)或者课程fibonacci(6)也会被调用,所以发生的事情是每个人调用fibonacci 2个新fibonacci被调用。

可视化:

      fibonacci(7)
      ____|_____
     |          |
fibonacci(5)  fibonacci(6)
____|____     ____|_____
|        |    |         |
fib(3)  fib(4) fib(4)   fib(5)

看看它如何分支?什么时候停止?当n小于2时,这就是if (n < 2)的原因。此时,分支停止,一切都加在一起。

答案 3 :(得分:5)

希望以下有帮助。主叫:

fibonacci(3)

将到达第5行并执行:

return fibonacci(1) + fibonacci(2);

第一个表达式再次调用该函数并返回1(从n < 2开始)。

第二个再次调用该函数,到达第5行并执行:。

return fibonacci(0) + fibonacci(1);

两个表达式都返回1(因为n < 2都是),所以这个函数调用返回2.

所以答案是1 + 2,即3。

答案 4 :(得分:3)

我认为这两个函数对我的递归给出了更明确的解释(来自blog post):

function fibDriver(n) {
  return n === 0 ? 0 : fib(0, 1, n);
}

function fib(a, b, n) {
  return n === 1 ? b : fib(b, a + b, n-1);
}

答案 5 :(得分:2)

要计算第n个斐波纳契数,关系式为F(n)= F(n-2)+ F(n-1)。

如果我们在代码中实现关系,对于第n个数,我们使用相同的方法计算第(n-2)和第(n-1)个数。

每个后续数字是前两个数字的总和。因此,第七个数字是第六个和第五个数字的总和。更一般地,第n个数是n-2和n-1的总和,只要n> 1。 2.由于递归函数需要停止条件来停止递归,因此n&lt; 2是条件。

f(7)= F(6)+ F(5);

反过来,F(6)= F(5)+ F(4)

F(5)= F(4)+ F(3)...... 它一直持续到n <2

F(1)返回1

答案 6 :(得分:2)

 
   /*
* Steps Fibonacci recursion
* 1) 3 gets passed. (3 is printed to the screen during this call)
* 2) Fibonacci A gets decrements by 2 and recursion happens passing 1 as a param. (1 is printed to the screen during this call)
* 3) Fibonacci A hits the base case returning 1 and it "unwinds". (No recursion here)
* 4) Fibonacci B gets called, decrementing the previous value of n (3 was the previous value of n before A made the returning call) to 2. (2 is printed to the screen during this call)
* 5) Fibonacci A is called again subtracting 2 from n (2-2=0) and passes 0 as a param. (1 is printed to the screen during this call since it's converted from 0)
* 6) Fibonacci A hits the base case and "unwinds" (no recursion here)
* 7) Fibonacci B is called subtracting 1 from 2 (2 was the previous value of n before A made the returning call) and passes 1 as a param. (1 is printed to the screen during this call)
* 7) Fibonacci B now hits the base case, returning 1 and "unwinds" (no recursion here)
* 8) Fibonacci B retraces it's steps back through All previous fucntion calls and values of n (n=2 in our case) and adds [them] to the copy of n=1 stored in its local scope
* 9) Once Fibonacci B completes the "unwinding" process, it returns the calculated value to the original caller (no recursion here)

 Note* 
    Each instance of Fibonacci recursion creates its own scope and stores the returned value in a copy of n (in our case 1). 
    As it the function "unwinds" it executes subsequent code that receive the value of n at that time. (all functions that call other functions "unwind" back through previous calls once they return)

    In the last call of our Fibonacci example, Fibonacci B receives the value of n=2 as Fibonaccci A "unwinds" since that was the last value before it made the returning call.
    Once Fibonacci B reached the base case and "unwound", it retraced its steps back through all previous values of n (in our case just n=2) and added [them] to its local copy of n=1.

* The result when passing the number 3 is: 
                3
                 1
                 2
                  1
                  1
            (3)
*/
var div = document.getElementById('fib');

function fib( n, c ) {
  var indent = "";
  for (var i = 0; i < c; i++) {
    indent += " ";
}
  var v = n===0 ? 1 : n
  var el = document.createElement('div'),
  text = indent + "fibonacci(" + v + ")";
  el.innerHTML = text;
  div.appendChild(el);
  if(n<2){
     return 1;
  } 
  return fib(n-2, c + 4)  + fib(n-1, c + 4);

}

答案 7 :(得分:1)

该函数正在调用自身。这只是递归函数的定义。在第5行中,它通过传递将导致值的参数将执行转移到自身。

为了确保递归函数不会变成无限循环,必须存在调用自身的某种情况。问题中代码的目标是执行斐波那契序列的计算。

答案 8 :(得分:0)

基于ES6的具有递归函数的斐波那契算法

const fibonacci = ( n, k = 1, fib2 = 0, fib1 = 1 ) => {
  return k === n ? 
    (() => { return fib1; })() 
    : 
    (() => {
      k++;
      return fibonacci(n, k, fib1, fib1 + fib2);
    })();  
}
console.info(' fibonacci ' + 11 + ' = ' + fibonacci(11));

答案 9 :(得分:0)

看,fib是

  

t(n)= t(n-1)+ n;

     

如果n = 0,则为1

因此,让我们看看递归的工作原理,我只是将n中的t(n)替换为n-1,依此类推。它看起来:

  

t(n-1)= t(n-2)+ n + 1;

     

t(n-1)= t(n-3)+ n + 1 + n;

     

t(n-1)= t(n-4)+ n + 1 + n + 2 + n;

     

     

     

     

t(n)= t(n-k)+ ... +(n-k-3)+(n-k-2)+(n-k-1)+ n;

我们知道t(0)=(n-k)是否等于1,然后是n-k=0,所以n=k我们将k替换为n

  

t(n)= t(n-n)+ ... +(n-n + 3)+(n-n + 2)+(n-n + 1)+ n;

如果我们省略n-n,那么:

  

t(n)= t(0)+ ... + 3 + 2 + 1 +(n-1)+ n;

所以3+2+1+(n-1)+n是自然数。计算公式为Σ3+2+1+(n-1)+n = n(n+1)/2 => n²+n/2

fib的结果是:O(1 + n²) = O(n²)

这是理解递归关系的最佳方式

答案 10 :(得分:0)

使用递归在JS / ES6中为fib共享一个简单的代码。

   function fib(n, first = 1, second = 1) {
    if (n <= 2) return 1;
    [first, second] = [second, first + second];
    return (n - 2 === 1) ? second : fib(n - 1, first, second);
    }

console.log(fib(10));