A versão ingênua de recursão de Fibonacci é exponencial por design devido à repetição no cálculo:
Na raiz, você está computando:
F (n) depende de F (n-1) e F (n-2)
F (n-1) depende de F (n-2) novamente e F (n-3)
F (n-2) depende de F (n-3) novamente e F (n-4)
em cada chamada recursiva de nível 2 que está desperdiçando muitos dados no cálculo, a função de tempo terá a seguinte aparência:
T (n) = T (n-1) + T (n-2) + C, com constante C
T (n-1) = T (n-2) + T (n-3)> T (n-2) então
T (n)> 2 * T (n-2)
...
T (n)> 2 ^ (n / 2) * T (1) = O (2 ^ (n / 2))
Este é apenas um limite inferior que, para os fins de sua análise, deve ser suficiente, mas a função em tempo real é um fator de constante pela mesma fórmula de Fibonacci e pela forma fechada é conhecida por ser exponencial da proporção áurea.
Além disso, você pode encontrar versões otimizadas do Fibonacci usando programação dinâmica como esta:
static int fib(int n)
{
/* memory */
int f[] = new int[n+1];
int i;
/* Init */
f[0] = 0;
f[1] = 1;
/* Fill */
for (i = 2; i <= n; i++)
{
f[i] = f[i-1] + f[i-2];
}
return f[n];
}
Isso é otimizado e executa apenas n etapas, mas também é exponencial.
As funções de custo são definidas de Tamanho da entrada até o número de etapas para resolver o problema. Quando você vê a versão dinâmica do Fibonacci ( n etapas para calcular a tabela) ou o algoritmo mais fácil para saber se um número é primo ( sqrt (n) para analisar os divisores válidos do número)). você pode pensar que esses algoritmos são O (n) ou O (sqrt (n)), mas isso simplesmente não é verdade pelo seguinte motivo: A entrada para o seu algoritmo é um número: n , usando a notação binária, o tamanho da entrada para um inteiro n é log2 (n) , fazendo uma alteração variável de
m = log2(n) // your real input size
vamos descobrir o número de etapas em função do tamanho da entrada
m = log2(n)
2^m = 2^log2(n) = n
então o custo do seu algoritmo em função do tamanho da entrada é:
T(m) = n steps = 2^m steps
e é por isso que o custo é exponencial.