Existe algo que possa ser feito com recursão que não possa ser feito com loops?


126

Há momentos em que usar recursão é melhor do que usar um loop, e momentos em que usar um loop é melhor do que usar recursão. Escolher a opção "certa" pode economizar recursos e / ou resultar em menos linhas de código.

Existem casos em que uma tarefa só pode ser executada usando recursão, em vez de um loop?


13
Eu duvido seriamente. A recursão é um loop glorificado.
Lightness Races in Orbit

6
Vendo as direções divergentes para as quais as respostas vão (e tendo falhado em fornecer uma melhor), você pode fazer alguém tentando responder a um favor se fornecer um pouco mais de experiência e que tipo de resposta você procura. Deseja uma prova teórica para máquinas hipotéticas (com armazenamento e tempo de execução ilimitados)? Ou exemplos práticos? (Onde “seria ridiculamente complicado” pode se qualificar como “não pode ser feito”.) Ou algo diferente?
5gon12eder

8
@LightnessRacesinOrbit Para o meu falante não nativo de inglês, "Recursão é um loop glorificado" soa como "Você pode usar uma construção de loop em vez de uma chamada recursiva em qualquer lugar, e o conceito realmente não merece seu próprio nome" . Talvez eu interprete o idioma "algo glorificado" errado, então.
Hyde

13
E a função Ackermann? pt.wikipedia.org/wiki/Ackermann_function , não é particularmente útil, mas impossível de fazer via loop. (Você também pode querer assistir a este vídeo youtube.com/watch?v=i7sm9dzFtEI por Computerphile)
WizardOfMenlo

8
@WizardOfMenlo, o código befunge é uma implementação da solução ERRE (que também é uma solução interativa ... com uma pilha). Uma abordagem iterativa com uma pilha pode emular uma chamada recursiva. Em qualquer programação que seja adequadamente poderosa, uma construção em loop pode ser usada para emular outra. A máquina de registro com as instruções INC (r), JZDEC (r, z)pode implementar uma máquina de Turing. Não tem 'recursão' - isso é um salto se zero mais DECrement. Se a função Ackermann é computável (é), essa máquina de registro pode fazê-lo.

Respostas:


164

Sim e não. Por fim, não há nada que a recursão possa calcular que o loop não pode, mas o loop requer muito mais encanamento. Portanto, a única coisa que a recursão pode fazer que os loops não podem é tornar algumas tarefas super fáceis.

Tome andando uma árvore. Andar em uma árvore com recursão é estúpido e fácil. É a coisa mais natural do mundo. Andar em uma árvore com laços é muito menos direto. É necessário manter uma pilha ou alguma outra estrutura de dados para acompanhar o que você fez.

Muitas vezes, a solução recursiva para um problema é mais bonita. Esse é um termo técnico e importa.


120
Basicamente, fazer loops em vez de recursão significa manipular manualmente a pilha.
Silviu Burcea

15
... a (s) pilha (s) . A situação a seguir pode preferir fortemente ter mais de uma pilha. Considere uma função recursiva Aque encontra algo em uma árvore. Sempre Aque encontra essa coisa, ela lança outra função recursiva Bque encontra uma coisa relacionada na subárvore na posição em que foi lançada A. Uma vez Bfinalizada a recursão, ela retorna Ae a última continua sua própria recursão. Pode-se declarar uma pilha para Ae uma para B, ou colocar a Bpilha dentro do Aloop. Se alguém insistir em usar uma única pilha, as coisas ficam realmente complicadas.
Rwong

35
Therefore, the one thing recursion can do that loops can't is make some tasks super easy. E a única coisa que os loops podem fazer e não a recursão é tornar algumas tarefas super fáceis. Você já viu as coisas feias e não intuitivas que você precisa fazer para transformar os problemas iterativos mais naturais de recursão ingênua em recursão posterior, para que eles não explodam?
Mason Wheeler

10
@MasonWheeler 99% do tempo essas "coisas" podem ser melhor encapsulado dentro de uma recursão-operador como mapou fold(na verdade, se você optar por considerá-los primitivos, eu acho que você pode usar fold/ unfoldcomo uma terceira alternativa para laços ou recursão). A menos que você esteja escrevendo o código da biblioteca, não há muitos casos em que você deveria se preocupar com a implementação da iteração, e não com a tarefa que ela deveria estar realizando - na prática, isso significa que loops explícitos e recursão explícita são igualmente pobres. abstrações que devem ser evitadas no nível superior.
Leushenko

7
Você pode comparar duas seqüências de caracteres comparando recursivamente substrings, mas apenas comparando cada caractere, um por um, até obter uma incompatibilidade que possa ter um desempenho melhor e ser mais claro para o leitor.
22815 Steven Burnap

78

Não.

Descer até os próprios fundamentos dos mínimos necessários, a fim de calcular, você só precisa ser capaz de loop (isso por si só não é suficiente, mas é um componente necessário). Não importa como .

Qualquer linguagem de programação que possa implementar uma máquina de Turing é chamada de Turing complete . E há muitos idiomas que estão completos.

Minha língua favorita no caminho de "isso realmente funciona?" A completude de Turing é a do FRACTRAN , que é Turing completo . Ele possui uma estrutura de loop e você pode implementar uma máquina de Turing. Assim, qualquer coisa que seja computável, pode ser implementada em uma linguagem que não tenha recursão. Portanto, não há nada que a recursão possa fornecer em termos de computabilidade que um loop simples não pode.

Isso realmente se resume a alguns pontos:

  • Tudo o que é computável é computável em uma máquina de Turing
  • Qualquer idioma que possa implementar uma máquina de Turing (chamada Turing complete), pode calcular qualquer coisa que qualquer outro idioma possa
  • Como existem máquinas de Turing em idiomas que não têm recursão (e outras que têm recursão quando você entra em outras esolangs), é necessariamente verdade que não há nada que você possa fazer com a recursão que você não pode fazer com um loop (e nada que você possa fazer com um loop que você não pode fazer com recursão).

Isso não quer dizer que existem algumas classes de problemas que são mais facilmente pensadas com recursão do que com loop ou com loop ao invés de com recursão. No entanto, essas ferramentas também são igualmente poderosas.

E embora eu tenha levado isso ao extremo 'esolang' (principalmente porque você pode encontrar coisas que Turing estão completas e implementadas de maneiras bastante estranhas), isso não significa que os esolangs sejam de qualquer forma opcionais. Existe uma lista completa de coisas que são acidentalmente concluídas por Turing, incluindo os modelos Magic the Gathering, Sendmail, MediaWiki e o sistema de tipos Scala. Muitos deles estão longe de ser ótimos quando se trata de realmente fazer algo prático, mas você pode calcular qualquer coisa que seja computável usando essas ferramentas.


Essa equivalência pode se tornar particularmente interessante quando você entra em um tipo específico de recursão conhecido como chamada final .

Se você tem, digamos, um método fatorial escrito como:

int fact(int n) {
    return fact(n, 1);
}

int fact(int n, int accum) {
    if(n == 0) { return 1; }
    if(n == 1) { return accum; }
    return fact(n-1, n * accum);
}

Esse tipo de recursão será reescrito como um loop - nenhuma pilha usada. Tais abordagens são realmente mais elegantes e fáceis de entender do que o loop equivalente sendo gravado, mas, novamente, para cada chamada recursiva pode haver um loop equivalente gravado e para cada loop pode ser gravada uma chamada recursiva.

Há também momentos em que a conversão do loop simples em uma chamada recursiva de chamada de cauda pode ser complicada e mais difícil de entender.


Se você quiser entrar no lado da teoria, consulte a tese de Church Turing . Você também pode achar útil a tese de revisão da igreja no CS.SE.


29
A perfeição de Turing é exagerada demais, como é importante. Muitas coisas são Turing Complete ( como Magic the Gathering ), mas isso não significa que é o mesmo que outra coisa que é Turing Complete. Pelo menos não em um nível que importa. Não quero andar em uma árvore com Magic the Gathering.
Scant Roger

7
Uma vez que você pode reduzir um problema para "isso tem o mesmo poder que uma máquina de Turing", é o suficiente para chegar lá. Máquinas de Turing são um obstáculo bastante baixo, mas é tudo o que é necessário. Não há nada que um loop possa fazer que a recursão não possa fazer, nem vice-versa.

4
A afirmação feita nesta resposta é obviamente correta, mas ouso dizer que o argumento não é realmente convincente. As máquinas de Turing não têm um conceito direto de recursão, pelo que dizer "você pode simular uma máquina de Turing sem recursão" não prova realmente nada. O que você teria que mostrar para provar a afirmação é que as máquinas de Turing podem simular recursão. Se você não mostra isso, deve assumir fielmente que a hipótese de Church-Turing também vale para recursão (o que ocorre), mas o OP questionou isso.
5gon12eder

10
A pergunta do OP é "pode", não "melhor" ou "mais eficiente" ou algum outro qualificador. "Turing Complete" significa que qualquer coisa que possa ser feita com recursão também pode ser feita com um loop. Se essa é a melhor maneira de fazê-lo em qualquer implementação de linguagem específica é uma questão totalmente diferente.
Steven Burnap 22/11/2015

7
"Pode" NÃO é a mesma coisa que "melhor". Quando você confunde "não é o melhor" para "não pode", fica paralisado porque, não importa de que maneira você faça alguma coisa, quase sempre há uma maneira melhor.
22615 Steven Burnap

31

Existem casos em que uma tarefa só pode ser executada usando recursão, em vez de um loop?

Você sempre pode transformar o algoritmo recursivo em um loop, que usa uma estrutura de dados Last-In-First-Out (pilha AKA) para armazenar o estado temporário, porque a chamada recursiva é exatamente isso, armazenando o estado atual em uma pilha, prosseguindo com o algoritmo, depois, restaurando o estado. A resposta é tão curta: Não, não existem casos assim .

No entanto, um argumento pode ser feito para "sim". Vamos dar um exemplo concreto e fácil: mesclar classificação. Você precisa dividir os dados em duas partes, mesclar as partes e combiná-las. Mesmo se você não fizer uma chamada de função da linguagem de programação real para mesclar a classificação, a fim de mesclar as partes, será necessário implementar uma funcionalidade idêntica à real de uma chamada de função (pressione o estado em sua própria pilha, vá para início do loop com diferentes parâmetros de partida e depois pop o estado da sua pilha).

É recursão, se você implementar a chamada de recursão, como etapas separadas de "push state" e "jump to begin" e "pop state"? E a resposta para isso é: Não, ainda não é chamado de recursão, é chamado de iteração com pilha explícita (se você deseja usar a terminologia estabelecida).


Observe que isso também depende da definição de "tarefa". Se a tarefa é classificar, você pode fazê-lo com muitos algoritmos, muitos dos quais não precisam de nenhum tipo de recursão. Se a tarefa é implementar um algoritmo específico, como a classificação de mesclagem, aplica-se a ambiguidade acima.

Então, vamos considerar a questão: existem tarefas gerais, para as quais existem apenas algoritmos semelhantes à recursão. Do comentário de @WizardOfMenlo sob a questão, a função Ackermann é um exemplo simples disso. Portanto, o conceito de recursão é autônomo, mesmo que possa ser implementado com uma construção de programa de computador diferente (iteração com pilha explícita).


2
Ao lidar com uma montagem para um processador sem pilha, essas duas técnicas se tornam uma de repente.
Joshua

@Joshua Indeed! É uma questão de nível de abstração. Se você subir um nível ou dois, são apenas portas lógicas.
Hyde

2
Isso não está correto. Para emular recursão com iteração, você precisa de uma pilha onde o acesso aleatório é possível. Uma única pilha sem acesso aleatório mais uma quantidade finita de memória diretamente acessível seria um PDA, que não é completo para Turing.
Gilles

@ Gilles Post antigo, mas por que a pilha de acesso aleatório é necessária? Além disso, todos os computadores reais não são menos do que os PDAs, pois possuem apenas uma quantidade finita de memória diretamente acessível e nenhuma pilha (exceto usando essa memória)? Isso não parece abstração muito prática, se diz "não podemos fazer recursão na realidade".
hyde

20

Depende de quão estritamente você define "recursão".

Se exigirmos estritamente que envolva a pilha de chamadas (ou qualquer mecanismo para manter o estado do programa usado), sempre poderemos substituí-lo por algo que não o faça. De fato, as linguagens que levam naturalmente ao uso pesado de recursão tendem a ter compiladores que fazem uso intenso da otimização de chamada de cauda; portanto, o que você escreve é ​​recursivo, mas o que você executa é iterativo.

Mas vamos considerar um caso em que fazemos uma chamada recursiva e usamos o resultado de uma chamada recursiva para essa chamada recursiva.

public static BigInteger Ackermann(BigInteger m, BigInteger n)
{
  if (m == 0)
    return  n+1;
  if (n == 0)
    return Ackermann(m - 1, 1);
  else
    return Ackermann(m - 1, Ackermann(m, n - 1));
}

É fácil fazer a primeira chamada recursiva iterativa:

public static BigInteger Ackermann(BigInteger m, BigInteger n)
{
restart:
  if (m == 0)
    return  n+1;
  if (n == 0)
  {
    m--;
    n = 1;
    goto restart;
  }
  else
    return Ackermann(m - 1, Ackermann(m, n - 1));
}

Em seguida, podemos limpar o dispositivo gotopara afastar os velociraptores e a sombra de Dijkstra:

public static BigInteger Ackermann(BigInteger m, BigInteger n)
{
  while(m != 0)
  {
    if (n == 0)
    {
      m--;
      n = 1;
    }
    else
      return Ackermann(m - 1, Ackermann(m, n - 1));
  }
  return  n+1;
}

Mas, para remover as outras chamadas recursivas, teremos que armazenar os valores de algumas chamadas em uma pilha:

public static BigInteger Ackermann(BigInteger m, BigInteger n)
{
  Stack<BigInteger> stack = new Stack<BigInteger>();
  stack.Push(m);
  while(stack.Count != 0)
  {
    m = stack.Pop();
    if(m == 0)
      n = n + 1;
    else if(n == 0)
    {
      stack.Push(m - 1);
      n = 1;
    }
    else
    {
      stack.Push(m - 1);
      stack.Push(m);
      --n;
    }
  }
  return n;
}

Agora, quando consideramos o código fonte, certamente transformamos nosso método recursivo em um método iterativo.

Considerando o que isso foi compilado, transformamos o código que usa a pilha de chamadas para implementar recursão em código que não o faz (e, ao fazer isso, transformou o código que lançará uma exceção de excesso de pilha para valores muito pequenos em código que apenas demore muito tempo para retornar [consulte Como posso impedir que minha função Ackerman transborde a pilha? para algumas otimizações adicionais que fazem com que ela realmente retorne para muitas outras entradas possíveis]).

Considerando como a recursão é implementada geralmente, transformamos o código que usa a pilha de chamadas em código que usa uma pilha diferente para manter operações pendentes. Poderíamos, portanto, argumentar que ainda é recursivo, quando considerado nesse nível baixo.

E nesse nível, de fato não há outras maneiras de contornar isso. Portanto, se você considera esse método recursivo, há realmente coisas que não podemos fazer sem ele. Geralmente, embora não rotulemos esse código como recursivo. O termo recursão é útil porque cobre um certo conjunto de abordagens e nos dá uma maneira de falar sobre elas, e não estamos mais usando uma delas.

Claro, tudo isso pressupõe que você tem uma escolha. Existem dois idiomas que proíbem chamadas recursivas e idiomas que não possuem as estruturas de loop necessárias para a iteração.


Só é possível substituir a pilha de chamadas por algo equivalente se a pilha de chamadas estiver limitada ou se houver acesso a uma memória ilimitada fora da pilha de chamadas. Há uma classe significativa de problemas que são solucionáveis ​​por autômatos push-down que têm uma pilha de chamadas ilimitada, mas que só podem ter um número finito de estados.
Supercat

Esta é a melhor resposta, talvez a única resposta correta. Até o segundo exemplo ainda é recursivo e, nesse nível, a resposta para a pergunta original é não . Com uma definição mais ampla de recursão, é impossível evitar a recursão para a função Ackermann.
gerrit

@gerrit e com um valor mais estreito, evita isso. Por fim, tudo se resume ao que fazemos ou não ao aplicar esse rótulo útil que usamos para determinados códigos.
Jon Hanna

1
Ingressou no site para votar. A função de Ackermann é de natureza recursiva. Implementar uma estrutura recursiva com um loop e uma pilha não a torna uma solução iterativa; você acabou de mover a recursão para o espaço do usuário.
Aaron McMillin

9

A resposta clássica é "não", mas permita-me explicar por que acho que "sim" é uma resposta melhor.


Antes de prosseguir, vamos desviar o caminho: do ponto de vista da computabilidade e da complexidade:

  • A resposta é "não" se você tiver permissão para ter uma pilha auxiliar durante o loop.
  • A resposta é "sim" se você não tiver permitido nenhum dado extra ao fazer um loop.

Ok, agora, vamos colocar um pé na área de prática, mantendo o outro pé na terra da teoria.


A pilha de chamadas é uma estrutura de controle , enquanto uma pilha manual é uma estrutura de dados . Controle e dados não são conceitos iguais, mas são equivalentes no sentido de que podem ser reduzidos um ao outro (ou "emulados" um pelo outro) do ponto de vista da computabilidade ou da complexidade.

Quando essa distinção é importante? Quando você trabalha com ferramentas do mundo real. Aqui está um exemplo:

Digamos que você esteja implementando o N-way mergesort. Você pode ter um forloop que percorra cada um dos Nsegmentos, os chama mergesortseparadamente e depois mescla os resultados.

Como você pode paralelizar isso com o OpenMP?

No domínio recursivo, é extremamente simples: basta colocar #pragma omp parallel forseu loop que vai de 1 a N e pronto. No domínio iterativo, você não pode fazer isso. Você precisa gerar threads manualmente e passar os dados apropriados manualmente para que eles saibam o que fazer.

Por outro lado, existem outras ferramentas (como vetorizadores automáticos, por exemplo #pragma vector) que funcionam com loops, mas são totalmente inúteis com recursão.

A propósito, apenas porque você pode provar que os dois paradigmas são equivalentes matematicamente, isso não significa que eles sejam iguais na prática. Um problema que pode ser trivial de automatizar em um paradigma (por exemplo, paralelização de loop) pode ser muito mais difícil de resolver no outro paradigma.

ou seja: as ferramentas para um paradigma não se traduzem automaticamente para outros paradigmas.

Conseqüentemente, se você precisar de uma ferramenta para resolver um problema, é provável que a ferramenta funcione apenas com um tipo específico de abordagem e, consequentemente, você não conseguirá resolver o problema com uma abordagem diferente, mesmo se puder provar matematicamente que o problema pode ser resolvido de qualquer maneira.


Além disso, considere que o conjunto de problemas que podem ser resolvidos com um autômato push-down é maior que o conjunto que pode ser resolvido com um autômato finito (determinístico ou não), mas menor que o conjunto que pode ser resolvido com um Máquina de Turing.
Supercat 24/11

8

Deixando de lado o raciocínio teórico, vamos dar uma olhada em como são a recursão e os loops do ponto de vista da máquina (hardware ou virtual). Recursão é uma combinação de fluxo de controle que permite iniciar a execução de algum código e retornar após a conclusão (em uma visão simplista quando sinais e exceções são ignorados) e de dados que são passados ​​para esse outro código (argumentos) e retornados de (resultado). Geralmente, nenhum gerenciamento de memória explícita está envolvido; no entanto, há alocação implícita de memória da pilha para salvar endereços de retorno, argumentos, resultados e dados locais intermediários.

Um loop é uma combinação de fluxo de controle e dados locais. Comparando isso com recursão, podemos ver que a quantidade de dados nesse caso é fixa. A única maneira de quebrar essa limitação é usar a memória dinâmica (também conhecida como heap ) que pode ser alocada (e liberada) sempre que necessário.

Para resumir:

  • Caso de recursão = Fluxo de controle + Pilha (+ Heap)
  • Caixa de loop = fluxo de controle + pilha

Supondo que a parte do fluxo de controle seja razoavelmente poderosa, a única diferença está nos tipos de memória disponíveis. Então, ficamos com 4 casos (o poder da expressividade está listado entre parênteses):

  1. Sem pilha, sem pilha: recursão e estruturas dinâmicas são impossíveis. (recursão = loop)
  2. Stack, no heap: recursão é OK, estruturas dinâmicas são impossíveis. (recursão> loop)
  3. Sem pilha, pilha: recursão é impossível, estruturas dinâmicas estão OK. (recursão = loop)
  4. Pilha, pilha: recursão e estruturas dinâmicas estão OK. (recursão = loop)

Se as regras do jogo forem um pouco mais rígidas e a implementação recursiva não permitir o uso de loops, obtemos o seguinte:

  1. Sem pilha, sem pilha: recursão e estruturas dinâmicas são impossíveis. (recursão <loop)
  2. Stack, no heap: recursão é OK, estruturas dinâmicas são impossíveis. (recursão> loop)
  3. Sem pilha, pilha: recursão é impossível, estruturas dinâmicas estão OK. (recursão <loop)
  4. Pilha, pilha: recursão e estruturas dinâmicas estão OK. (recursão = loop)

A principal diferença no cenário anterior é que a falta de memória da pilha não permite que a recursão sem loops execute mais etapas durante a execução do que as linhas de código.


2

Sim. Existem várias tarefas comuns que são fáceis de executar usando recursão, mas impossíveis com apenas loops:

  • Causando estouros de pilha.
  • Programadores iniciantes totalmente confusos.
  • Criando funções de aparência rápida que realmente são O (n ^ n).

3
Por favor, estes são realmente fáceis com loops, eu os vejo o tempo todo. Caramba, com um pouco de esforço, você nem precisa dos loops. Mesmo se a recursão for mais fácil.
AviD 24/11/2015

1
na verdade, A (0, n) = n + 1; A (m, 0) = A (m-1,1) se m> 0; A (m, n) = A (m-1, A (m, n-1)) se m> 0, n> 0 cresce um pouco mais rápido que O (n ^ n) (para m = n) :)
John Donne

1
@ JohnDonn Mais do que um pouco, é super exponencial. para n = 3 n ^ n ^ n para n = 4 n ^ n ^ n ^ n ^ n e assim por diante. n à n potência n vezes.
precisa

1

Há uma diferença entre funções recursivas e funções recursivas primitivas. Funções recursivas primitivas são aquelas calculadas usando loops, em que a contagem máxima de iterações de cada loop é calculada antes do início da execução do loop. (E "recursivo" aqui não tem nada a ver com o uso de recursão).

As funções recursivas primitivas são estritamente menos poderosas que as funções recursivas. Você obteria o mesmo resultado se assumisse funções que usam recursão, em que a profundidade máxima da recursão deve ser calculada previamente.


3
Não tenho certeza de como isso se aplica à pergunta acima. Você pode, por favor, deixar essa conexão mais clara?
Yakk

1
Substituindo o "laço" imprecisa com a importante distinção entre "loop com contagem de iteração limitada" e "loop com contagem de iteração ilimitado", que eu pensei que todo mundo saberia de CS 101.
gnasher729

claro, mas ainda não se aplica à pergunta. A questão é sobre loop e recursão, não recursividade e recursão primitivas. Imagine se alguém perguntasse sobre as diferenças de C / C ++ e você respondesse sobre a diferença entre K&R C e Ansi C. Certamente isso torna as coisas mais precisas, mas não responde à pergunta.
Yakk 29/11

1

Se você está programando em c ++ e usa o c ++ 11, há uma coisa a ser feita usando recursões: funções constexpr. Mas o padrão limita isso a 512, conforme explicado nesta resposta . O uso de loops nesse caso não é possível, pois, nesse caso, a função não pode ser constexpr, mas isso é alterado no c ++ 14.


0
  • Se a chamada recursiva é a primeira ou a última declaração (excluindo a verificação de condições) de uma função recursiva, é muito fácil traduzir para uma estrutura em loop.
  • Mas se a função fizer outras coisas antes e depois da chamada recursiva , seria complicado convertê-la em loops.
  • Se a função tiver várias chamadas recursivas, a conversão para código que está usando apenas loops será praticamente impossível. Alguma pilha será necessária para acompanhar os dados. Na recursão, a própria pilha de chamadas funcionará como a pilha de dados.

A caminhada em árvore tem várias chamadas recursivas (uma para cada filho), mas é trivialmente transformada em um loop usando uma pilha explícita. Os analisadores, por outro lado, costumam ser irritantes para se transformar.
CodesInChaos

@CodesInChaos Editado.
Gulshan #

-6

Eu concordo com as outras perguntas. Não há nada que você possa fazer com recursão que não possa fazer com um loop.

MAS , na minha opinião, a recursão pode ser muito perigosa. Primeiro, para alguns, é mais difícil entender o que realmente está acontecendo no código. Segundo, pelo menos para C ++ (Java, não tenho certeza), cada etapa da recursão afeta a memória porque cada chamada de método causa acúmulo de memória e inicialização do cabeçalho dos métodos. Dessa forma, você pode aumentar sua pilha. Simplesmente tente a recursão dos números de Fibonacci com um alto valor de entrada.


2
Uma implementação ingênua e recursiva dos números de Fibonacci com recursão será executada "fora do tempo" antes de ficar sem espaço na pilha. Eu acho que existem outros problemas que são melhores para este exemplo. Além disso, para muitos problemas, uma versão em loop tem exatamente o mesmo impacto de memória que uma versão recursiva, apenas no heap em vez da pilha (se sua linguagem de programação as distingue).
Pa Elo Ebermann 22/11/2015

6
Loop, também pode ser "muito perigoso" se você apenas se esqueça de incrementar a variável do laço ...
h22

2
Portanto, produzir deliberadamente um estouro de pilha é uma tarefa que se torna muito complicada sem o uso de recursão.
5gon12eder

@ 5gon12eder, que nos leva a Quais métodos existem para evitar um estouro de pilha em um algoritmo recursivo? - escrever para contratar TCO, ou Memoisation pode ser útil. Abordagens iterativas x abordagens recursivas também é interessante, pois lida com duas abordagens recursivas diferentes para Fibonacci.

1
Na maioria das vezes, se você receber um estouro de pilha na recursão, teria sofrido um travamento na versão iterativa. Pelo menos o primeiro lança com um rastreamento de pilha.
Jon Hanna
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.