Skip to content


Analisando Número de Fibonacci e Recursividade

O número de Fibonacci é bem conhecido no mundo da computação. Ele é representado por uma função recursiva:
Fibonacci e Proporção áurea

Calculando

Tendo isso em mente é bem fácil criar um algoritmo que calcule o número de Fibonacci, certo?
Na internet normalmente encontramos um algoritmo usando um método de recursão simples. Algo parecido com:

Felipe: Esse artigo usa apenas exemplos em C++

fibo1.cpp

#include <iostream>
#include <cstdlib>
 
int F(int n)
{
    if (n == 0) return 0;
    if (n == 1) return 1;
    return F(n - 1) + F(n - 2);
}
 
int main(int argc, char *argv[])
{
    int n = atoi(argv[1]);
    std::cout << F(n) << std::endl;
    return 0;
}

código no pastebin

Compilando:

g++ -o fibo1 fibo1.cpp

Rodando:

./fibo1 8

Deu 21, certo? Isso mesmo.

Analisando a Função

Mas digo para você: Não use esse programa! É totalmente ineficiente. O número de chamadas para calcular F_{N} é exatamente F_{N+1}. A função calcula recursivamente o mesmo número quantas vezes a chamar. Como podemos ver, a taxa de crescimento do número de Fibonacci tende a Proporção áurea:
Fibonacci e Proporção áurea

Lembrando que a razão áurea é definida por:
Razão áurea

Sim, fibo1.cpp é uma função exponencial com Big Theta Notation, em função do tempo, de:
Big Theta Notation 1

O tempo de execução para computar F_{N+1} é \phi \approx 1,6 vezes mais demorado que para calcular F_{N}. Por exemplo, já que \phi^{9} > 60 e percebemos que nosso computador leva um segundo para calcular F_{N}, então levará mais de um minuto para calcular F_{N+9} e mais de uma hora para calcular F_{N+18}.

Podemos verificar isso calculando F_{36} e F_{45}

pet@felipe-opensuse:~/Projetos/C++/algoritmos> time ./fibo1 36
14930352

real 0m1.077s
user 0m1.040s
sys 0m0.008s
pet@felipe-opensuse:~/Projetos/C++/algoritmos> time ./fibo1 45
1134903170

real 0m54.927s
user 0m54.547s
sys 0m0.092s

Em contraste, usando o método de programação dinâmica temos um tempo proporcional a N.

Programação dinâmica

Esse método consiste em guardar os valores dos números já calculados em um array. Fazendo isso, apenas checamos se o array já foi memorizado, e então retornamos o número, caso contrário calculamos recursivamente e assim por diante.
Lembrando que para um int de 32 bits, F_{46} = 1836311903 será o maior numero a ser calculado.

fibo2.cpp

#include <iostream>
#include <cstdlib>
 
namespace Fibonacci {
    int *memory;
    int F(int n)
    {
        // int inicializa como 0
        if (memory[n] != 0) return memory[n];
        int aux = n;
        if (n < 0) return 0;
        if (n > 1) aux = F(n-1) + F(n-2);
        return memory[n] = aux;
    }
}
 
int main(int argc, char *argv[])
{
    int n = atoi(argv[1]);
    Fibonacci::memory = new int[n+1];
    std::cout << Fibonacci::F(n) << std::endl;
    delete [] Fibonacci::memory;
    return 0;
}

código no pastebin

Compilando:

g++ -o fibo2 fibo2.cpp

Rodando:

./fibo2 8

Deu 21, de novo? =)

Aplicamos aqui uma técnica chamada bottom-up dynamic programming. Se aplica em uma função recursiva que podemos salvar tempo por armazenar valores anteriores que serão usados depois. Essa técnica tem sido usada para uma gama grande de algoritmos e aplicações. E essa simples mudança fez com que nosso algoritmo passasse de exponencial para linear.

Agora temos uma função, em relação ao tempo, proporcional ao N.
Big Theta Notation 2

Comparando

Fazendo vários testes, podemos chegar em uma tabela de resultados, em função do tempo:

arquivo F_{8} F_{36} F_{45} F_{46}
fibo1.cpp 0m0.009s 0m1.156s 0m54.927s 1m29.403s
fibo2.cpp 0m0.007s 0m0.007s 0m0.006s 0m0.007s

fibo2.cpp é constante? Sim! Para N relativamente pequeno fibo2.cpp calcula em tempo constante.

Será interessante ver até quanto esse algoritmo agüenta(ta errado né? hehe ¬¬) fazer em tempo constante. Alguém se habilita?

Posted in Algoritmos, Artigos, C++, Matemática.

Tagged with , , .


11 Responses

Stay in touch with the conversation, subscribe to the RSS feed for comments on this post.

  1. Elton Minetto says

    Muito legal. Vou indicar para os alunos das disciplinas que eu dou aulas. Parabéns

  2. Tiago "PacMan" Peczenyj says

    A versão AWK dessa função seria assim
    function F(n) {
    if (!m[n]) m[n] = (n > 1)? F(n-1) + F(n-2) : n
    return m[n]
    }

    $ time awk -v n=45 -f script.awk
    fatorial de n = 45 : 1134903170

    real 0m0.002s
    user 0m0.000s
    sys 0m0.000s

    Detalhe que para a versão duplamente recursiva o awk, para calcular F(36), levou quase 22 segundos e para 45 ele ficou 3 minutos processando sem informar a resposta.

  3. Tiago "PacMan" Peczenyj says

    Ops, escrevi errado, é fibonacci e não fatorial, :$

  4. Felipe Tonello says

    @Elton Minetto, Valeu minetto!

    @Thiago, Legal sua implementação em awk. Pelos resultados podemos ver também que C++ tem performasse muito maior que em awk(usando recursividade simples). F_{36} foi cerca de 60 vezes mais rápido.
    Mas usando a técnica de programação dinâmica o tempo é o mesmo(0.002 para 0.007 podemos dizer que é desconsiderável).

  5. Walter Cruz says

    Bacana! Um amigo meu fez uns testes em Python, acho que é legal vc dar uma olhada ;)

    http://montegasppa.blogspot.com/2006/07/desempenho-de-algoritmos.html

  6. Tiago "PacMan" Peczenyj says

    Felipe,

    Comparando com a forma tradicional, por exemplo, eu gasto 2189 iterações para achar fib(20), 39 iterações usando programação dinamica e apenas 22 usando tail-recursion.

    function G(N) { total++; return G_tr(N,0,1); }
    function G_tr(I,R,N){
    total++;
    return (I==0)? R : G_tr(I-1,N,R+N)
    }
    onde total acumula as chamadas as funções.

    Eu me inspirei na versão Erlang do algoritmo
    http://en.literateprograms.org/Fibonacci_numbers_(Erlang)

  7. Chris Benseler says

    Fibonacci me lembra a faculdade de engenharia.
    Boas lembranças, acho!

    E bacana teu blog!
    []s!

  8. erica says

    olá,
    iniciei ontem aulas de algoritmo (sou biologa, imagine o desespero). Tenho uma atividade para escrever um algoritmo que expresse a razão de uma sequencia for o numero da termo da sequencia de fibonacci essa sequencia tem q ser proximal a 10…

    Não entendi nada que :(

    Por favor ajude
    Att,
    Erica

  9. Enio says

    Bem legal o seu blog.

    Parabéns.

Continuing the Discussion

  1. Fibonacci: alguns algoritmos efetivos em AWK | Blog do PacMan linked to this post on January 30, 2009

    [...] em AWK Easy AdSenser by UnrealEstava apreciando hoje de manhã um post do Felipe Tonello: Analisando Número de Fibonacci e Recursividade. É um bom artigo sobre aquelas coisas que alguns podem ter visto na faculdade e são sempre uteis: [...]

  2. Fibonacci: alguns algoritmos efetivos em AWK | Planeta Globo.com linked to this post on April 16, 2009

    [...] apreciando hoje de manhã um post do Felipe Tonello: Analisando Número de Fibonacci e Recursividade. É um bom artigo sobre aquelas coisas que alguns podem ter visto na faculdade e são sempre uteis: [...]



Some HTML is OK

or, reply to this post via trackback.