Algoritmos Recursivos
Conceptos base de recursión, seguimiento mediante pila, ejemplos clásicos y clasificación inicial de recurrencias.
11. Algoritmos Recursivos
Módulos 1–3: complejidad temporal, operaciones elementales, ciclos iterativos y conocimiento de pilas.
Un algoritmo recursivo es aquel que se llama a sí mismo como parte de su solución. A diferencia de los iterativos —que se analizan línea por línea—, los recursivos se siguen a través de ambientes de ejecución, también llamados marcos de pila.
La recursión está en la naturaleza: las muñecas rusas, los fractales, la definición del factorial. Su poder está en expresar soluciones complejas de forma elegante y concisa.
11.1 Ambientes de ejecución y la pila
Cada vez que un algoritmo recursivo se llama a sí mismo, el sistema crea un nuevo ambiente de ejecución y lo apila sobre los anteriores.
En ese ambiente se guardan:
- Todos los parámetros recibidos en esa llamada.
- Todas las variables locales declaradas en esa ejecución.
- La dirección de retorno: el punto exacto del código al que hay que volver cuando esa llamada termine.
Mientras el ambiente no termine, todos los ambientes anteriores quedan suspendidos esperando en la pila. Por eso la recursión tiene un costo espacial relevante: cada ambiente consume memoria.
El número de casos base debe ser IGUAL al número de llamados en el caso general. Si el caso general hace 2 llamados recursivos, se necesitan al menos 2 casos base. Si hace 3, se necesitan 3.
11.2 Estructura de un algoritmo recursivo
Todo algoritmo recursivo correcto tiene exactamente dos partes:
Estructura obligatoria de un algoritmo recursivo
Es la condición de parada. Se resuelve directamente, sin llamado recursivo. Su costo en la ecuación de recurrencia siempre es una constante.
Es la situación donde el problema se reduce a versiones más pequeñas de sí mismo mediante llamados recursivos. Aquí está la lógica de la reducción.
11.3 Ejemplo: Fibonacci
Fibonacci es la suma de los dos anteriores: . En Fibonacci, se requieren dos casos base no porque haya dos llamadas recursivas, sino porque la fórmula depende de dos valores iniciales.
Pseudocódigo
int Fib(E int n) {
if (n=0) or (n=1) then
f <- n # caso base: Fib(0)=0, Fib(1)=1
else
f <- Fib(n-1) + Fib(n-2) # caso general
endif
return f
}Seguimiento con : el algoritmo primero resuelve (abre un ambiente), dentro de ese resuelve , y así hasta llegar a los casos base. El de cada nivel queda pendiente hasta que el izquierdo termine.
Diagrama en Mermaid — Fib(5)
Fibonacci sin optimización recalcula los mismos subproblemas muchas veces. calcula dos veces. es enormemente ineficiente. Esto motiva la Programación Dinámica (Módulo 13).
Ecuación de recurrencia
11.4 Ejemplo: Torres de Hanói
Tres torres (origen, auxiliar, destino) y discos apilados de mayor a menor en el origen.
Mover un disco a la vez, nunca poner uno más grande sobre uno más pequeño.
Estrategia recursiva: para mover discos del origen al destino:
- Mover los discos superiores del origen al auxiliar, usando destino como apoyo.
- Mover el disco más grande del origen al destino.
- Mover los discos del auxiliar al destino, usando origen como apoyo.
Pseudocódigo
TH(E int n, E char o, E char a, E char d) {
if (n = 1) then
Print('mover disco del origen al destino') # caso base
else
TH(n-1, o, d, a) # mueve n-1 al auxiliar
Print('mover disco del origen al destino')
TH(n-1, a, o, d) # mueve n-1 al destino
endif
}Mermaid — Torres de Hanói con n=4
Árbol de recursión: cada nivel del árbol tiene el doble de nodos que el anterior. Nivel 1 → , Nivel 2 → , Nivel 3 → , Nivel → . Cada nodo cuesta una constante: un movimiento.
Esta ecuación es de tipo 'Resta y serás vencido': con . El costo crece exponencialmente porque cada nivel duplica el número de llamadas.
11.5 Clasificación de ecuaciones de recurrencia
Antes de elegir el método de resolución, hay que identificar qué tipo de ecuación tenemos:
Clasificación de ecuaciones de recurrencia
| Tipo | Forma | Identificador | Ejemplo |
|---|---|---|---|
| Divide y vencerás | División en el llamado | Merge Sort, Búsqueda Binaria | |
| Resta y vencerás | Resta en el llamado, | Búsqueda secuencial recursiva | |
| Resta y serás vencido | Resta en el llamado, | Torres de Hanói, Fibonacci |
Toda ecuación de recurrencia DEBE tener caso general Y caso base. Sin caso base no hay parada. El caso base siempre tiene costo constante: , .
11.6 Recursión de cola
Una recursión es de cola cuando el llamado recursivo es lo último que ocurre en el caso general. En ese caso, el sistema puede convertirla en iteración automáticamente o manualmente porque no hay nada pendiente después del llamado.
Si un algoritmo recursivo es de cola, siempre conviene convertirlo a iterativo: misma lógica, costo espacial en lugar de por la pila.