Una relación de recurrencia permite calcular la cantidad de configuraciones de tamaño n a partir de conteos de tamaños menores.
En el tema anterior introdujimos la recurrencia. Ahora construiremos relaciones a partir de problemas concretos de conteo.
La técnica consiste en observar cómo puede terminar una configuración, clasificar esos casos y expresar cada grupo mediante un problema más pequeño.
Una relación de recurrencia puede tener la forma:
Los coeficientes indican cuántas decisiones o variantes producen cada tipo de caso menor.
Una fila de n posiciones puede cubrirse con piezas de longitud 1 o 2. Al observar la última pieza:
El primer término representa una última pieza de longitud 1 y el segundo una última pieza de longitud 2.
Elige un modelo combinatorio y observa cómo se construyen sus valores para distintos tamaños.
Sea B(n) la cantidad de cadenas binarias de longitud n que no contienen dos unos consecutivos. Si la cadena termina en 0, el prefijo puede ser cualquiera; si termina en 1, el símbolo anterior debe ser 0.
Una forma de expresar el conteo es:
Si un recorrido puede avanzar 1, 2 o 3 posiciones, la última decisión produce tres tamaños menores:
Los casos base dependen de cómo definamos el recorrido vacío y las posiciones iniciales.
Esta función calcula la cantidad de coberturas con piezas de tamaño 1 o 2.
function contarCoberturas(n) {
const valores = [1, 1];
for (let posicion = 2; posicion <= n; posicion += 1) {
valores[posicion] = valores[posicion - 1] + valores[posicion - 2];
}
return valores[n];
}
console.log(contarCoberturas(8));
La clasificación por el último paso evita contar una misma configuración en dos categorías. Cada configuración tiene una última pieza, símbolo o movimiento bien definido.
Los grupos deben ser excluyentes y cubrir todas las formas de terminar.
Si existen varias formas distintas de realizar una misma última etapa, aparece un coeficiente:
El término 2an-1 indica que cada configuración de tamaño n-1 puede extenderse de dos maneras diferentes.
Una implementación recursiva expresa la definición de forma natural, pero puede repetir los mismos cálculos. La versión iterativa guarda los valores ya obtenidos.
Para comprobar una recurrencia podemos generar algunos casos pequeños mediante enumeración y comparar los valores con la relación propuesta.
function verificarRecurrencia(valores) {
for (let indice = 2; indice < valores.length; indice += 1) {
const esperado = valores[indice - 1] + valores[indice - 2];
if (valores[indice] !== esperado) return false;
}
return true;
}
console.log(verificarRecurrencia([1, 1, 2, 3, 5, 8]));
Las relaciones de recurrencia convierten problemas de conteo en una secuencia de decisiones más pequeñas. Analizar la última etapa permite construir fórmulas que luego pueden implementarse con recursión, iteración o programación dinámica.
En el próximo tema estudiaremos sucesiones combinatorias y sus patrones de crecimiento.