Algoritmos Voraces

Definición, componentes, modelado, demostración, ejemplos clásicos y comparación con Programación Dinámica.

Algoritmos Voraces: idea central

Un algoritmo voraz construye una solución paso a paso. En cada etapa mira los candidatos disponibles, escoge el que parece mejor según un criterio local y, si es factible, lo agrega al conjunto solución.

La decisión es irrevocable

La característica peligrosa del voraz es que no reconsidera. Una vez acepta un candidato, ese candidato permanece en la solución. Si la elección local fue mala para el óptimo global, el algoritmo no se corrige solo.

Por eso los voraces son rápidos y simples, pero matemáticamente delicados. No basta con que la estrategia parezca razonable: hay que demostrar que esa regla local siempre conduce a una solución óptima, o aceptar que es solo una heurística.

La tensión central de los voraces

VentajaRiesgoExigencia
Suelen ser rápidos y fáciles de implementar.Pueden quedarse con una solución mala por una elección temprana.Demostrar propiedad voraz o mostrar contraejemplo.

Componentes de un algoritmo voraz

Un algoritmo voraz no se describe correctamente diciendo “escojo lo mejor”. Hay que especificar qué se escoge, con qué criterio, bajo qué restricciones y cuándo se considera terminada la solución.

Componentes formales de un algoritmo voraz

ComponentePregunta que respondeEjemplo en cambio de monedas
Conjunto de candidatos¿Qué elementos puedo escoger?Denominaciones disponibles.
Función de selección¿Cuál candidato parece mejor ahora?La moneda más grande disponible.
Función de factibilidad¿Puedo agregar este candidato sin violar restricciones?La moneda debe ser menor o igual al restante.
Conjunto solución¿Qué candidatos ya acepté?Monedas acumuladas.
Función objetivo¿Qué quiero optimizar?Minimizar el número de monedas.
Función comprobante¿La solución ya está completa?El restante llegó a cero.
Selección y factibilidad no son lo mismo

La función de selección dice cuál candidato parece mejor. La función de factibilidad dice si ese candidato se puede aceptar. Un candidato puede ser el más atractivo y aun así ser inválido.

Modelo general de un algoritmo voraz

El modelo voraz tiene tres movimientos repetidos: seleccionar un candidato, verificar si es prometedor y actualizar la solución parcial. La forma exacta cambia por problema, pero el esqueleto es el mismo.

Modelo de selección voraz

En esta expresión, xt representa el candidato elegido en el paso t. El símbolo 'arg opt' indica que buscamos el elemento x, dentro del conjunto de candidatos Ct, que maximice o minimice el criterio local definido por el problema.

Modelo de actualización del conjunto solución

CSt es el conjunto solución acumulado. Si el candidato xt es factible (no rompe las reglas), se une al conjunto anterior usando el símbolo de unión (U). De lo contrario, la solución se mantiene igual.

Modelo de actualización de candidatos

Ct+1 define el nuevo conjunto de candidatos para la siguiente etapa. Dependiendo del problema, el candidato xt se descarta (resta de conjuntos) o permanece disponible para ser reutilizado.

Esquema general voraz
algoritmoVoraz(Candidatos) BEGIN
  CS <- {};
  encontrado <- F;

  WHILE (NOT vacio(Candidatos) AND NOT encontrado) DO BEGIN
    x <- seleccionar(Candidatos);

    IF (factible(x, CS)) THEN BEGIN
      CS <- incluir(x, CS);
    END

    Candidatos <- remover(x, Candidatos);

    IF (esSolucion(CS)) THEN BEGIN
      encontrado <- T;
    END
  END

  RETURN CS;
END
El esquema no prueba correctitud

Este esquema solo describe cómo se construye la solución. No demuestra que sea óptima. La correctitud depende de probar que la función de selección es válida para el problema específico.

Correctitud: propiedad voraz y subestructura óptima

El punto débil de los voraces es la correctitud. Implementar la selección local suele ser fácil; demostrar que esa selección local siempre lleva al óptimo global es lo difícil.

Condiciones para confiar en un voraz

CondiciónSignificadoPregunta de control
Propiedad vorazExiste una solución óptima que contiene la elección local tomada por el algoritmo.¿Puedo demostrar que escoger este candidato ahora no destruye el óptimo?
Subestructura óptimaDespués de tomar la decisión voraz, lo que queda sigue siendo un subproblema del mismo tipo.¿El problema restante se resuelve con la misma lógica?
Factibilidad acumuladaCada decisión mantiene una solución parcial prometedora.¿Agregar el candidato conserva las restricciones?

Plantilla de demostración voraz

Definir una solución óptima

Se parte de una solución óptima cualquiera.

Identificar la elección voraz

El candidato es la elección local del algoritmo.

Intercambio

Se intenta reemplazar algún elemento de la solución óptima por la elección voraz sin empeorar la solución.

No empeora

Según sea minimización o maximización, se prueba que el intercambio conserva optimalidad.

Reducir al subproblema

Después de fijar la elección voraz, el problema restante debe tener la misma estructura.

Un contraejemplo mata una estrategia voraz

Si encuentras una sola entrada donde la regla voraz produce una solución peor que otra posible, la estrategia no es correcta en general. Puede seguir siendo una heurística, pero no un algoritmo exacto.

Ejemplo 1 — Cambio de monedas

El problema pide dar cambio exacto para una cantidad objetivo usando el menor número posible de monedas. La regla voraz natural es tomar en cada paso la moneda más grande que no exceda el restante.

Componentes del voraz para cambio de monedas

ComponenteDefinición en el problema
CandidatosDenominaciones disponibles.
Función de selecciónEscoger la denominación más grande.
FactibilidadLa moneda debe ser menor o igual al restante.
Función objetivoMinimizar el número total de monedas.
ComprobaciónEl restante debe llegar a cero.

Modelo de selección

dt es el valor de la moneda elegida. Buscamos el valor máximo (max) en el conjunto de denominaciones D que no supere el dinero restante rt.

Modelo de actualización

rt+1 es la nueva cantidad de dinero que falta por entregar. Se calcula restando el valor de la moneda entregada dt al monto previo.

Modelo del conjunto solución

El conjunto solución se actualiza añadiendo la moneda dt si esta cumple la condición de ser menor o igual al restante.

Cambio de monedas voraz
cambioVoraz(D[n], objetivo) BEGIN
  restante <- objetivo;
  CS <- {};

  WHILE (restante > 0) DO BEGIN
    moneda <- seleccionarMayorFactible(D, restante);

    IF (moneda = -1) THEN BEGIN
      RETURN -1;
    END

    CS <- incluir(moneda, CS);
    restante <- restante - moneda;
  END

  RETURN CS;
END

Ejecución para objetivo 17 y denominaciones {10, 4, 2, 1}

EtapaCandidato elegido¿Factible?RestanteCS acumulado
110Sí, 10 ≤ 1717 - 10 = 7{10}
24Sí, 4 ≤ 77 - 4 = 3{10, 4}
32Sí, 2 ≤ 33 - 2 = 1{10, 4, 2}
41Sí, 1 ≤ 11 - 1 = 0{10, 4, 2, 1}
Resultado del ejemplo

El algoritmo devuelve 4 monedas: {10, 4, 2, 1}. La suma es 17.

Contraejemplo: el voraz puede fallar

Con denominaciones {11, 5, 3, 2} y objetivo 15, el voraz toma 11, queda restante 4 y luego toma 3 y 1 si existiera, o falla si no hay moneda de valor 1. Pero la solución óptima es {5, 5, 5}, con solo 3 monedas.

Diagnóstico honesto

El cambio de monedas solo es exacto con voraz para ciertos sistemas monetarios. Si el sistema de denominaciones no tiene la propiedad adecuada, el algoritmo puede ser rápido y aun así incorrecto.

Ejemplo 2 — Caballo en el ajedrez: regla de Warnsdorff

El problema consiste en recorrer un tablero de ajedrez con un caballo pasando una sola vez por cada casilla. En cada etapa, los candidatos son las casillas alcanzables por el movimiento del caballo.

Componentes voraces en Warnsdorff

ComponenteDefinición
CandidatosCasillas a las que el caballo puede moverse desde la posición actual.
FactibilidadLa casilla no debe haber sido visitada.
Función de selecciónEscoger la casilla con menor número de movimientos futuros disponibles.
Conjunto soluciónSecuencia de casillas visitadas por el caballo.
ComprobaciónTodas las casillas fueron visitadas exactamente una vez.

Modelo de selección de Warnsdorff

La función siguiente(v) elige la próxima casilla u que minimiza el 'grado disponible', es decir, la que tiene menos opciones de salida hacia casillas no visitadas.

Modelo de actualización

La ruta del caballo se extiende con la casilla elegida solo si existe al menos un movimiento factible desde la posición actual.

Decisión voraz en Warnsdorff

Renderizando diagrama...

Esquema de Warnsdorff
warnsdorff(tablero[n][n], inicio) BEGIN
  actual <- inicio;
  ruta <- {actual};
  marcarVisitada(actual);

  WHILE (longitud(ruta) < n * n) DO BEGIN
    candidatos <- movimientosFactibles(actual, tablero);

    IF (vacio(candidatos)) THEN BEGIN
      RETURN -1;
    END

    siguiente <- candidatoMenorGrado(candidatos, tablero);
    ruta <- incluir(siguiente, ruta);
    marcarVisitada(siguiente);
    actual <- siguiente;
  END

  RETURN ruta;
END
Warnsdorff es una heurística

La regla de Warnsdorff es voraz porque escoge la casilla localmente más restrictiva. Puede funcionar muy bien, pero no debe venderse como demostración automática de solución óptima para cualquier variante del problema. Si no hay prueba, es heurística.

Ejemplo 3 — Selección de actividades

Dado un conjunto de actividades con hora de inicio y fin, se busca seleccionar el máximo número de actividades compatibles, es decir, actividades que no se solapen.

Componentes voraces en selección de actividades

ComponenteDefinición
CandidatosActividades disponibles.
Función de selecciónElegir la actividad que termina más temprano.
FactibilidadLa actividad debe iniciar después o justo cuando termina la última actividad seleccionada.
Función objetivoMaximizar el número de actividades seleccionadas.

Modelo de selección

at representa la actividad elegida. Se busca aquella que tenga el tiempo de finalización (fin(a)) más temprano entre todas las que inician después de que termine la última actividad ya aceptada.

Modelo de actualización

El conjunto de actividades seleccionadas CS crece solo si la nueva actividad at es compatible con el horario de la última actividad del conjunto.

Ejemplo de selección de actividades

ActividadInicioFin
A114
A235
A306
A457
A589
A659

Ejecución voraz

Ordenar por fin

La regla voraz trabaja con la actividad que termina más temprano.

Seleccionar A1

Es la primera actividad que termina.

Descartar incompatibles

Ambas se solapan con A1.

Seleccionar A4

A4 es compatible y termina antes que las demás compatibles.

Seleccionar A5

A5 es compatible con la última seleccionada.

Resultado

Se seleccionan tres actividades compatibles.

Selección voraz de actividades
seleccionarActividades(inicio[n], fin[n]) BEGIN
  ordenarPorFin(inicio, fin);
  CS <- {};
  ultimoFin <- 0;

  FOR i <- 1 TO n DO BEGIN
    IF (inicio[i] >= ultimoFin) THEN BEGIN
      CS <- incluir(i, CS);
      ultimoFin <- fin[i];
    END
  END

  RETURN CS;
END
Por qué esta regla sí funciona

Elegir la actividad que termina más temprano deja el mayor espacio posible para las actividades restantes. Mediante argumento de intercambio, si una solución óptima empieza con otra actividad compatible que termina después, se puede reemplazar por la que termina antes sin reducir el número total de actividades.

Cuándo usar Voraces vs Programación Dinámica

Voraz y Programación Dinámica pueden aparecer en problemas de optimización, pero no razonan igual. El voraz se compromete con una decisión local. PD evalúa alternativas y guarda subproblemas.

Algoritmo Voraz vs Programación Dinámica

CriterioAlgoritmo VorazProgramación Dinámica
Garantía de óptimoSolo si se demuestra la propiedad voraz.Sí, si el modelo cumple principio de optimalidad y se evalúan los estados necesarios.
ReconsideraciónNo reconsidera decisiones aceptadas.Evalúa alternativas y conserva mejores valores.
VelocidadGeneralmente más rápido.Puede ser más lento porque llena tablas o memoria.
Memoria a o más, según la tabla.
AplicabilidadProblemas con propiedad voraz demostrable.Problemas con subproblemas solapados y decisiones entre alternativas.

Decisión entre Voraz y Programación Dinámica

Renderizando diagrama...

No uses voraz por pereza

Si no puedes demostrar que la elección local es segura, no tienes un algoritmo voraz correcto. Tienes una conjetura. Para clase, eso no alcanza.

Errores típicos en algoritmos voraces

Errores frecuentes en algoritmos voraces

ErrorPor qué está malCorrección
Confundir óptimo local con óptimo globalUna decisión buena ahora puede bloquear una solución mejor después.Demostrar propiedad voraz o buscar contraejemplo.
No definir función de factibilidadEl algoritmo puede agregar candidatos que violan restricciones.Separar selección de factibilidad.
Decir que es correcto porque parece intuitivoLa intuición no prueba optimalidad.Usar intercambio, corte o inducción según el problema.
Ignorar contraejemplos pequeñosUn solo contraejemplo invalida la estrategia general.Probar con entradas adversas antes de afirmar correctitud.
Llamar voraz a cualquier algoritmo rápidoLa velocidad no define la técnica.Debe existir selección local irrevocable por etapas.
Usar voraz donde se requiere reconsiderarSi el problema exige comparar combinaciones completas, PD o búsqueda pueden ser necesarias.Comparar contra Programación Dinámica, Backtracking o Branch and Bound.

Resumen operativo

Receta mínima
  1. Define el conjunto de candidatos.
  2. Define la función objetivo: qué quieres maximizar o minimizar.
  3. Define la función de selección local.
  4. Define la función de factibilidad.
  5. Define cómo se actualiza el conjunto solución.
  6. Define cuándo termina el algoritmo.
  7. Demuestra la propiedad voraz o encuentra un contraejemplo.
  8. Analiza complejidad temporal y espacial.

Fórmulas y modelos clave

Selección local

La elección depende solo de la etapa actual.

Actualización factible

Solo se agregan candidatos que conservan las restricciones.

Costo típico

Muchos voraces ordenan candidatos y luego hacen un recorrido lineal. Por eso aparecen costos típicos como .

Correctitud

Sin estas propiedades, la estrategia puede ser solo heurística.