Este é a continuação do artigo sobre recursão.
Neste post vamos entender o conceito de Tail Recursion, que também pode ser chamado de Recursão em Cauda, nada mas é do que uma técnica para otimizar os algoritmos recursivos. Conforme vimos no artigo anterior, a medida que a recursão cresce, o consumo de memoria Stack(Pilha) também cresce, levando um gasto excessivo para resolver o problema principal. Para evitarmos este consumo, que afeta o desempenho do algoritmo, é necessário usar o conceito de Tail Recursion.
Algoritmos Tail Recursion tem um desempenho próximo a de uma interação do tipo ‘for’, portando de comportamento assintótico de O(n).
Para usufruirmos do desempenho aqui citado, o algoritmo não deve realizar operações que dependam do resultado de métodos recursivo. Mas a frente veremos o porquê.
O compilador
A maneira como um compilador ou uma linguagem são arquitetados podem interferir na implementação do Tail Recursion, em geral linguagem funcionais não tem problemas para implementar este tipo de conceito.
O compilador precisa armazenar os endereços, assim como o estado das execuções dos métodos que precisam esperar um resultado de outro método. Essa rotina consome muito recurso, levando a um baixo desempenho para os algoritmos recursivos. Apesar disso, é possível diminuir este consumo.
O conceito de Tail Recursion envolve o compilador e também o Programador, este ultimo deve permitir que o algoritmo possa ser otimizado. Como ? veremos a seguir.
A chamada recursiva deve ser a última instrução a ser executada. O programador deve ter certeza que sua solução atenda esta frase para implementar um algoritmo Tail Recursion, o resto fica com o compilador.
O compilador deve reconhecer que salvar os endereçamentos e ou instruções serão um desperdício para os algoritmos Tail Recursion.
Se não houver instruções adicionais para executar, ou seja Tail Recursion, então não há necessidade de armazenar o ponteiro de instrução na pilha.
Algorítimos – Recursão x Recursão em Cauda x Interação
Os algoritmos a seguir implementam o algoritmo de Fibonacci em Java utilizando Recursão tradicional, Recursão em cauda e Interação. Tais algoritmos foram usados em um pequeno benchmark.
public interface Fibonacci { public long calcula(long n); }
1ª Algoritmo – Uso de interação:
public class AlgoritmoFibonnacciInterativo implements Fibonacci { public long calcula(long n){ long anterior = -1; long resultado = 1; for(long i=0; i <= n; ++i){ long soma = resultado + anterior; anterior = resultado ; resultado = soma; } return resultado; } }
2ª Algoritmo – Uso de Recursão tradicional:
public class AlgoritmoFibonnacciRecursivo implements Fibonacci{ public long calcula(long n){ if(n==0||n==1){ return n; }else{ return calcula(n-1)+calcula(n-2); } } }
3ª Algoritmo – Uso de Recursão em Cauda:
public class AlgoritmoFibonnacciTailRecursion implements Fibonacci{ public long calcula(long n){ return fibonnacci(n, 0, 1); } private long fibonnacci(long n, long primeiro, long segundo){ if( n == 0 ){ return primeiro; } return fibonnacci(n-1, segundo, primeiro+segundo); //Tailrecursion porque a chamada recursiva é a ultima instrução a ser executada. } }
Benchmark
Nos testes apenas foram contabilizados o tempo dos métodos calcula(), desconsiderando o tempo de inicialização das classes e construtores. Cada teste foi realizado individualmente em um Intel Core 2 Duo CPU, E7500 2.93Chz, 4G 64Bits – Windows 7 Ultimate
O teste foi realizado na IDE Eclipse com o complemento ‘Eclipse Test & Performance Tools Platform Project’ (TPTP)
N | Resultado | Interativo (Segundos) | Recursivo (Segundos) | Tail Recursion (Segundos) |
---|---|---|---|---|
10 | 55 | 0,000005 | 0,001312 | 0,000103 |
20 | 6765 | 0,000005 | 0,483866 | 0,000171 |
25 | 75025 | 0,000006 | 5,037891 | 0,000211 |
30 | 832040 | 0,000005 | 0,000301 | |
40 | 102334155 | 0,000005 | 0,000314 | |
100 | 3736710778780430000 | 0,000007 | 0,000715 | |
1000 | 817770325994397000 | 0,000026 | 0,007581 |
Referencias
http://blogs.msdn.com/b/chrsmith/archive/2008/08/07/understanding-tail-recursion.aspx
http://c2.com/cgi/wiki?TailRecursion
http://algol.dcc.ufla.br/~bruno/aulas/lp2/recursao.html
Estrutura de Dados e Algoritmos: Padrões de Projetos Orientados a Objetos Com Java – Livro
It’s an remarkable paragraph in favor of all the online visitors; they will get benefit from it I am sure.