Ejemplos de recursividad del mundo real [cerrado]

97

¿Cuáles son los problemas del mundo real donde un enfoque recursivo es la solución natural además de la búsqueda en profundidad (DFS)?

(No considero la Torre de Hanoi , el número de Fibonacci o los problemas factoriales del mundo real. Son un poco inventados en mi mente).

Peter Mortensen
fuente
2
Gracias por todas las sugerencias, pero todos sugieren recorridos de árbol / red. Las tesis son esencialmente todos ejemplos de búsqueda en profundidad (o BFS, supongo). Estaba buscando otros algoritmos / problemas bien motivados.
Redfood
10
¡Me gusta esta pregunta! "Dime todos los usos de la técnica X, EXCEPTO el uso práctico principal de la técnica X"
Justin Standard
1
Utilizo la recursividad todo el tiempo, pero generalmente para cosas matemáticas y gráficas. Estoy tratando de buscar ejemplos de recursividad que sean significativos para los no programadores.
Redfood
6
¡Elige tus propias novelas de aventuras! Quiero leer todo y la recursividad es la mejor manera de hacerlo.
Andrés
No hay recursividad en el mundo real. La recursividad es una abstracción matemática. Puedes modelar muchas cosas usando la recursividad. En ese sentido, Fibonacci es absolutamente del mundo real, ya que hay bastantes problemas del mundo real que se pueden modelar de esta manera. Si cree que Fibonacci no es del mundo real, entonces afirmaría que todos los demás ejemplos son también abstracciones, no ejemplos del mundo real.
Zane

Respuestas:

41

Hay muchos ejemplos matemáticos aquí, pero querías un ejemplo del mundo real , así que con un poco de pensamiento, esto es posiblemente lo mejor que puedo ofrecer:

Encuentra a una persona que ha contraído una determinada infección contagiosa, que no es mortal, y se cura por sí sola rápidamente (tipo A), excepto una de cada 5 personas (las llamaremos tipo B) que se infectan permanentemente y no muestran síntomas y simplemente actúa como esparcidor.

Esto crea olas de estragos bastante molestas cuando el tipo B infecta a una multitud de tipo A.

Su tarea es localizar a todos los tipos B e inmunizarlos para detener la columna vertebral de la enfermedad. Desafortunadamente, no se puede administrar una cura a nivel nacional para todos, porque las personas que son tipo A también son mortalmente alérgicas a la cura que funciona para el tipo B.

La forma en que haría esto sería el descubrimiento social, dada una persona infectada (Tipo A), elegir todos sus contactos en la última semana, marcando cada contacto en un montón. Cuando pruebe que una persona está infectada, agréguela a la cola de "seguimiento". Cuando una persona es del tipo B, agréguela al "seguimiento" en la parte superior (porque desea detener esto rápido).

Después de procesar a una persona determinada, seleccione a la persona del frente de la cola y aplique la inmunización si es necesario. Consiga que todos sus contactos no hayan sido visitados anteriormente y luego pruebe para ver si están infectados.

Repita hasta que la cola de personas infectadas se convierta en 0 y luego espere otro brote.

(De acuerdo, esto es un poco iterativo, pero es una forma iterativa de resolver un problema recursivo, en este caso, el primer recorrido en amplitud de una base de población tratando de descubrir posibles caminos a los problemas y, además, las soluciones iterativas suelen ser más rápidas y efectivas , y elimino compulsivamente la recursividad en todas partes tanto que se vuelve instintivo ... ¡maldita sea!)

Kent Fredric
fuente
2
Gracias, este sigue siendo un recorrido de gráfico, pero está bien motivado y tiene sentido para las personas que no son programadores.
Redfood
Creo que encontrar al paciente 0 sería un mejor ejemplo. Determine todas las interacciones que podrían haber causado una infección. Repita con todos los involucrados que eran contagiosos en el momento de la interacción hasta que no se encuentren contagiosos
William FitzPatrick
4
este ejemplo del mundo real se siente tan familiar ahora :(
haroldolivieri
109

Un ejemplo real de recursividad

Un girasol

Hans Sjunnesson
fuente
12
codificado con recursividad por el arquitecto Matrix :)
Marcel
3
¿Cómo es esto recursivo? Claro, es bonito. ¿Pero recursivo? Un repollo fractal habría funcionado muy bien, pero no veo auto-similitudes en esta flor.
Clément
1
Bueno, es un poco burlón, pero es un ejemplo de filotaxis, que se puede describir con la secuencia de Fibonacci, que comúnmente se implementa a través de la recursividad.
Hans Sjunnesson
1
"comúnmente implementado mediante recursividad" no significa necesariamente que la flor lo haga. Quizás lo haga; No soy lo suficientemente biólogo molecular para saberlo, pero sin una explicación sobre cómo funciona, no veo esto como particularmente útil. Votar en contra. Si desea agregar una descripción (sea o no biológicamente precisa, podría brindar información) para acompañarla, reconsideraré con gusto la votación.
lindes
65

¿Qué tal cualquier cosa que involucre una estructura de directorios en el sistema de archivos? Buscar archivos de forma recursiva, eliminar archivos, crear directorios, etc.

Aquí hay una implementación de Java que imprime de forma recursiva el contenido de un directorio y sus subdirectorios.

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());
            }
        }       
    }

}
Adrien Be
fuente
2
Un sistema de archivos proporciona motivación (lo cual es bueno, gracias) pero este es un ejemplo específico de DFS.
Redfood
4
No obtuve el acrónimo "DFS"; ha pasado un tiempo desde que me senté en un salón de clases.
Matt Dillard
5
búsqueda en profundidad: dfs (nodo) {para cada hijo en el nodo {visita (hijo); }}
Haoest
Para obtener un ejemplo de código simple, consulte, por ejemplo, stackoverflow.com/questions/126756/…
Jonik
¿Hay algún error en este código? ¿No debería reemplazarse getPrivateDirectoryContent () por getDirectoryContent ()?
Shn_Android_Dev
16

El ejemplo de Matt Dillard es bueno. De manera más general, cualquier caminata sobre un árbol puede manejarse por recursividad muy fácilmente. Por ejemplo, compilar árboles de análisis, caminar sobre XML o HTML, etc.

Cody Brocious
fuente
Encuentro que esta "compilación de árboles de análisis sintáctico" es una respuesta sensata, que involucra árboles pero aún no es un problema de búsqueda, como quería el autor de la pregunta. Podría generalizarse a alguna noción general de compilación o interpretación de un idioma. También podría ser "interpretar" (comprender, escuchar) un idioma natural, por ejemplo, el inglés.
imz - Ivan Zakharyaschev
13

La recursividad es apropiada siempre que un problema se pueda resolver dividiéndolo en subproblemas, que pueden usar el mismo algoritmo para resolverlos. Los algoritmos en árboles y listas ordenadas son un ajuste natural. Muchos problemas de geometría computacional (y juegos 3D) se pueden resolver de forma recursiva utilizando árboles de partición de espacio binario (BSP), subdivisiones gruesas u otras formas de dividir el mundo en subpartes.

La recursividad también es apropiada cuando se intenta garantizar la exactitud de un algoritmo. Dada una función que toma entradas inmutables y devuelve un resultado que es una combinación de llamadas recursivas y no recursivas en las entradas, generalmente es fácil probar que la función es correcta (o no) mediante inducción matemática. A menudo es difícil hacer esto con una función iterativa o con entradas que pueden mutar. Esto puede ser útil cuando se trata de cálculos financieros y otras aplicaciones donde la corrección es muy importante.

Sam
fuente
11

Seguramente muchos compiladores usan la recursividad en gran medida. Los lenguajes de computadora son inherentemente recursivos en sí mismos (es decir, puede incrustar declaraciones 'if' dentro de otras declaraciones 'if', etc.).

Martin Cote
fuente
Las declaraciones if integradas no son recursivas.
John Meagher
Pero analizarlos requiere recursividad, John.
Apocalisp
2
John, el hecho de que pueda anidar declaraciones if significa que la definición del idioma (y probablemente el analizador de idiomas) es recursiva.
Derek Park
El descenso recursivo es una de las formas más fáciles de codificar manualmente un compilador. No es tan fácil como usar una herramienta como yacc, pero es más fácil entender cómo funciona. Se pueden explicar todas las máquinas de estado controladas por tablas, pero generalmente terminan siendo cajas negras.
Eclipse
La respuesta de Cody Brocious que menciona "compilar árboles de análisis sintáctico" también apuntó a esta área: análisis / interpretación / compilación del lenguaje.
imz - Ivan Zakharyaschev
9

Desactivación / configuración de solo lectura para todos los controles secundarios en un control de contenedor. Necesitaba hacer esto porque algunos de los controles secundarios eran contenedores.

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);
    }
}
chitza
fuente
8

Famoso ciclo de evaluación / aplicación de SICP

texto alternativo
(fuente: mit.edu )

Aquí está la definición 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))))

Aquí está la definición 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))))

Aquí está la definición de secuencia de evaluación:

(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

jfs
fuente
7

La recursividad se usa en cosas como árboles BSP para la detección de colisiones en el desarrollo de juegos (y otras áreas similares).

marca
fuente
7

La gente suele clasificar pilas de documentos mediante un método recursivo. Por ejemplo, imagine que está clasificando 100 documentos con nombres. Primero coloque los documentos en pilas por la primera letra, luego clasifique cada pila.

La búsqueda de palabras en el diccionario a menudo se realiza mediante una técnica de búsqueda binaria, que es recursiva.

En las organizaciones, los jefes a menudo dan órdenes a los jefes de departamento, quienes a su vez dan órdenes a los gerentes, etc.

tkerwin
fuente
5

Requisito del mundo real que obtuve recientemente:

Requisito A: implemente esta función después de comprender a fondo el requisito A.

continuar
fuente
4

Los analizadores y compiladores pueden escribirse en un método de descenso recursivo. No es la mejor manera de hacerlo, ya que herramientas como lex / yacc generan analizadores sintácticos más rápidos y eficientes, pero conceptualmente simples y fáciles de implementar, por lo que siguen siendo comunes.

davenpcj
fuente
4

La recursividad se aplica a problemas (situaciones) en las que puede dividirla (reducirla) en partes más pequeñas, y cada parte se ve similar al problema original.

Buenos ejemplos de dónde están las cosas que contienen partes más pequeñas similares a sí mismas:

  • estructura de árbol (una rama es como un árbol)
  • listas (parte de una lista sigue siendo una lista)
  • contenedores (muñecas rusas)
  • secuencias (parte de una secuencia se parece a la siguiente)
  • grupos de objetos (un subgrupo sigue siendo un grupo de objetos)

La recursividad es una técnica para seguir dividiendo el problema en trozos cada vez más pequeños, hasta que uno de esos trozos se vuelve lo suficientemente pequeño como para ser un pedazo de pastel. Por supuesto, después de dividirlos, debe "unir" los resultados en el orden correcto para formar una solución total de su problema original.

Algunos algoritmos de clasificación recursiva, algoritmos de caminata por árboles, algoritmos de mapa / reducción, divide y vencerás son todos ejemplos de esta técnica.

En programación de computadoras, la mayoría de los lenguajes de devolución de llamadas basados ​​en pilas ya tienen las capacidades integradas para la recursividad: es decir,

  • dividir el problema en partes más pequeñas ==> llamarse a sí mismo en un subconjunto más pequeño de los datos originales),
  • realizar un seguimiento de cómo se dividen las piezas ==> pila de llamadas,
  • coser los resultados de nuevo ==> retorno basado en la pila
Stephen Chung
fuente
4

Tengo un sistema que usa recursividad de cola pura en algunos lugares para simular una máquina de estado.

Peter Mortensen
fuente
4

Algunos buenos ejemplos de recursividad se encuentran en lenguajes de programación funcionales . En los lenguajes de programación funcional ( Erlang , Haskell , ML / OCaml / F # , etc.), es muy común que cualquier procesamiento de listas utilice la recursividad.

Cuando se trata de listas en lenguajes típicos imperativos de estilo OOP, es muy común ver listas implementadas como listas enlazadas ([item1 -> item2 -> item3 -> item4]). Sin embargo, en algunos lenguajes de programación funcional, encontrará que las listas en sí mismas se implementan de forma recursiva, donde la "cabeza" de la lista apunta al primer elemento de la lista y la "cola" apunta a una lista que contiene el resto de los elementos ( [elemento1 -> [elemento2 -> [elemento3 -> [elemento4 -> []]]]]). Es bastante creativo en mi opinión.

Este manejo de listas, cuando se combina con la coincidencia de patrones, es MUY poderoso. Digamos que quiero sumar una lista de números:

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

Esto esencialmente dice "si fuimos llamados con una lista vacía, devuelve 0" (lo que nos permite romper la recursividad), de lo contrario devuelve el valor de head + el valor de Sum llamado con los elementos restantes (de ahí, nuestra recursividad).

Por ejemplo, podría tener una lista de URL , creo que dividir todas las URL a las que enlaza cada URL y luego reducir la cantidad total de enlaces hacia / desde todas las URL para generar "valores" para una página (un enfoque que Google toma con PageRank y que puede encontrar definido en el documento MapReduce original ). También puede hacer esto para generar recuentos de palabras en un documento. Y muchas, muchas, muchas otras cosas también.

Puede extender este patrón funcional a cualquier tipo de código MapReduce donde puede tomar una lista de algo, transformarlo y devolver algo más (ya sea otra lista o algún comando zip en la lista).

Jason Olson
fuente
3

XML, o atravesar cualquier cosa que sea un árbol. Aunque, para ser honesto, casi nunca uso la recursividad en mi trabajo.

Charles Graham
fuente
¿Ni siquiera la recursividad de la cola?
Apocalisp
Usé la recursividad una vez en mi carrera, y cuando el marco cambió, me deshice de él. El 80% de lo que hacemos es CRUD.
Charles Graham
1
Mencionar "XML" en primer lugar es bastante extraño. No es algo natural, no es algo con lo que una persona normal a la que vas a enseñar tenga que lidiar en la vida diaria. Pero, por supuesto, la idea es bastante sensata.
imz - Ivan Zakharyaschev
3

Bucles de retroalimentación en una organización jerárquica.

El jefe superior les dice a los altos ejecutivos que recopilen comentarios de todos en la empresa.

Cada ejecutivo reúne a sus subordinados directos y les dice que recopilen comentarios de sus subordinados directos.

Y en la línea.

Las personas que no tienen subordinados directos (los nodos hoja del árbol) dan su opinión.

La retroalimentación viaja por el árbol y cada gerente agrega su propia retroalimentación.

Eventualmente, todos los comentarios regresan al jefe superior.

Esta es la solución natural porque el método recursivo permite filtrar en cada nivel: la recopilación de duplicados y la eliminación de comentarios ofensivos. El jefe superior podría enviar un correo electrónico global y hacer que cada empleado le informe directamente de sus comentarios, pero existen los problemas de "no puedes manejar la verdad" y "estás despedido", por lo que la recursividad funciona mejor aquí.

marca
fuente
2

Suponga que está creando un CMS para un sitio web, donde sus páginas están en una estructura de árbol, con la raíz como la página de inicio.

Suponga también que su {usuario | cliente | cliente | jefe} solicita que coloque una ruta de navegación en cada página para mostrar dónde se encuentra en el árbol.

Para cualquier página n dada, es posible que desee caminar hasta el padre de n, y su padre, y así sucesivamente, de forma recursiva para construir una lista de nodos hasta la raíz del árbol de la página.

Por supuesto, está presionando la base de datos varias veces por página en ese ejemplo, por lo que es posible que desee usar algunos alias de SQL donde busque page-table como a, y page-table nuevamente como b, y unir a.id con b.parent para que la base de datos realice las uniones recursivas. Ha pasado un tiempo, por lo que mi sintaxis probablemente no sea útil.

Por otra parte, es posible que solo desee calcular esto una vez y almacenarlo con el registro de página, actualizándolo solo si mueve la página. Probablemente sería más eficiente.

De todos modos, ese es mi $ .02

Sam McAfee
fuente
2

Tiene un árbol de organización que tiene N niveles de profundidad. Varios de los nodos están marcados y desea expandirlos solo a los nodos que se han verificado.

Esto es algo que realmente codifiqué. Es agradable y fácil con la recursividad.

MagicKat
fuente
2

En mi trabajo tenemos un sistema con una estructura de datos genérica que se puede describir como un árbol. Eso significa que la recursividad es una técnica muy eficaz para trabajar con los datos.

Resolverlo sin recursividad requeriría mucho código innecesario. El problema con la recursividad es que no es fácil seguir lo que sucede. Realmente tienes que concentrarte al seguir el flujo de ejecución. Pero cuando funciona, el código es elegante y eficaz.

Tommy
fuente
2

Cálculos para finanzas / física, como promedios compuestos.

Apocalipsis
fuente
2
  • Analizando un archivo XML .
  • Búsqueda eficiente en espacios multidimensionales. P.ej. quad-trees en 2D, oct-trees en 3D, kd-trees, etc.
  • Agrupación jerárquica.
  • Ahora que lo pienso, atravesar cualquier estructura jerárquica se presta naturalmente a la recursividad.
  • Plantilla de metaprogramación en C ++, donde no hay bucles y la recursividad es la única forma.
Dima
fuente
"XML" no es esencial para la idea de esta respuesta (y mencionar específicamente XML puede ser repugnante / aburrido para las personas a las que está enseñando). Cualquier lenguaje típico (un lenguaje de computadora o uno natural) sería un ejemplo de un problema de análisis recursivo.
imz - Ivan Zakharyaschev
El cartel pedía "problemas del mundo real donde un enfoque recursivo es la solución natural". Analizar un archivo xml es sin duda un problema del mundo real y, naturalmente, se presta a la recursividad. El hecho de que parezca tener una extraña aversión al XML no cambia el hecho de que se usa ampliamente.
Dima
2

El mejor ejemplo que conozco es el ordenamiento rápido , es mucho más simple con la recursividad. Echa un vistazo a:

shop.oreilly.com/product/9780596510046.do

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

(Haga clic en el primer subtítulo debajo del capítulo 3: "El código más hermoso que he escrito").

Fabio Ceconello
fuente
1
Y MergeSort también es más simple con la recursividad.
Matthew Schinckel
1
El vínculo está roto. ¿Puedes agregar el título del libro?
Peter Mortensen
@PeterMortensen, el libro es Beautiful Code de Greg Wilson y Andy Oram. Actualicé el enlace, aunque parece que O'Reilly ya no permite mirar adentro. Pero puedes echar un vistazo a Amazon.
Fabio Ceconello
1

Las empresas de telefonía y cable mantienen un modelo de su topología de cableado, que de hecho es una gran red o gráfico. La recursividad es una forma de recorrer este modelo cuando desea encontrar todos los elementos principales o secundarios.

Dado que la recursividad es costosa desde la perspectiva del procesamiento y la memoria, este paso normalmente solo se realiza cuando se cambia la topología y el resultado se almacena en un formato de lista preordenado modificado.

Steve Moyer
fuente
1

El razonamiento inductivo, el proceso de formación de conceptos, es de naturaleza recursiva. Tu cerebro lo hace todo el tiempo, en el mundo real.

Apocalipsis
fuente
1

Lo mismo ocurre con el comentario sobre los compiladores. Los nodos del árbol de sintaxis abstracta se prestan naturalmente a la recursividad. Todas las estructuras de datos recursivas (listas vinculadas, árboles, gráficos, etc.) también se manejan más fácilmente con la recursividad. Creo que la mayoría de nosotros no usamos mucho la recursividad una vez que salimos de la escuela debido a los tipos de problemas del mundo real, pero es bueno tenerlo en cuenta como una opción.

origen
fuente
1

La multiplicación de números naturales es un ejemplo real de recursividad:

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
Apocalipsis
fuente