en.javascript.info/1-js/8-more-functions/1-recursion/3-fibonacci-numbers/solution.md
Ilya Kantor d4c714cbe1 work
2016-08-05 16:53:08 +03:00

93 lines
3.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Вычисление рекурсией (медленное)
Решение по формуле, используя рекурсию:
```js 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)` уже будет вычисляться очень долго.
Это потому, что функция порождает обширное дерево вложенных вызовов. При этом ряд значений вычисляется много раз. Например, посмотрим на отрывок вычислений:
```js no-beautify
...
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)` и так далее, до нужного значения.
Это решение будет наиболее эффективным. Попробуйте его написать.
# Алгоритм вычисления в цикле
Будем идти по формуле слева-направо:
```js no-beautify
var a = 1, b = 1; // начальные значения
var c = a + b; // 2
/* переменные на начальном шаге:
a b c
1, 1, 2
*/
```
Теперь следующий шаг, присвоим `a` и `b` текущие 2 числа и получим новое следующее в `c`:
```js no-beautify
a = b, b = c;
c = a + b;
/* стало так (ещё число):
a b c
1, 1, 2, 3
*/
```
Следующий шаг даст нам ещё одно число последовательности:
```js no-beautify
a = b, b = c;
c = a + b;
/* стало так (ещё число):
a b c
1, 1, 2, 3, 5
*/
```
Повторять в цикле до тех пор, пока не получим нужное значение. Это гораздо быстрее, чем рекурсия, хотя бы потому что ни одно из чисел не вычисляется дважды.
P.S. Этот подход к вычислению называется [динамическое программирование снизу-вверх](http://ru.wikipedia.org/wiki/%D0%94%D0%B8%D0%BD%D0%B0%D0%BC%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B5_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5).
# Код для вычисления в цикле
```js 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`.