en.javascript.info/1-js/2-first-steps/19-recursion/3-fibonacci-numbers/solution.md
2015-01-21 11:37:57 +03:00

3.6 KiB
Raw Blame History

Вычисление рекурсией (медленное)

Решение по формуле, используя рекурсию:

//+ run
function fib(n) {
   return n <= 1 ? n : fib(n-1) + fib(n-2); 
}

alert( fib(3) ); // 2
alert( fib(7) ); // 13
// fib(77); // не запускаем, подвесит браузер

При больших значениях n оно будет работать очень медленно. Например, fib(77) уже будет вычисляться очень долго.

Это потому, что функция порождает обширное дерево вложенных вызовов. При этом ряд значений вычисляются много раз. Например, посмотрим на отрывок вычислений:

...
fib(5) = fib(4) + fib(3)
fib(4) = fib(3) + fib(2)
...

Здесь видно, что значение fib(3) нужно одновременно и для fib(5) и для fib(4). В коде оно будет вычислено два раза, совершенно независимо.

Можно это оптимизировать, запоминая уже вычисленные значения, получится гораздо быстрее. Альтернативный вариант -- вообще отказаться от рекурсии, а вместо этого в цикле начать с первых значений 1, 2, затем из них получить fib(3), далее fib(4), затем fib(5) и так далее, до нужного значения.

Это решение будет наиболее эффективным. Попробуйте его написать.

Алгоритм вычисления в цикле

Будем идти по формуле слева-направо:

var a = 1, b = 1; // начальные значения
var c = a + b; // 2

/* переменные на начальном шаге:
a  b  c
1, 1, 2
*/

Теперь следующий шаг, присвоим a и b текущие 2 числа и получим новое следующее в c:

a = b, b = c; 
c = a + b;

/* стало так (еще число):
   a  b  c
1, 1, 2, 3
*/

Следующий шаг даст нам еще одно число последовательности:

a = b, b = c; 
c = a + b;

/* стало так (еще число):
      a  b  c
1, 1, 2, 3, 5
*/

Повторять в цикле до тех пор, пока не получим нужное значение. Это гораздо быстрее, чем рекурсия, хотя бы потому что ни одно из чисел не вычисляется дважды.

P.S. Этот подход к вычислению называется динамическое программирование снизу-вверх.

Код для вычисления в цикле

//+ run
function fib(n) {
  var a = 1, b = 1;
  for (var i = 3; i <= n; i++) {
    var c = a + b;
    a = b;
    b = c;
  }          
  return b;
}

alert( fib(3) ); // 2
alert( fib(7) ); // 13
alert( fib(77)); // 5527939700884757

Цикл здесь начинается с i=3, так как первое и второе числа Фибоначчи заранее записаны в переменные a=1, b=1.