Executando verificação de segurança...
3

mas nem sempre existirá uma versão recursiva de um código iterativo.

Na verdade, qualquer algoritmo iterativo pode ser convertido para um recursivo e vice-versa. A questão é que geralmente uma das versões é mais adequada para determinado contexto, e isso depende de vários fatores (muitos citados no texto).

Mas concordo que só porque pode, não quer dizer que você deve fazê-lo. Tem casos em que recursão é melhor, tem casos que é pior, então o importante é sempre entender bem os prós e contras e escolher conforme a situação.


Mas também é possível realizar essa mesma tarefa de forma recursiva que torna o código até um pouco mais claro (e performático em alguns casos).

Para a versão recursiva ser mais performática, depende de vários fatores, como por exemplo a implementação da linguagem. No caso do Python, não tem como, já que a implementação atual não otimiza recursão em cauda. Fazendo um teste rápido:

from timeit import timeit

def fatorial_iter(n):
    resultado = 1
    for i in range(1, n + 1):
        resultado = resultado * i
    return resultado

def fatorial_rec(n):
    if n == 0:
        return 1
    else:
        return n * fatorial_rec(n - 1)

# executa 100 mil vezes cada teste
params = { 'number' : 100000, 'globals': globals() }

n = 100

# imprime os tempos em segundos
print(timeit('fatorial_iter(n)', **params))
print(timeit('fatorial_rec(n)', **params))

Os tempos exatos podem variar de acordo com o hardware, mas enfim, na minha máquina foi (tempos em segundos - quanto menor, mais rápido):

0.7660143249995599
1.53468600800079

Ou seja, a versão recursiva foi 2 vezes mais lenta.

Claro que para poucas execuções com números pequenos, a diferença será imperceptível. Mas se tiver que fazer vários cálculos com números grandes, começa a fazer diferença. Ainda daria para fazer otimizações, como usar o lru_cache para cachear os resultados, mas ainda tem outro problema: a versão recursiva pode estourar a pilha de execução. Basta testar as funções que criei acima com um valor mais alto:

def fatorial_iter(n):
    resultado = 1
    for i in range(1, n + 1):
        resultado = resultado * i
    return resultado

def fatorial_rec(n):
    if n == 0:
        return 1
    else:
        return n * fatorial_rec(n - 1)

n = 1000
# iterativo: ok
print(fatorial_iter(n))
# recursivo: erro (RecursionError)
print(fatorial_rec(n))

A versão iterativa roda sem problemas, já a recursiva dá erro: RecursionError: maximum recursion depth exceeded in comparison. Isso porque cada chamada recursiva fica empilhada, até que se chegue no caso base (quando n é zero) e elas comecem a retornar. E como há um limite do tamanho da pilha, valores grandes podem estourá-la. Com iteração não ocorre esse problema porque é apenas uma chamada de função.

Carregando publicação patrocinada...
1

Sensacional sua complementação @kht! Muuuito obrigado pelo apontamento:

Na verdade, qualquer algoritmo iterativo pode ser convertido para um recursivo e vice-versa. A questão é que geralmente uma das versões é mais adequada para determinado contexto, e isso depende de vários fatores (muitos citados no texto).

Irei refletir este apontamento no artigo!