Для начала помните, что нотация big-O всегда обеспечивает верхнюю границу.То есть, если функция является O (n), это также O (n 2 ), O (n 3 ), O (2 n ),и т.д. Таким образом, в этом смысле вы не ошибаетесь, если говорите, что время выполнения равно O (2 n ).У вас просто нет жесткой границы.
Чтобы увидеть, откуда исходит жесткая граница Θ (φ n ), это может помочь выяснить, сколько рекурсивных вызовов в конечном итоге получаютсделано при оценке Fibonacci(n)
.Обратите внимание, что для
Fibonacci(0)
требуется один вызов, для вызова Fibonacci(0)
. Fibonacci(1)
требуется один вызов, для вызова Fibonacci(1)
. Fibonacci(3)
требует пять вызовов: один на Fibonacci(3)
, затем три сгенерированных вызовапутем вызова Fibonacci(2)
и одного вызова, генерируемого путем вызова Fibonacci(1)
. Fibonacci(4)
, требуется девять вызовов: один на Fibonacci(4)
, затем пять от вызова на Fibonacci(3)
и три от вызова наFibonacci(2)
.
В более общем случае картина выглядит так: если вы звоните Fibonacci(n)
для некоторого n ≥ 2, то количество сделанных вызовов равно одному (для самого вызова)плюс количество звонков, необходимое для оценки Fibonacci(n-1)
и Fibonacci(n-2)
.Если мы допустим, что L n обозначает количество совершенных вызовов, это означает, что у нас есть
- L 0 = L 1 = 1
- L n + 2 = 1 + L n + L n + 1 .
Итак, теперь вопрос в том, насколько быстро эта последовательность растет.Оценка первых нескольких терминов дает нам
1, 1, 3, 5, 9, 15, 25, 41, ...
, который определенно становится все больше и больше, но не ясно, насколько оно больше.
Здесь вы можете заметить, что L n своего рода сортировка выглядит как числа Фибоначчи.То есть он определен в терминах суммы двух предыдущих терминов, но у него есть дополнительный член +1.Поэтому, возможно, нам стоит взглянуть на разницу между L n и F n , поскольку это может показать нам, насколько «быстрее» растет серия L.Вы можете заметить, что первые два значения ряда L равны 1, 1, а первые два значения ряда Фибоначчи равны 0, 1, поэтому мы будем сдвигать все на один член, чтобы все выстроилось в ряд:
L(n) 1 1 3 5 9 15 25 41
F(n+1) 1 1 2 3 5 8 13 21
Diff: 0 0 1 2 4 7 12 20
И подождите, подождите секунду.Что произойдет, если мы добавим один к каждому члену разницы?Это дает нам
L(n) 1 1 3 5 9 15 25 41
F(n+1) 1 1 2 3 5 8 13 21
Diff+1 1 1 2 3 5 8 13 21
Вау!Выглядит как L n - F n + 1 + 1 = F n + 1 .Переставляя, мы видим, что
L n = 2F n + 1 - 1.
Ух ты!Таким образом, фактическое количество вызовов, сделанных рекурсивной функцией Фибоначчи, очень тесно связано с возвращаемым фактическим значением.Таким образом, мы могли бы сказать, что время выполнения функции Фибоначчи Θ (F n + 1 ), и мы были бы правы.
Но теперь вопрос в том, откуда приходит φ.Прекрасный математический результат называется Формула Бине , который говорит, что F n = Θ (φ n ).Есть много способов доказать это, но все они по сути сводятся к наблюдению, что
- числа Фибоначчи, кажется, растут экспоненциально быстро;
- , если они растут экспоненциально с базойx, тогда F n + 2 = F n + F n + 1 можно переписать как x 2 = x + 1;и
- φ является решением x 2 = x + 1.
Из этого мы можем видеть, что поскольку время выполнения Фибоначчи равно Θ (F n + 1 ), тогда время выполнения также равно Θ (φ n + 1 ) = Θ (φ n ).