Exemplos do mundo real de recursão [fechado]


95

Quais são os problemas do mundo real em que uma abordagem recursiva é a solução natural, além da pesquisa em profundidade (DFS)?

(Não considero a Torre de Hanói , o número de Fibonacci ou os problemas fatoriais do mundo real. Eles são um pouco inventados em minha mente.)


2
Obrigado por todas as sugestões, mas todos estão sugerindo travessias de árvore / rede. Teses são essencialmente todos exemplos de Depth-First-Search (ou BFS, eu acho). Eu estava procurando por outros algoritmos / problemas bem motivados.
redfood de

10
Eu gosto dessa pergunta! "Conte-me todos os usos da técnica X, EXCETO o principal uso prático da técnica X"
Justin Standard

1
Eu uso recursão o tempo todo, mas geralmente para coisas matemáticas e gráficas. Estou tentando procurar exemplos de recursão que sejam significativos para não programadores.
redfood

6
Escolha seus próprios romances de aventura! Quero ler tudo e a recursão é a melhor maneira de fazer isso.
Andres

Não há recursão no mundo real. A recursão é uma abstração matemática. Você pode modelar muitas coisas usando recursão. Nesse sentido, Fibonacci é absolutamente do mundo real, pois existem vários problemas do mundo real que podem ser modelados dessa maneira. Se você pensa que Fibonacci não é do mundo real, então eu afirmaria que todos os outros exemplos também são abstrações, não exemplos do mundo real.
Zane

Respostas:


41

Existem muitos exemplos matemáticos aqui, mas você queria um exemplo do mundo real , então, com um pouco de reflexão, este é possivelmente o melhor que posso oferecer:

Você encontra uma pessoa que contraiu uma determinada infecção contagiosa, que não é fatal, e se corrige rapidamente (Tipo A), exceto uma em cada 5 pessoas (chamaremos de tipo B) que se infectou permanentemente com ela e não mostra sintomas e apenas atua como um propagador.

Isso cria ondas de destruição bastante irritantes sempre que o tipo B infecta uma multidão de tipo A.

Sua tarefa é rastrear todos os tipos B e imunizá-los para interromper a espinha dorsal da doença. Infelizmente, você não pode administrar uma cura nacional para todos, porque as pessoas que são do tipo A também são mortalmente alérgicas à cura que funciona para o tipo B.

A forma como você faria isso, seria a descoberta social, dado uma pessoa infectada (Tipo A), escolher todos os seus contatos na última semana, marcando cada contato em um heap. Quando você testa uma pessoa infectada, adicione-a à fila de "acompanhamento". Quando uma pessoa é do tipo B, adicione-a ao "acompanhamento" na cabeça (porque você deseja interromper isso rapidamente).

Depois de processar uma determinada pessoa, selecione a pessoa na frente da fila e aplique a imunização, se necessário. Obtenha todos os seus contatos não visitados anteriormente e, em seguida, teste para ver se eles estão infectados.

Repita até que a fila de pessoas infectadas chegue a 0 e aguarde outro surto.

(Ok, isso é um pouco iterativo, mas é uma maneira iterativa de resolver um problema recursivo, neste caso, a primeira travessia de uma base populacional tentando descobrir caminhos prováveis ​​para os problemas e, além disso, as soluções iterativas são frequentemente mais rápidas e eficazes , e eu compulsivamente removo a recursão em todos os lugares a ponto de se tornar instintivo. ... droga!)


2
Obrigado - isso ainda é uma travessia de gráfico, mas é bem motivado e faz sentido para pessoas não programadoras.
redfood

Acredito que encontrar o paciente 0 seria um exemplo melhor. Determine todas as interações que podem ter causado a infecção. Repita para todos os envolvidos que eram contagiosos no momento da interação até que nenhum contagioso seja encontrado
William FitzPatrick

4
este exemplo do mundo real parece tão familiar agora :(
haroldolivieri

109

Um exemplo do mundo real de recursão

Um girassol


12
codificado com recursão pelo arquiteto Matrix :)
Marcel

3
Como isso é recursivo? Claro, é lindo. Mas recursivo? Um repolho fractal teria funcionado bem, mas não vejo semelhanças nesta flor.
Clément

1
Bem, é um pouco brincalhão, mas é um exemplo de filotaxia, que pode ser descrita com a sequência de Fibonacci, que é comumente implementada por meio de recursão.
Hans Sjunnesson

1
"comumente implementado por meio de recursão" não significa necessariamente que a flor o faça. Talvez sim; Não sou biólogo molecular o suficiente para saber, mas sem uma explicação sobre como funciona, não vejo isso como particularmente útil. Downvoting. Se você quiser adicionar uma descrição (seja biologicamente precisa ou não, pode fornecer uma visão) para acompanhá-la, terei o prazer de reconsiderar a votação.
lindes

65

Que tal qualquer coisa envolvendo uma estrutura de diretório no sistema de arquivos. Localização recursiva de arquivos, exclusão de arquivos, criação de diretórios, etc.

Aqui está uma implementação Java que imprime recursivamente o conteúdo de um diretório e seus subdiretórios.

import java.io.File;

public class DirectoryContentAnalyserOne implements DirectoryContentAnalyser {

    private static StringBuilder indentation = new StringBuilder();

    public static void main (String args [] ){
        // Here you pass the path to the directory to be scanned
        getDirectoryContent("C:\\DirOne\\DirTwo\\AndSoOn");
    }

    private static void getDirectoryContent(String filePath) {

        File currentDirOrFile = new File(filePath);

        if ( !currentDirOrFile.exists() ){
            return;
        }
        else if ( currentDirOrFile.isFile() ){
            System.out.println(indentation + currentDirOrFile.getName());
            return;
        }
        else{
            System.out.println("\n" + indentation + "|_" +currentDirOrFile.getName());
            indentation.append("   ");

            for ( String currentFileOrDirName : currentDirOrFile.list()){
                getPrivateDirectoryContent(currentDirOrFile + "\\" + currentFileOrDirName);
            }

            if (indentation.length() - 3 > 3 ){
                indentation.delete(indentation.length() - 3, indentation.length());
            }
        }       
    }

}

2
Um sistema de arquivos fornece motivação (o que é bom, obrigado), mas este é um exemplo específico de DFS.
redfood

4
Eu não entendi a sigla "DFS" - já faz algum tempo desde que sentei em uma sala de aula.
Matt Dillard

5
pesquisa em profundidade: dfs (nó) {foreach filho no nó {visita (filho); }}
Haoest,

Para um exemplo de código simples, consulte, por exemplo, stackoverflow.com/questions/126756/…
Jonik

Existe um erro neste código? Não deve getPrivateDirectoryContent () ser substituído por getDirectoryContent ()?
Shn_Android_Dev


16

O exemplo de Matt Dillard é bom. De modo mais geral, qualquer caminhada em uma árvore pode geralmente ser tratada por recursão com muita facilidade. Por exemplo, compilar árvores de análise, percorrer XML ou HTML, etc.


Considero essa "compilação de árvores de análise" uma resposta sensata, que envolve árvores, mas ainda não é um problema de pesquisa, como queria aquele que perguntou. Pode ser generalizado para alguma noção geral de compilação ou interpretação de uma linguagem. Também pode ser "interpretar" (compreender, ouvir) uma linguagem natural, por exemplo, o inglês.
imz - Ivan Zakharyaschev


13

A recursão é apropriada sempre que um problema pode ser resolvido dividindo-o em subproblemas, que podem usar o mesmo algoritmo para resolvê-los. Algoritmos em árvores e listas ordenadas são um ajuste natural. Muitos problemas em geometria computacional (e jogos 3D) podem ser resolvidos recursivamente usando árvores de particionamento de espaço binário (BSP), subdivisões de gordura ou outras maneiras de dividir o mundo em subpartes.

A recursão também é apropriada quando você está tentando garantir a exatidão de um algoritmo. Dada uma função que recebe entradas imutáveis ​​e retorna um resultado que é uma combinação de chamadas recursivas e não recursivas nas entradas, geralmente é fácil provar que a função está correta (ou não) usando indução matemática. Freqüentemente, é difícil fazer isso com uma função iterativa ou com entradas que podem sofrer mutação. Isso pode ser útil ao lidar com cálculos financeiros e outras aplicações onde a correção é muito importante.


11

Certamente, muitos compiladores por aí usam muito a recursão. As linguagens de computador são inerentemente recursivas (ou seja, você pode incorporar declarações 'if' dentro de outras declarações 'if', etc.).


As instruções if incorporadas não são recursivas.
John Meagher

Mas analisá-los requer recursão, John.
Apocalisp

2
John, o fato de você poder aninhar instruções if significa que a definição da linguagem (e provavelmente o analisador da linguagem) é recursiva.
Derek Park

A descida recursiva é uma das maneiras mais fáceis de codificar manualmente um compilador. Não é tão fácil quanto usar uma ferramenta como o yacc, mas mais fácil de entender como funciona. Todas as máquinas de estado baseadas em tabelas podem ser explicadas, mas geralmente acabam sendo caixas pretas.
Eclipse

A resposta de Cody Brocious mencionando "árvores de análise de compilação" também apontou para esta área: análise / interpretação / compilação da linguagem.
imz - Ivan Zakharyaschev

9

Desabilitando / configurando somente leitura para todos os controles filhos em um controle de contêiner. Eu precisava fazer isso porque alguns dos controles filhos eram eles próprios contêineres.

public static void SetReadOnly(Control ctrl, bool readOnly)
{
    //set the control read only
    SetControlReadOnly(ctrl, readOnly);

    if (ctrl.Controls != null && ctrl.Controls.Count > 0)
    {
        //recursively loop through all child controls
        foreach (Control c in ctrl.Controls)
            SetReadOnly(c, readOnly);
    }
}

8

Ciclo de avaliação / aplicação famoso do SICP

texto alternativo
(fonte: mit.edu )

Aqui está a definição de eval:

(define (eval exp env)
  (cond ((self-evaluating? exp) exp)
        ((variable? exp) (lookup-variable-value exp env))
        ((quoted? exp) (text-of-quotation exp))
        ((assignment? exp) (eval-assignment exp env))
        ((definition? exp) (eval-definition exp env))
        ((if? exp) (eval-if exp env))
        ((lambda? exp)
         (make-procedure (lambda-parameters exp)
                         (lambda-body exp)
                         env))
        ((begin? exp) 
         (eval-sequence (begin-actions exp) env))
        ((cond? exp) (eval (cond->if exp) env))
        ((application? exp)
         (apply (eval (operator exp) env)
                (list-of-values (operands exp) env)))
        (else
         (error "Unknown expression type - EVAL" exp))))

Aqui está a definição de aplicar:

(define (apply procedure arguments)
  (cond ((primitive-procedure? procedure)
         (apply-primitive-procedure procedure arguments))
        ((compound-procedure? procedure)
         (eval-sequence
           (procedure-body procedure)
           (extend-environment
             (procedure-parameters procedure)
             arguments
             (procedure-environment procedure))))
        (else
         (error
          "Unknown procedure type - APPLY" procedure))))

Aqui está a definição de eval-sequence:

(define (eval-sequence exps env)
  (cond ((last-exp? exps) (eval (first-exp exps) env))
        (else (eval (first-exp exps) env)
              (eval-sequence (rest-exps exps) env))))

eval-> apply-> eval-sequence->eval


7

A recursão é usada em coisas como árvores BSP para detecção de colisão no desenvolvimento de jogos (e outras áreas semelhantes).


7

As pessoas geralmente classificam pilhas de documentos usando um método recursivo. Por exemplo, imagine que você está classificando 100 documentos com nomes. Primeiro coloque os documentos em pilhas pela primeira letra e, em seguida, classifique cada pilha.

A pesquisa de palavras no dicionário geralmente é realizada por uma técnica semelhante à pesquisa binária, que é recursiva.

Nas organizações, os chefes geralmente dão comandos aos chefes de departamento, que por sua vez dão comandos aos gerentes e assim por diante.


5

Requisito do mundo real que recebi recentemente:

Requisito A: implemente este recurso após entender completamente o Requisito A.


4

Analisadores e compiladores podem ser escritos em um método descendente recursivo. Não é a melhor maneira de fazer isso, já que ferramentas como lex / yacc geram analisadores mais rápidos e eficientes, mas conceitualmente simples e fáceis de implementar, portanto, permanecem comuns.


4

A recursão é aplicada a problemas (situações) em que você pode dividi-la (reduzi-la) em partes menores, e cada parte (s) se parece com o problema original.

Bons exemplos de onde estão as coisas que contêm partes menores semelhantes a si mesmas:

  • estrutura de árvore (um galho é como uma árvore)
  • listas (parte de uma lista ainda é uma lista)
  • recipientes (bonecas russas)
  • sequências (parte de uma sequência se parece com a próxima)
  • grupos de objetos (um subgrupo ainda é um grupo de objetos)

A recursão é uma técnica para quebrar continuamente o problema em pedaços cada vez menores, até que um desses pedaços se torne pequeno o suficiente para ser um pedaço de bolo. É claro que, depois de separá-los, você terá que "costurar" os resultados na ordem certa para formar uma solução total para o problema original.

Alguns algoritmos de classificação recursiva, algoritmos de caminhada em árvore, algoritmos de mapeamento / redução, divisão e conquista são todos exemplos dessa técnica.

Em programação de computador, a maioria das linguagens de tipo de retorno de chamada baseadas em pilha já têm os recursos integrados para recursão: ou seja,

  • dividir o problema em partes menores ==> chamar a si mesmo em um subconjunto menor dos dados originais),
  • acompanhe como as peças são divididas ==> pilha de chamadas,
  • costurar os resultados de volta ==> retorno baseado em pilha


4

Alguns ótimos exemplos de recursão são encontrados em linguagens de programação funcionais . Em linguagens de programação funcional ( Erlang , Haskell , ML / OCaml / F # , etc.), é muito comum que qualquer processamento de lista use recursão.

Ao lidar com listas em linguagens de estilo OOP imperativas típicas, é muito comum ver listas implementadas como listas vinculadas ([item1 -> item2 -> item3 -> item4]). No entanto, em algumas linguagens de programação funcional, você descobre que as próprias listas são implementadas recursivamente, onde a "cabeça" da lista aponta para o primeiro item da lista e a "cauda" aponta para uma lista contendo o resto dos itens ( [item1 -> [item2 -> [item3 -> [item4 -> []]]]]). É muito criativo na minha opinião.

Essa manipulação de listas, quando combinada com correspondência de padrões, é MUITO poderosa. Digamos que eu queira somar uma lista de números:

let rec Sum numbers =
    match numbers with
    | [] -> 0
    | head::tail -> head + Sum tail

Isso basicamente diz "se formos chamados com uma lista vazia, retorne 0" (permitindo-nos quebrar a recursão), caso contrário, retorne o valor de head + o valor de Sum chamado com os itens restantes (portanto, nossa recursão).

Por exemplo, posso ter uma lista de URLs , acho que separo todos os URLs aos quais cada URL se vincula e, em seguida, reduzo o número total de links de / para todos os URLs para gerar "valores" para uma página (uma abordagem que o Google leva com o PageRank e que você pode encontrar definido no papel MapReduce original ). Você também pode fazer isso para gerar contagens de palavras em um documento. E muitas, muitas outras coisas também.

Você pode estender esse padrão funcional para qualquer tipo de código MapReduce onde você pode pegar uma lista de algo, transformá-lo e retornar algo mais (seja outra lista ou algum comando zip na lista).


3

XML, ou percorrer qualquer coisa que seja uma árvore. Embora, para ser honesto, eu quase nunca use a recursão no meu trabalho.


Nem mesmo recursão da cauda?
Apocalisp

Usei a recursão uma vez na minha carreira e, quando a estrutura mudou, livrei-me dela. 80% do que fazemos é CRUD.
Charles Graham

1
Mencionar "XML" em primeiro lugar é muito estranho. Não é uma coisa natural, não é algo que uma pessoa comum a quem você vai ensinar tenha que lidar na vida cotidiana. Mas a ideia é muito sensata.
imz - Ivan Zakharyaschev

3

Loops de feedback em uma organização hierárquica.

O chefe principal diz aos executivos para coletar feedback de todos na empresa.

Cada executivo reúne seus subordinados diretos e lhes diz para coletar feedback de seus subordinados diretos.

E assim por diante.

Pessoas sem subordinados diretos - os nós folha da árvore - dão seu feedback.

O feedback sobe na árvore com cada gerente adicionando seu próprio feedback.

Eventualmente, todo o feedback volta para o chefe.

Esta é a solução natural porque o método recursivo permite a filtragem em cada nível - a comparação de duplicatas e a remoção de feedback ofensivo. O chefe poderia enviar um e-mail global e fazer com que cada funcionário reportasse feedback diretamente para ele / ela, mas existem os problemas "você não consegue lidar com a verdade" e "você está demitido", então a recursão funciona melhor aqui.


2

Suponha que você esteja construindo um CMS para um site, onde suas páginas estão em uma estrutura de árvore, com, digamos, a raiz sendo a página inicial.

Suponha também que seu {usuário | cliente | cliente | chefe} solicite que você coloque uma trilha de navegação em cada página para mostrar onde você está na árvore.

Para qualquer página n fornecida, você pode querer ir até o pai de n, e seu pai, e assim por diante, recursivamente para construir uma lista de nós de volta à raiz da árvore da página.

Claro, você está acessando o banco de dados várias vezes por página nesse exemplo, então você pode querer usar algum aliasing SQL onde você procura a tabela de páginas como a, e tabela de páginas novamente como b, e une a.id com b.parent para que você faça o banco de dados fazer as junções recursivas. Já faz um tempo, então minha sintaxe provavelmente não ajuda.

Então, novamente, você pode querer apenas calcular isso uma vez e armazená-lo com o registro da página, atualizando-o apenas se você mover a página. Isso provavelmente seria mais eficiente.

Enfim, esse é meu $ 0,02


2

Você tem uma árvore de organização com N níveis de profundidade. Vários dos nós são verificados e você deseja expandir apenas para os nós que foram verificados.

Isso é algo que eu realmente codifiquei. É bom e fácil com recursão.


2

No meu trabalho, temos um sistema com uma estrutura de dados genérica que pode ser descrita como uma árvore. Isso significa que a recursão é uma técnica muito eficaz para trabalhar com os dados.

Resolvê-lo sem recursão exigiria muito código desnecessário. O problema da recursão é que não é fácil acompanhar o que acontece. Você realmente precisa se concentrar ao seguir o fluxo de execução. Mas quando funciona, o código é elegante e eficaz.



2
  • Analisando um arquivo XML .
  • Pesquisa eficiente em espaços multidimensionais. Por exemplo. quad-trees em 2D, oct-trees em 3D, kd-trees, etc.
  • Agrupamento hierárquico.
  • Venha para pensar sobre isso, atravessar qualquer estrutura hierárquica naturalmente se presta à recursão.
  • Metaprogramação de template em C ++, onde não há loops e a recursão é a única maneira.

"XML" não é essencial para a ideia desta resposta (e mencionar especificamente XML pode ser nojento / entediante para as pessoas que você está ensinando). Qualquer linguagem típica (uma linguagem de computador ou natural) serviria de exemplo para um problema de análise recursiva.
imz - Ivan Zakharyaschev

O pôster perguntou por "problemas do mundo real onde uma abordagem recursiva é a solução natural". A análise de um arquivo xml é certamente um problema do mundo real e, naturalmente, se presta à recursão. O fato de você parecer ter uma estranha aversão ao XML não altera o fato de ele ser amplamente usado.
Dima


2

O melhor exemplo que conheço é quicksort , é muito mais simples com recursão. Dê uma olhada em:

shop.oreilly.com/product/9780596510046.do

www.amazon.com/Beautiful-Code-Leading-Programmers-Practice/dp/0596510047

(Clique no primeiro subtítulo do capítulo 3: "O código mais bonito que já escrevi").


1
E MergeSort também é mais simples com recursão.
Matthew Schinckel,

1
O link está quebrado. Você pode adicionar o título do livro?
Peter Mortensen

@PeterMortensen, o livro é Beautiful Code, de Greg Wilson e Andy Oram. Eu atualizei o link, embora pareça que O'Reilly não permite mais espiar dentro. Mas você pode dar uma olhada na Amazon.
Fabio Ceconello

1

As empresas de telefonia e cabo mantêm um modelo de sua topologia de fiação, que na verdade é uma grande rede ou gráfico. A recursão é uma maneira de percorrer este modelo quando você deseja localizar todos os elementos pais ou filhos.

Uma vez que a recursão é cara do ponto de vista do processamento e da memória, esta etapa normalmente só é executada quando a topologia é alterada e o resultado é armazenado em um formato de lista pré-ordenado modificado.


1

O raciocínio indutivo, o processo de formação de conceitos, é recursivo por natureza. Seu cérebro faz isso o tempo todo, no mundo real.


1

Idem ao comentário sobre compiladores. Os nós da árvore de sintaxe abstrata naturalmente se prestam à recursão. Todas as estruturas de dados recursivas (listas vinculadas, árvores, gráficos, etc.) também são mais facilmente tratadas com recursão. Acho que a maioria de nós não consegue usar muito a recursão depois que saímos da escola por causa dos tipos de problemas do mundo real, mas é bom estar ciente disso como uma opção.


1

A multiplicação de números naturais é um exemplo do mundo real de recursão:

To multiply x by y
  if x is 0
    the answer is 0
  if x is 1
    the answer is y
  otherwise
    multiply x - 1 by y, and add x
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.