Atlasingeniería

Diseño de algoritmosTécnicas de diseñoTema 1

Divide y vencerás y teorema maestro

Partir el problema, resolver las partes y combinar. El teorema maestro dice, sin resolver la recurrencia, cuál de los tres costos manda: dividir, combinar o el trabajo de las hojas.

Para este tema conviene tener claro:Mergesort y quicksort

Mergesort, la búsqueda binaria y la multiplicación rápida de enteros comparten una forma: el problema se parte, cada parte se resuelve igual y los resultados se combinan. Lo que cambia entre uno y otro es dónde está el trabajo real, y de eso depende el costo final.

Dividir, resolver y combinar

El esquema tiene tres pasos: dividir la entrada en subproblemas del mismo tipo, resolverlos recursivamente y combinar las soluciones. La recursión corta en un caso base lo bastante chico como para resolver directo.

La condición para que sirva es que los subproblemas sean independientes. Si se pisan entre sí y se recalculan, hace falta memorizar resultados, y eso ya es programación dinámica.

El arreglo entero, sin ordenar. Primer nivel del árbol de llamadas.

1 / 6
Contá los niveles: son log n, y en cada uno se toca cada elemento una vez. Ese producto es el n log n, y se lee del dibujo sin resolver ninguna recurrencia. Fijate también que ninguna rama comparte trabajo con otra: eso es lo que distingue divide y vencerás de programación dinámica.

Antes de seguir, predecí

Mergesort sobre ocho elementos. ¿Cuántos niveles de mezcla hay?

El costo se escribe como recurrencia

El costo se escribe como una recurrencia. Si el problema de tamaño nn se parte en aa subproblemas de tamaño n/bn/b y dividir más combinar cuesta f(n)f(n):

T(n)=aT ⁣(nb)+f(n).T(n)=a\,T\!\left(\frac{n}{b}\right)+f(n).

Mergesort es a=2a=2, b=2b=2, f(n)=Θ(n)f(n)=\Theta(n): dos mitades y una fusión lineal. La búsqueda binaria es a=1a=1, b=2b=2, f(n)=Θ(1)f(n)=\Theta(1): una sola mitad y nada que combinar.

El árbol de llamadas lo explica sin cuentas

La forma de entender la recurrencia es dibujar el árbol de llamadas. Tiene profundidad logbn\log_b n, y en el nivel ii hay aia^i subproblemas de tamaño n/bin/b^i.

La cantidad de hojas es nlogban^{\log_b a}, y ese número es la clave: representa el trabajo que cuesta la recursión pura, sin contar lo que se hace al dividir y combinar. Todo el análisis se reduce a comparar ese trabajo de las hojas contra f(n)f(n).

El teorema maestro y sus tres casos

Eso es el teorema maestro. Comparando f(n)f(n) con nlogban^{\log_b a} hay tres casos:

  • Si f(n)f(n) crece más lento, mandan las hojas: T(n)=Θ ⁣(nlogba)T(n)=\Theta\!\left(n^{\log_b a}\right).
  • Si crecen igual, cada nivel aporta lo mismo y se multiplica por la cantidad de niveles: T(n)=Θ ⁣(nlogbalogn)T(n)=\Theta\!\left(n^{\log_b a}\log n\right).
  • Si f(n)f(n) crece más rápido, manda la raíz: T(n)=Θ(f(n))T(n)=\Theta(f(n)).

Mergesort cae en el segundo caso —nlog22=nn^{\log_2 2}=n contra f(n)=nf(n)=n— y por eso da nlognn\log n.

Los extremos: búsqueda binaria y mergesort

Los casos extremos se ven mejor con ejemplos. La búsqueda binaria tiene una sola hoja por nivel: nlog21=1n^{\log_2 1}=1, y el costo termina siendo Θ(logn)\Theta(\log n), puro camino.

La multiplicación de Karatsuba parte dos números en mitades y, con un truco algebraico, usa tres multiplicaciones en vez de cuatro: a=3a=3, b=2b=2, y el costo baja de n2n^2 a nlog23n1.58n^{\log_2 3}\approx n^{1.58}. Bajar aa de 4 a 3 cambia el exponente: en el primer caso, lo único que importa es cuántos subproblemas hay.

Lo que el teorema no cubre

El teorema no cubre todo. Pide subproblemas del mismo tamaño y un f(n)f(n) razonable; con particiones desparejas —quicksort en su peor caso— hay que analizar a mano o acotar con el árbol de recursión.

Y hay un costo que la recurrencia no muestra: cada llamada usa pila. Con profundidad logarítmica no importa, pero una recursión mal balanceada puede desbordarla mucho antes de que el tiempo sea un problema.

El teorema maestro, y cuándo no alcanza

RecurrenciaCasoResultadoEjemplo
T(n) = 2T(n/2) + O(n)empateO(n log n)mergesort
T(n) = 2T(n/2) + O(1)gana la recursiónO(n)recorrer un árbol binario
T(n) = T(n/2) + O(1)gana la recursiónO(log n)búsqueda binaria
T(n) = 2T(n/2) + O(n²)gana la combinaciónO(n²)un combinar caro
T(n) = 7T(n/2) + O(n²)gana la recursiónO(n^2,81)Strassen
El teorema maestro compara dos fuerzas: cuánto trabajo generan las llamadas y cuánto cuesta combinar. Gana la más grande, y si empatan aparece el factor logarítmico.

La forma corta de usarlo es comparar nlogban^{\log_b a} —el trabajo total de las hojas— con f(n)f(n) —el de combinar—. Si las hojas dominan, el resultado es el de las hojas; si domina la combinación, el resultado es f(n)f(n); si empatan, se multiplica por logn\log n.

Cierre

Divide y vencerás sirve cuando los subproblemas son independientes. Su costo se escribe como aT(n/b)+f(n)a\,T(n/b)+f(n), y el teorema maestro lo resuelve comparando f(n)f(n) con el trabajo de las hojas nlogban^{\log_b a}: gana el más grande, y si empatan aparece el factor logn\log n.

Autoevaluación

¿Lo entendiste?

¿Qué condición tienen que cumplir los subproblemas para que divide y vencerás sirva?
En T(n) = a·T(n/b) + f(n), ¿qué representa n^(log_b a)?
La búsqueda binaria es a = 1, b = 2, f(n) = Θ(1). ¿Qué dice eso?
¿Para qué sirve dibujar el árbol de llamadas?