//Returns the nth term in the Fibonacci Sequence (recursive): O(2^N) [really slow] unsigned long long recursive_fibonacci(unsigned short n) { if (n == 0) return 0; if (n == 1) return 1; return recursive_fibonacci(n - 1) + recursive_fibonacci(n - 2); }
int main() { unsigned short n = 45; std::cout << "Calculating fib(" << n << ") using bottom-up method: "; std::cout << bottom_up_fibonacci(n) << std::endl; std::cout << "Calculating fib(" << n << ") using recursive method: "; std::cout << recursive_fibonacci(n) << std::endl; // As you can see bottom-up it's instantanious but recursive takes some seconds to calculate fib. }
// ### Programa principal int main(void) { // Definimos uma constante que guarda o número do maior termo da // sucessão de Fibonacci que será usado nas experiências realizadas com // a função que usa o algoritmo recursivo estúpido. Sem este cuidado, o // nosso programa não terminaria em tempo útil. const int last_term_to_test_with_stupid_algorithm = 42; // Realizamos experiências com cada uma das funções, para obter // estimativas do seu tempo de execução. experiment_efficiency_of( "Stupidly recursive implementation of the fibonacci sequence:", stupidly_recursive_fibonacci, last_term_to_test_with_stupid_algorithm); experiment_efficiency_of( "Recursive implementation of the fibonacci sequence using array" " for storage:", recursive_fibonacci, MAXIMUM_TERM_FITTING_A_LONG); experiment_efficiency_of( "Recursive implementation of the fibonacci sequence using ADT" " for storage:", recursive_fibonacci_using_ADT, MAXIMUM_TERM_FITTING_A_LONG); experiment_efficiency_of( "Tail recursive implementation of the fibonacci sequence:", iterative_fibonacci, MAXIMUM_TERM_FITTING_A_LONG); experiment_efficiency_of( "Two variable iterative implementation of the fibonacci" " sequence:", iterative_fibonacci, MAXIMUM_TERM_FITTING_A_LONG); // Finalmente, usamos cada uma das funções para obter os termos da // sucessão, podendo assim mais facilmente confirmar que os cálculos // estão a ser realizados correctamente. for (int n = 0; n != MAXIMUM_TERM_FITTING_A_LONG + 1; n++) { if (n <= last_term_to_test_with_stupid_algorithm) printf("F(%d) [stupidly recursive] = %ld\n", n, stupidly_recursive_fibonacci(n)); printf("F(%d) [recursive ] = %ld\n", n, recursive_fibonacci(n)); printf("F(%d) [recursive with ADT] = %ld\n", n, recursive_fibonacci_using_ADT(n)); printf("F(%d) [tail recursive ] = %ld\n", n, tail_recursive_fibonacci(n)); printf("F(%d) [iterative ] = %ld\n", n, iterative_fibonacci(n)); putchar('\n'); } return EXIT_SUCCESS; }
// #### Definição long recursive_fibonacci(int n) { assert(n >= 0); assert(n <= MAXIMUM_TERM_FITTING_A_LONG); // Esta implementação evita os cálculos repetidos dos mesmos termos da // sucessão de Fibonacci usando uma memória auxiliar. A ideia é calcular // cada termo da sucessão uma única vez, guardar o seu valor em memória // e, das próximas vezes que o termo for solicitado, usar o valor // guardado em memória. Isto implica, naturalmente, que a memória usada // persista para além do âmbito restrito de uma execução da função. Ou // seja, não podemos de forma nenhuma usar variáveis locais // _automáticas_, pois estas são construídas quando a instrução que as // define é executada, e destruídas quando se atinge o final do bloco // onde a sua definição se encontra. Em vez de partir daqui para as // variáveis globais, cujo tempo de vida abarca todo o tempo de execução // do programa, e que são visíveis em todo o programa, preferimos usar // variáveis locais _estáticas_. Estas variáveis definem-se usando o // _qualificador_ `static`, duram desde a execução da instrução que as // define até o final do programa e têm a mesma visibilidade restrita // que qualquer variável local tem. Note-se que a instrução de definição // de qualquer variável local estática é executada uma única vez, // nomeadamente a primeira vez que o fluxo de execução do programa passa // por essa instrução. A partir daí a instrução de definição, com a // respectiva inicialização, é ignorada. Logo, a inicialização de uma // variável local estática _é feita uma única vez_ durante a execução do // programa. // // Uma vez que o número de termos da sucessão de Fibonacci calculáveis // através desta função é limitado, dado que ela devolve valores `long`, // só se podendo calcular o valor dos termos entre 0 e // `MAXIMUM_TERM_FITTING_A_LONG`, podemos reservar espaço para todos os // termos num _array_ `F` de `long`. A utilização de uma macro // `MAXIMUM_TERM_FITTING_A_LONG` na expressão para cálculo do // comprimento do _array_ permite-nos usar inicializadores do C. Dessa // forma, podemos inicializar a memória dos termos da sucessão de // Fibonacci com os dois primeiros termos dessa sucessão, que têm os // valores 0 e 1, respectivamente, correspondentes aos termos com // índices 0 e 1. static long F[MAXIMUM_TERM_FITTING_A_LONG + 1] = {0, 1}; // Não basta definir o espaço de memória dos termos da sucessão: é // necessário definir uma variável que guarde em cada instante quantos // termos estão já calculados. Neste caso inicializa-se a variável com o // valor 2, uma vez que se inicializou a memória com os dois primeiros // termos da sucessão de Fibonacci. static int number_of_calculated_terms = 2; // Estamos agora preparados para indagar o valor do termo da sucessão de // Fibonacci solicitado. Se o termo já constar na memória de termos // calculados, i.e., se o valor `n` for inferior a // `number_of_calculated_terms` limitamo-nos a devolver o valor // memorizado. if (n < number_of_calculated_terms) return F[n]; // O caso geral passa por // // 1. invocar recursivamente a própria função para obter os termos `n - // 2` e `n - 1`, // // 2. calcular o termo `n` como soma dos dois termos anteriores // calculados, // // 3. guardar esse novo termo na memória, de modo a não precisar de ser // recalculado, e // // 4. terminar a função, devolvendo o valor do novo termo ao retornar ao // ponto de invocação da função. // // Estes passos estão condensados em apenas duas linhas de código. Essa // condensação seria considerada uma má prática em quase todas as // linguagens de programação, mas este tipo de código é idiomático em C, // pelo que preferimos apresentá-lo desta forma. Seja como for, uma // forma ortodoxa de escrever este código seria: // // ```C // F[n] = recursive_fibonacci(n - 2) + recursive_fibonacci(n - 1); // number_of_calculated_terms++; // // return F[n]; // ``` // // Alternativamente, podemos considerar que o caso especial é aquele em // que o termo pretendido _não_ foi ainda calculado, o que nos levaria // ao seguinte código, bem mais legível, que substitui não apenas as // duas últimas linhas do código apresentado, mas também as duas // anteriores: // // ```C // if (number_of_calculated_terms <= n) { // F[n] = recursive_fibonacci(n - 2) + // recursive_fibonacci(n - 1); // number_of_calculated_terms++; // } // // return F[n]; // ``` number_of_calculated_terms++; return F[n] = recursive_fibonacci(n - 2) + recursive_fibonacci(n - 1); }