Как "работает" рекурсивная функция Фибоначчи? - PullRequest
58 голосов
/ 13 января 2012

Я новичок в Javascript и читал об этом, когда пришел к главе, в которой описана рекурсия функций. Он использовал пример функции, чтобы найти n-е число последовательности Фибоначчи. Код выглядит следующим образом:

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

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

Мне трудно понять, что именно делает эта функция. Может кто-нибудь объяснить, что здесь происходит? Я застреваю на 5-й строке, где функция вызывает себя. Что здесь происходит?

Ответы [ 11 ]

86 голосов
/ 13 января 2012

Вы определяете функцию с точки зрения самой себя.В общем, 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 1 при n < 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)

Значения с одинаковым уровнем отступа суммируются для получениярезультат для предыдущего уровня отступа.

27 голосов
/ 01 декабря 2015

Здесь есть много хороших ответов, но я сделал эту диаграмму, которая помогает лучше объяснить результат функции.Единственные значения, которые когда-либо будут возвращены, это 1 или 0 (ваш пример возвращает 1 для n <2, но вместо этого должен возвращать n). </p>

Это означает, что каждый рекурсивный вызов в конечном итоге будет возвращать либо 0, либо1. В итоге они «кэшируются» в стеке и «переносятся» в исходный вызов и складываются вместе.

Так что, если бы вы нарисовали эту же диаграмму для каждого значения 'n', вы могли бынайдите ответ вручную.

Эта диаграмма приблизительно показывает, как каждая функция возвращается для fib (5).

![Fibonacci Javascript Tree Diagram

Это показываетпоток управления, т.е. порядок выполнения функций.Помните, что код всегда выполняется слева-> справа и сверху-> снизу.Поэтому всякий раз, когда вызывается новая функция, она приостанавливается, и затем происходит следующий вызов.

Ниже приведен фактический поток управления, основанный на исходном сообщении.Обратите внимание, что базовое условие if (n <= 0) {return 0} else if (n <= 2) {return 1;} для упрощения:

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)
   };
20 голосов
/ 13 января 2012

Шаг 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).В этот момент ветвление прекращается и все складывается вместе.

5 голосов
/ 13 января 2012

Надеюсь, поможет следующее.Вызов:

fibonacci(3)

попадет в строку 5 и сделает:

return fibonacci(1) + fibonacci(2);

первое выражение снова вызывает функцию и возвращает 1 (начиная с n < 2).

Второй снова вызывает функцию, попадает в 5-ю строку и выполняет:.

return fibonacci(0) + fibonacci(1);

оба выражения возвращают 1 (так как n < 2 для обоих), поэтому этот вызов функциивозвращает 2.

Таким образом, ответ 1 + 2, что составляет 3.

3 голосов
/ 08 января 2016

Я думаю, что эти две функции дали мне более четкое объяснение рекурсии (из этого сообщения в блоге ):

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);
}
2 голосов
/ 09 апреля 2015
 
   /*
* 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);

}

2 голосов
/ 13 января 2012

Чтобы вычислить n-е число Фибоначчи, соотношение F (n) = F (n-2) + F (n-1).

Если мы реализуем отношение в коде, для n-го числа,мы вычисляем (n-2) -й и (n-1) -й номер, используя тот же метод.

Каждое последующее число является суммой двух предыдущих чисел.Таким образом, седьмое число является суммой шестого и пятого чисел.В более общем смысле, n-е число является суммой n - 2 и n - 1, если n> 2. Поскольку рекурсивным функциям необходимо условие остановки, чтобы прекратить рекурсию, здесь n <2 - это условие. </p>

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

в свою очередь, F (6) = F (5) + F (4)

F (5) = F(4) + F (3) ... продолжается до тех пор, пока n <2 </p>

F (1) не вернет 1

1 голос
/ 13 января 2012

Функция вызывает сама себя. Это просто определение рекурсивной функции. В 5-й строке он передает выполнение самому себе, передавая параметры, которые приведут к значению.

Чтобы рекурсивная функция не превратилась в бесконечный цикл, должно быть какое-то условие, которое не вызывает . Цель вашего кода в вопросе - выполнить вычисления последовательности Фибоначчи.

0 голосов
/ 30 марта 2019

Совместное использование более простого кода для fib в JS / ES6 с использованием рекурсии.

   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));
0 голосов
/ 13 декабря 2018

смотри, фиб

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²)

Это лучший способ понять рекурсивное отношение

...