Recursión: ¿es "divide y vencerás" o "reutilización de código"?

11

La recursión , como todos sabemos, es uno de esos problemas, que envolver la cabeza se siente como lograr un "hito" en su viaje de programación.

Pero cuando se trata de usarlo realmente en problemas del mundo real, conocer la mecánica de la recursión NO es suficiente, también se debe comprender la naturaleza de los problemas en los que la recursión es la solución más adecuada.

Así que mi pregunta es esta...

  • ¿Cuáles son los "patrones problemáticos" que requieren la solución de la recursividad?
  • es la recurrencia una forma de estrategia de "divide y vencerás" o una forma de "reutilización de código", o es un patrón de diseño en sí mismo
  • ¿Puede darnos un ejemplo de un problema del mundo real en el que la recursión viene a la mente como una solución inmediata?

- ACTUALIZACIÓN -

muchas respuestas se refieren a "problemas reales" como el desplazamiento de árboles, factorial, etc. Preferiría "los problemas reales REALES" - déjenme darles un ejemplo ...

Teníamos una GRAN tirada de texto (aproximadamente 30 MB de texto como una lista vinculada de structs), y necesitábamos hacer un índice para la búsqueda de texto completo. Necesitábamos mantener todo el índice en la memoria y volver a indexar el texto cada 10 minutos.

Cada 10 minutos, compararíamos todo el texto (dos listas vinculadas, línea por línea) con un fragmento de texto recién generado, para ver qué línea se cambió, y luego volveríamos a indexar solo esa línea, de esa manera podríamos evitar tener que volver a indexar TODO el texto. Recuerde: necesitábamos encontrar los puntos de diferencia entre dos listas vinculadas de 30 MB.

A uno de mis colegas se le ocurrió un programa fantástico que utilizaba la recursión PESADA para comparar las líneas, y luego recolectaba las posiciones en las que los mandriles diferían en una matriz. Sí, sé que suena desconcertante. lo hizo.

El punto es: ¿cómo podría ver que este problema podría resolverse de manera inteligente con un uso intensivo de la recursividad?

codificador de árboles
fuente
¿Son 30 MB realmente grandes en estos días donde la mayoría de las computadoras tienen GB de RAM y TB de espacio en el disco duro?
JB King
Puede que 30 MB NO sean grandes, pero teniendo en cuenta el tipo de estructura de datos en el que nuestro texto estaba repleto, fue realmente una GRAN tirada de texto para PROCESAR, y DIFF.
treecoder
3
¿No es "REAL atravesar una estructura de carpetas" lo suficientemente REAL? Y no veo por completo, en su ejemplo, cómo la recursión no debe ser intuitiva aquí, y por qué su uso debería ser particularmente notable. Su colega diseñó un algoritmo recursivo, como cualquier otro algoritmo. También podría preguntar cómo Hoare tuvo la idea de resolver el problema de clasificación de forma recursiva.
Konrad Rudolph
2
¿Estoy en lo cierto al pensar que se refería a "reutilización de código" más que a "ejecutar la misma serie de operaciones un número indeterminado de veces"? Esto es opuesto a la "reutilización de código" en el sentido de escribir código genérico para usarlo en otros lugares.
Andy Hunt
44
Pero el recorrido del árbol es un "verdadero problema real", que muchas personas encuentran casi a diario.
Falcon

Respuestas:

16
  • ¿Cuáles son los "patrones problemáticos" que requieren la solución de la recursividad?

No diría que existe un patrón de problema para el uso de la recursividad. Cada función que se puede implementar con recursividad también se puede implementar de forma iterativa, a menudo presionando y haciendo estallar una pila.

Es una cuestión de expresión y también de rendimiento. Los algoritmos iterativos a menudo tienen un mejor rendimiento y son más fáciles de optimizar. Sin embargo, los algoritmos recursivos se benefician de una expresión más clara y, por lo tanto, a menudo son más fáciles de leer, comprender e implementar.

Algunas cosas incluso no se pueden expresar sin recurrencia, por ejemplo, listas infinitas. Los llamados lenguajes funcionales dependen en gran medida de la recursividad, ya que es su forma natural de expresión. El dicho es: "La programación recursiva es una programación funcional bien hecha".

  • es la recurrencia una forma de estrategia de "divide y vencerás" o una forma de "reutilización de código", o es un patrón de diseño en sí mismo

No lo llamaría un patrón de diseño. Es una cuestión de expresión. A veces, una expresión recursiva es simplemente más poderosa y más expresiva y, por lo tanto, conduce a un código mejor y más limpio.

  • ¿Puede darnos un ejemplo de un problema del mundo real en el que la recursión viene a la mente como una solución inmediata?

Cualquier cosa que necesite atravesar árboles se expresará adecuadamente mediante un algoritmo recursivo.

Halcón
fuente
77
"Cada función que se puede implementar con recursión también se puede implementar de forma iterativa, a menudo presionando y haciendo estallar una pila". Después de todo, en los idiomas que utilizan memoria basada en la pila, ya está presionando y haciendo estallar los datos de la función dentro y fuera de la pila cuando usa la recursividad.
JAB
Solo si compila o interpreta el lenguaje por una máquina ;-) Además, desde un punto de vista muy elevado, la expresión y el lenguaje son completamente independientes de la máquina y el hardware y el sistema operativo, por lo tanto, no necesariamente hay una pila.
Falcon
Ah, sí, tienes toda la razón. Debería haber dicho "en implementaciones de idiomas / compiladores de idiomas que utilizan memoria basada en pila".
JAB
En general, también tienes razón. No quería parecer quisquilloso.
Falcon
2
Se pueden expresar listas infinitas sin recurrencia, al menos sin una implementación recursiva. Los generadores de Python pueden hacerlo, al igual que los generadores en Icon de los que Python parece haber tomado prestada la idea. Creo que F # puede hacer este truco, aunque no estoy seguro. Básicamente, los generadores son un caso especial de co-rutinas (como la multitarea cooperativa) que son muy adecuadas para implementar listas perezosas. Cada vez que un generador "produce" un resultado, la persona que llama recupera el control y el generador permanece inactivo hasta que se solicite el siguiente resultado.
Steve314
8

es la recurrencia una forma de estrategia de "divide y vencerás" o una forma de "reutilización de código", o es un patrón de diseño en sí mismo

Ninguno. Divide y vencerás utiliza la recursividad. Pero la recursión no necesariamente divide y vencerás, ya que esto último significa dividir un problema en dos (o más) partes y resolver cada una de ellas simétricamente. En la recursión, no haces esto.

La reutilización del código no tiene ninguna relación, y un patrón de diseño entra en juego en un nivel mucho más alto. Por ejemplo, incluso dividir y conquistar (también un patrón de nivel más alto que la recursividad) todavía no se considera un patrón de diseño, sino que es un patrón algorítmico .

¿Puede darnos un ejemplo de un problema del mundo real en el que la recursión viene a la mente como una solución inmediata?

Transversal del árbol. O, más generalmente, el recorrido transversal del gráfico. Esto incluye notablemente atravesar una estructura de carpetas.

Y, por supuesto, cualquier cosa que use la división y la conquista o la programación dinámica, ya que ambas se expresan naturalmente en términos de recursividad.

Konrad Rudolph
fuente
La programación dinámica no siempre se expresa naturalmente como recursividad. De hecho, la programación dinámica se refiere estrictamente a los enfoques tabulares, excluyendo la memorización. Las opiniones parecen variar sobre esto, pero la "programación" en "programación dinámica" es en realidad un término matemático, que se refiere a los enfoques tabulares (un truco que aprendí del curso de algoritmos de software de código abierto del MIT). Entonces, estrictamente, la programación dinámica explota la subestructura óptima utilizando lo que a menudo se expresa más fácilmente como un bucle simple. Es mucho más probable que la memorización implique recursión, pero no necesariamente.
Steve314
1
@ Steve314 Estoy de acuerdo en que la implementación práctica de DP (ya sea en un programa de computadora o de forma manual) rara vez utiliza la recursividad. Pero la idea se basa inherentemente en una relación de recurrencia, ¡que es solo una fórmula recursiva! - Y un caso base.
Konrad Rudolph
Estoy de acuerdo en que la "subestructura óptima" (una solución óptima tiene soluciones parciales óptimas) es una idea recursiva. Esa es una visión matemática / informática de la recursividad, que no se relaciona directamente con la implementación, pero el papel de la recursividad en la informática es un punto importante a destacar. Pocos algoritmos (y probablemente ningún patrón de diseño) son herramientas importantes en informática; la mayoría son puramente temas para estudiar en lugar de herramientas para estudiar en otra cosa.
Steve314
4
what are the "problem patterns" that call for the solution of recursion

Derivado de la auto-similitud de los fractales, yo diría que la auto-igualdad o la auto-identidad (o como se llame) es un patrón de problema típico para la recursividad. Es decir, un problema puede dividirse en subproblemas que tienen la misma estructura que el problema principal.

En el recorrido del árbol mencionado, cada árbol secundario es un árbol completo en sí mismo, al igual que el árbol principal, y el árbol principal puede ser un árbol secundario dentro de otro árbol.

Así que supongo que su colega descubrió las propiedades de auto-igualdad de su problema de indexación. O fue al revés y transformó el problema en una representación auto-igualitaria.

Seguro
fuente
1
+1 para "un problema puede dividirse en
subproblemas
+1 y parafraseando: donde la solución del problema se aplica a las capas secundarias. Mi ejemplo en el mundo real es encontrar cargos de tarjeta de crédito que contribuyen a un "lote". El software de contabilidad tendrá los cargos individuales y el depósito por lotes en la cuenta corriente. Mi caso podría convertirse en una pregunta aquí ya que stackoverflow no era demasiado claro al respecto. stackoverflow.com/questions/14719806
Chris K
3

Bueno, la recursividad se puede entender fácilmente si uno intenta transformar los bucles imperativos en funciones funcionales. De todos modos, intentemos dar respuestas a todas las preguntas:

¿Cuáles son los "patrones problemáticos" que requieren la solución de la recursividad?

Si tiene una estructura o algoritmo similar a un árbol, necesitará recurrencia. Si su código imperativo trata con una pila, necesitará recurrencia. Si un cierto paso en su algoritmo depende de pasos anteriores (think loops), necesita recurrencia. La necesidad aquí debe ser interpretada como puede usar.

es la recurrencia una forma de estrategia de "divide y vencerás" o una forma de "reutilización de código", o es un patrón de diseño en sí mismo

Ninguna. Dividir y conquistar utiliza la recursividad, pero se puede implementar con pilas. La reutilización del código se refiere a otra cosa. Los patrones de diseño son más complicados que la simple recursión.

¿Puede darnos un ejemplo de un problema del mundo real en el que la recursión viene a la mente como una solución inmediata?

Análisis y todo lo que se refiere a estructuras de árboles. Incluso estructuras arbóreas implícitas.

Mihai Maruseac
fuente
3

Si hay una manera de simplificarlo para que sea fácil, esa puede ser la pista de que la recursión podría funcionar. Podrías ordenar y buscar ejemplos donde existan soluciones recursivas como Merge Sort y Binary Search respectivamente.

Algo a tener en cuenta es cómo algunos problemas pueden resolverse mal con la recursividad como un factorial.

En cuanto a un ejemplo del mundo real en el que usaría la recursividad, buscar un libro en un estante de libros se puede hacer de manera bastante recursiva. Solo miro el libro y, si no es lo que quiero, paso al siguiente. Me detengo cuando encuentro el libro o llego al final de la fila. El bucle de consultar un libro y pasar al siguiente podría hacerse recursivamente. Quizás este es un ejemplo demasiado real.

JB King
fuente
2

¿Cuáles son los "patrones problemáticos" que requieren la solución de la recursividad?

En términos muy generales, se requiere recursividad cuando se resuelve un problema donde f (x) = f (g (x)) . A menos que esté de acuerdo con la recursión infinita, g (x) no debería evaluar a x .

es la recurrencia una forma de estrategia de "divide y vencerás" o una forma de "reutilización de código", o es un patrón de diseño en sí mismo

Ninguna de las anteriores. Es solo una forma de hacer lo mismo repetidamente, a veces basado en variaciones en la entrada. El concepto ha existido mucho más tiempo que los patrones de diseño, la reutilización de código o incluso las computadoras, para el caso.

¿Puede darnos un ejemplo de un problema del mundo real en el que la recursión viene a la mente como una solución inmediata?

Los factores factoriales, la secuencia de Fibonacci, el recorrido del árbol y muchos otros problemas se pueden resolver con recusrion. La recursión en el sentido de una función que se llama a sí misma no es necesariamente la mejor manera de implementar ese tipo de cosas; Hay otras formas de lograr el mismo efecto (por ejemplo, una pila y un bucle) que podrían ser más deseables.

Blrfl
fuente
-1

Cuando escribe un algoritmo recursivo, generalmente traduce una definición recursiva del problema al código. Entonces ni siquiera necesita saber cómo se ejecutará.

Es lo que sucede en la programación funcional. De hecho, especifica qué (definición) en lugar de cómo . En otras palabras, usted define la base y luego define su problema en el término de un subproblema.

Por ejemplo, considere el Factorialalgoritmo

  • Definir la base: Factorial (1) = 1;
  • Definir Factorial n: Factorial (n) = n * Factorial (n-1);

Luego, cuando encuentre un problema, debe pensar si puede definirlo recursivamente o no, si puede definirlo recursivamente, casi lo ha resuelto.

Sin embargo, cualquier función recursiva no debería ser una definición recursiva. Puede definir la base y relacionar (definir) la solución del problema principal con la solución (definición) de subproblemas. Pero para esta relación es posible que necesite un procedimiento.

Un ejemplo es el MergeSortque mergepodría ser un procedimiento imperativo para relacionar la definición o la solución de ordenar toda la matriz con la clase de las sub-matrices.

Ahmad
fuente