La clave de la recursión es que algunos problemas pueden resolverse resolviendo primero una versión más simple del mismo problema, y luego manipulando un poco esa respuesta.
La recursión es natural para algunos problemas, pero no tan buena para otros. Desafortunadamente para ti, este problema y el problema anterior que preguntaste sobre las orejas de conejo no encajan bien. Ambos pueden resolverse de manera más fácil y obvia mediante bucles simples.
Pero tomemos un ejemplo diferente: ordenar.
- ¿Qué es un algoritmo para emparejar 18 personas en los 816 grupos posibles de 3 con 6 grupos a la vez?
- ¿Cómo encuentras la distancia entre dos lugares, sin usar los mapas de Google?
- ¿Cuál es el mejor algoritmo para calcular la cantidad de números primos?
- ¿Cómo puedo aleatorizar la matriz almacenada y luego usarla como entrada?
- ¿Qué es la matriz? Por favor explique los detalles.
Una forma general de abordar la idea de ordenar una lista de elementos es dividir la lista en dos listas más cortas, clasificando ambas listas más cortas y luego recombinando las dos listas más cortas. Terminas con un método que se parece a esto (en algo similar a ML):
recursiveSort [] = []
recursiveSort [a] = [a]
recursiveSort list =
let val (izquierda, derecha) = dividir (lista)
val leftSorted = recursiveSort left
val rightSorted = recursiveSort right
en
combinar (leftSorted, rightSorted)
fin
Eso dice “Al ordenar de forma recursiva una lista vacía, simplemente devuelva una lista vacía. Al ordenar de forma recursiva una lista de un elemento, simplemente devuelva una lista de ese elemento. Al ordenar recursivamente cualquier otra cosa (que el sistema de tipos limita como una lista ), divide la lista en izquierda y derecha, ordena las dos mitades de forma recursiva y luego combina las mitades ordenadas “.
Este es un patrón típico para funciones recursivas: defina los casos base (en este caso, listas cortas de 0 o 1 elementos), divida el problema en problemas más simples (en este caso, divídalos en dos listas), resuelva los problemas más simples de forma recursiva, luego combine las soluciones más simples.
El truco para resolver problemas por recursividad es primero identificar cuáles son los casos más simples, luego descubrir cómo combinarlos y luego resolver los casos base.
Por ejemplo, dado su ejemplo sumDigits, una forma de dividirlo es reconocer que para sumar los dígitos de un número [math] k [/ math] -digit [math] n [/ math], puede sumar la suma de un [matemático] (k-1) [/ matemático] número de dígitos [matemático] n / 10 [/ matemático] y un número de 1 dígito [matemático] n% 10 [/ matemático].
Esto te da algo como:
sumDigits (n) = si n <10
entonces n
de lo contrario sumDigits (n / 10) + sumDigits (n% 10)
En realidad, esto puede simplificarse observando que n%10 < 10
, por lo que sumDigits(n%10) == n%10
siempre. Esto significa que puede cambiarlo a:
sumDigits (n) = si n <10
entonces n
de lo contrario sumDigits (n / 10) + n% 10
También puede observar que si n <10, entonces n / 10 == 0 yn% 10 == n, entonces si n < 10
, sumDigits(n) == n == sumDigits(0) + n == sumDigits(n/10) + sumDigits(n%10)
, lo que significa que n==0
es el único caso base real:
sumDigits (0) = 0
sumDigits (n) = sumDigits (n / 10) + n% 10
que es un buen resultado simple Lástima que Java sea una mierda para expresar resultados tan simples …