93 lines
3.6 KiB
Markdown
93 lines
3.6 KiB
Markdown
# Вычисление рекурсией (медленное)
|
||
|
||
Решение по формуле, используя рекурсию:
|
||
|
||
```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`.
|