Se você apenas procura acelerar seu algoritmo recursivo, a memória pode ser suficiente. Essa é a técnica de armazenar resultados de chamadas de função, para que futuras chamadas com os mesmos parâmetros possam apenas reutilizar o resultado. Isso é aplicável se (e somente se) sua função
- não tem efeitos colaterais e
- depende apenas de seus parâmetros (ou seja, não de algum estado).
Você economizará tempo se (e somente se) a função for chamada com os mesmos parâmetros repetidamente. Exemplos populares incluem a definição recursiva dos números de Fibonacci, ou seja,
f( 0 )f( 1 )f( n + 2 )= 0= 1= f( n + 1 ) + f( N ), n ≥ 0
ff( N )f( n + 1 )
Observe que, ao contrário, a memória é quase inútil para algoritmos como a classificação por mesclagem: geralmente poucas listas parciais (se houver) são idênticas e as verificações de igualdade são caras (a classificação é apenas um pouco mais cara!).
Em implementações práticas, como você armazena os resultados é de grande importância para o desempenho. Usar tabelas de hash pode ser a escolha óbvia, mas pode quebrar a localidade. Se seus parâmetros forem números inteiros não negativos, as matrizes são uma escolha natural, mas podem causar uma enorme sobrecarga de memória se você usar apenas algumas entradas. Portanto, memoisation é uma troca entre efeito e custo; se vale a pena depende do seu cenário específico.
A programação dinâmica é uma fera completamente diferente. É aplicável a problemas com a propriedade que
- pode ser particionado em subproblemas (provavelmente de mais de uma maneira),
- esses subproblemas podem ser resolvidos independentemente,
- soluções (ótimas) desses subproblemas podem ser combinadas com soluções (ótimas) do problema original e
- subproblemas têm a mesma propriedade (ou são triviais).
Isso geralmente está implícito quando as pessoas invocam o Princípio de Optimalidade de Bellman .
Agora, isso descreve apenas uma classe de problemas que podem ser expressos por um certo tipo de recursão. A avaliação daquelas é (geralmente) eficiente porque a memória pode ser aplicada com grande efeito (veja acima); geralmente, subproblemas menores ocorrem como partes de muitos problemas maiores. Exemplos populares incluem a distância de edição e o algoritmo Bellman-Ford .