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

O Que É Recursividade?

Quer entender O Que É Recursividade? Clique aqui!

Tá, já entendeu O Que É Recursividade? Então, continue a leitura 😋


Segue um lindo diagrama para ilustrar este conceito sensacional:

flowchart TD
    A[Tabnews] --> B(O Que É Recursividade?)
    B --> C{Quer entender O Que É Recursividade? Clique aqui!}
    C -->|Sim| B[O Que É Recursividade?]
    C -->|Não| D[Então, continue a leitura!]

Analogia do Espelho

Um outro bom exemplo de recursividade é um espelho na frente de outro espelho:
Espelho na frente de espelho na orla da praia

Conceito

Recursividade na programação vem de uma função que pode chamar a si mesma.

Para criar uma função recursiva é necessário 2 critérios:

  • Condição de parada
  • Avanço recursivo

Na condição de parada é necessário uma forma de parar nossa recursividade quando atingirmos nossos objetivos, ou seja, quando nosso problema for resolvido com sucesso.

No avanço recursivo é necessário uma forma de trazer a sensação de avanço na recursividade, ou melhor dizendo, uma forma da recursividade está chegando cada vez mais próximo da solução do problema.

Mas quando a recursividade é útil de fato?

  • A recursividade é útil quando temos um problema que pode ser dividido em partes menores e cada parte pode ser resolvida usando a mesma função.
  • É como se estivéssemos resolvendo um quebra-cabeça, onde cada peça é parecida com o quebra-cabeça completo.
  • Lembre-se: Sempre existirá uma versão iterativa de um código recursivo e uma versão recursiva de um código iterativo.
  • Mas não é porque existe 2 tipos de versões de códigos que devem ser utilizadas em todos os momentos, existem cenários que um ou outro brilhará mais!
  • Logo, recursividade não é bala de prata e você precisará saber lidar quando usar uma ou outra abordagem.

O Conhecido Fatorial

Quando desejamos obter o resultado de um número fatorial, simplesmente multiplicamos os seus números anteriores até o 1.

Por exemplo, ao obter o fatorial de 5, ou 5!:

5 x 4 x 3 x 2 x 1 = 120

Mas e como resolver isso na programação? É possível fazer isso de forma iterativa! Por exemplo em código python:

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

print("O fatorial de 5 é", fatorial(5))

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).

Segue um exemplo em código python sobre o Fatorial:

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

print("O fatorial de 5 é", fatorial(5))

Códigos Famosos Recursivos e Elegantes

  • Torre de Hanói

    • A solução mais eficiente e elegante para esse problema é implementada recursivamente.
  • Busca em profundidade (Depth-First Search)

    • A implementação recursiva é comumente usada nesse algoritmo, pois é baseada em explorar profundamente um ramo antes de voltar e explorar outros ramos.
  • Quicksort

    • O Quicksort é um algoritmo de ordenação eficiente que usa a estratégia "dividir para conquistar" e a implementação recursiva é mais intuitiva e concisa para o Quicksort.
  • Árvores binárias

    • A inserção, a remoção e a busca em árvores binárias podem ser implementadas de maneira mais clara e elegante usando a recursão, devido à natureza recursiva da própria estrutura.

Conclusão

Volte ao início deste artigo e leia novamente até você entender o conceito de recursividade. Se tão somente entender o conceito recursividade, você estará livre deste loop.

Referência

Carregando publicação patrocinada...
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.

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!

2

Recursão tem os seus downsides tambem, deve ser evitado ao maximo especialmente em ambientes com pouca memoria RAM como micro controladores, porque ocupa muito espaço na stack e vcpode ter um stack overflow, tente sempre substituir por for loops, no final vc vai ter uma função mais simples e mais performática

3

Acho que foi falado na postagem original que ela não é tão boa quanto alguns acham e que deve ser evitada quando for possível e for uma situação que ela não é bem aplicada. E o probelma nem é a questão de memória

O problema dela não é ter pouca memória. Você pode ter vários terabytes e ter problema. Se não tiver otimização de recursão em cauda, e em muitos casos isso não existe, pode estourar a pilha de execução porque cada "iteração" aloca memória, o que não aconteceria em modo iterativo real. Então o tamanho da pilha é que se torna um problema, mesmo com muita memória. O padrão do WIndows costuma ser 1 megabyte para cada thread criada. Mas em algumas tecnologias a criaça~ode novas threads pode ser já menor, e quem está programando pode pudar para qualquer lado. Já vi casos de 4 gigabytes, e dá para ser maior. Mas aumentar o tamanho da stack não é a solução real e definitiva para uso errado da recursão, mesm oque a memória RAM permita.

Faz sentido para você?

Espero ter ajudado.


Farei algo que muitos pedem para aprender a programar corretamente, gratuitamente. Para saber quando, me segue nas suas plataformas preferidas. Quase não as uso, não terá infindas notificações (links aqui).

1
1

Concordo totalmente @coffeeispower! Recursão não é um mundo colorido e quem utilizar tem que ter completa ciência do que está fazendo para evitar efeitos colaterais, bugs e vários outros possíveis problemas quando não se usa de forma diligente. Muito obrigado pelo comentário!!!