Los algoritmos voraces (también conocidos como algoritmos greedy) son un paradigma de diseño de algoritmos que construyen una solución paso a paso, eligiendo en cada etapa la opción que parece mejor en ese momento. A diferencia de otros métodos que pueden retroceder o explorar múltiples caminos, un algoritmo voraz toma una decisión irreversiblemente óptima localmente con la esperanza de alcanzar un óptimo global.

Esta estrategia es fundamental en la ciencia de la computación porque ofrece soluciones simples y eficientes para problemas complejos, como la minimización del costo en redes o la selección de tareas. Sin embargo, su eficacia depende de que el problema posea ciertas propiedades matemáticas específicas; de lo contrario, la elección inmediata más atractiva puede llevar a un resultado subóptimo.

Definición y concepto

Un algoritmo voraz, o greedy, es una estrategia de diseño de algoritmos que construye la solución paso a paso, eligiendo en cada etapa la opción que parece mejor en ese momento. El término proviene de la palabra inglesa greedy, que significa "codicioso" o "voraz", reflejando la naturaleza del enfoque: tomar lo que da más beneficio inmediato sin mirar demasiado lejos hacia el futuro. La premisa fundamental es que una serie de elecciones locales óptimas conducirá a una solución global óptima para el problema completo.

Este mecanismo se basa en cuatro componentes principales. Primero, se define un conjunto de candidatos potenciales. Segundo, se utiliza una función de selección para elegir el candidato más prometedor. Tercero, se verifica si ese candidato es viable, es decir, si al agregarlo a la solución parcial no se rompen las restricciones del problema. Finalmente, se aplica una función de solución para determinar si se ha alcanzado el objetivo. Este proceso se repite hasta que se forme la solución completa.

Mecanismo de decisión local

La característica definitoria de un algoritmo voraz es su falta de arrepentimiento. Una vez que se toma una decisión, esta se considera definitiva y, en la mayoría de los casos, no se revisa ni se corrige a menos que se reinicie todo el proceso. Esto contrasta con otros enfoques donde las decisiones anteriores pueden modificarse tras evaluar nuevas informaciones. La eficiencia radica en esta simplicidad: al reducir el espacio de búsqueda en cada paso, el algoritmo avanza rápidamente hacia una solución.

Consideremos el problema clásico de dar cambio en moneda. Si tenemos monedas de 1, 5, 10 y 25 centavos y necesitamos dar 41 centavos, el enfoque voraz selecciona la moneda más grande posible que no exceda el resto. Primero toma una de 25, luego una de 10, y finalmente dos de 1, resultando en cuatro monedas. En este caso específico, la elección local óptima conduce a la solución global perfecta. Sin embargo, si las monedas fueran de 1, 10 y 25 centavos, y necesitáramos 30 centavos, el algoritmo voraz tomaría una de 25 y cinco de 1 (seis monedas), mientras que la solución óptima sería tres de 10 (tres monedas). Este ejemplo ilustra claramente que la codicia no siempre garantiza la perfección.

Comparación con otros enfoques

Los algoritmos voraz se distinguen notablemente de la fuerza bruta y la programación dinámica. La fuerza bruta evalúa todas las posibles soluciones para encontrar la mejor, lo que garantiza la optimalidad pero suele ser costosa en tiempo de ejecución. Por otro lado, la programación dinámica divide el problema en subproblemas más pequeños, resuelve cada uno y almacena los resultados para evitar cálculos repetidos, asegurando una solución óptima global a menudo con mayor eficiencia que la fuerza bruta, pero generalmente más compleja que un enfoque voraz.

La programación dinámica considera las consecuencias futuras de cada decisión, mientras que el algoritmo voraz se enfoca casi exclusivamente en el beneficio inmediato. Esto hace que los algoritmos voraz sean generalmente más rápidos y requieren menos memoria, lo que los hace ideales para problemas donde una solución "bastante buena" es suficiente o donde la estructura del problema garantiza que la elección local lleve al óptimo global. Sin embargo, su principal desventaja es que no siempre encuentran la mejor solución posible, lo que requiere una demostración formal de optimalidad para cada problema específico.

Dato curioso: El término "voraz" en inglés (greedy) fue popularizado en la ciencia de la computación a finales de los años 60, aunque la lógica subyacente se había utilizado en matemáticas mucho antes, como en el algoritmo de Dijkstra para el camino más corto.

La elección entre un enfoque voraz y otros métodos depende de las características del problema. Si el problema posee la "propiedad de elección voraz" y la "estructura de subproblemas óptimos", un algoritmo voraz suele ser la opción más eficiente. De lo contrario, se corre el riesgo de quedar atrapado en un óptimo local que no es el mejor de todos. La clave está en analizar cuidadosamente cómo las decisiones actuales afectan a las futuras.

Historia y contexto

El término inglés greedy (voraz o codicioso) describe una estrategia de diseño de algoritmos que toma la mejor decisión local disponible en cada paso, con la esperanza de alcanzar un óptimo global. Esta noción no surgió de la nada, sino que se consolidó durante las décadas de 1950 y 1960, cuando los científicos de la computación buscaban métodos eficientes para resolver problemas de optimización en grafos y conjuntos.

Orígenes en la teoría de grafos y la codificación

Uno de los primeros ejemplos prácticos de un enfoque voraz aparece en el algoritmo de caminos mínimos de Edsger W. Dijkstra, publicado en 1957. Dijkstra buscaba demostrar la eficiencia del computador ARMAC, y su método seleccionaba repetidamente el nodo no visitado con la distancia acumulada más corta. Aunque Dijkstra no usó inicialmente la palabra greedy para describirlo, la lógica era puramente voraz: elegir el "mejor" vecino en cada iteración sin mirar demasiado lejos hacia el futuro.

Dato curioso: Dijkstra originalmente llamó a su algoritmo "Algoritmo A", pero su nombre se impuso tras su publicación en 1959. La simplicidad de su enfoque voraz lo convirtió en un estándar inmediato en la teoría de grafos.

Paralelamente, en 1952, David A. Huffman desarrolló su famoso algoritmo de codificación para la compresión de datos. Huffman trabajaba en la Universidad de Míchigan y necesitaba crear un código prefijo óptimo para transmitir textos. Su método construía el árbol de codificación de abajo hacia arriba, fusionando siempre los dos nodos con menor frecuencia. Este proceso es un ejemplo clásico de estrategia voraz porque la decisión de fusionar los dos nodos más ligeros se basa únicamente en la información disponible en ese instante.

Consolidación teórica en los años 60

Durante la década de 1960, el concepto se formalizó. Investigadores como Bernard Chvátal y otros comenzaron a analizar por qué los algoritmos voraces funcionaban en ciertos problemas pero fallaban en otros. Se descubrió que la propiedad clave era la "propiedad de elección voraz": si una elección local óptima siempre forma parte de una solución global óptima, el algoritmo es correcto.

Un ejemplo clásico es el problema de la mochila fraccionada. Si se puede dividir el objeto, la estrategia voraz de elegir primero el objeto con mayor valor por unidad de peso garantiza el óptimo. Sin embargo, en la mochila entera (donde el objeto se toma o se deja), esa misma estrategia puede fallar. Esta distinción fue crucial para entender los límites de la voracidad.

La notación matemática ayuda a clarificar esto. En el algoritmo de Dijkstra, la distancia mínima d a un nodo v se actualiza mediante:

d[v]=min(d[v],d[u]+w(u,v))

donde u es el nodo actual y w(u, v) es el peso de la arista. Esta fórmula refleja la esencia voraz: se actualiza la distancia basándose en el mejor camino conocido hasta ese momento.

La evolución de estos algoritmos muestra cómo la intuición de "elegir lo mejor ahora" se transformó en una herramienta rigurosa. Hoy, los algoritmos voraces siguen siendo fundamentales en la informática, desde la compresión de imágenes hasta la planificación de rutas en redes. Su poder reside en su simplicidad y eficiencia, aunque requieren un análisis cuidadoso para asegurar que la elección local lleve al óptimo global.

¿Cómo se estructura un algoritmo voraz?

Los algoritmos voraces no siguen una estructura rígida única, pero comparten un esqueleto lógico fundamental. En lugar de explorar todas las posibilidades como hacen los algoritmos de fuerza bruta, estos métodos toman decisiones locales óptimas con la esperanza de alcanzar una solución global óptima. Esta estrategia de "avanzar sin mirar atrás" requiere cuatro componentes definidos para funcionar correctamente.

Componentes estructurales

El primer elemento es el conjunto de candidatos. Este es el universo de opciones disponibles en cada paso del proceso. Por ejemplo, al devolver cambio, los candidatos son las monedas disponibles. El algoritmo no considera todas las monedas a la vez, sino que evalúa una por una según un criterio específico.

La función de selección es el corazón de la estrategia voraz. Esta función ordena o selecciona el mejor candidato disponible en el momento actual. Es lo que hace "voraz" al algoritmo: siempre elige la opción que parece mejor en ese instante, sin preocuparse demasiado por las consecuencias futuras inmediatas. Esta elección es irreversible en su forma más pura.

La función de viabilidad actúa como filtro. No todos los candidatos seleccionados por la función de selección encajan en la solución parcial construida hasta ahora. Esta función verifica si agregar el nuevo candidato viola alguna restricción del problema. Si la solución sigue siendo válida, el candidato se acepta; de lo contrario, se descarta y se pasa al siguiente.

Finalmente, la función objetivo asigna un valor a cada solución completa. El propósito del algoritmo es maximizar o minimizar este valor. En algunos casos, la función objetivo también puede servir para determinar cuándo se ha alcanzado una solución completa, permitiendo detener el proceso de búsqueda.

Ejemplo práctico: El problema del cambio

Consideremos el clásico problema de devolver el cambio mínimo usando monedas. Supongamos que tenemos monedas de 1, 5, 10 y 25 unidades, y debemos devolver 41 unidades. El conjunto de candidatos son las cuatro denominaciones de monedas.

La función de selección elige la moneda de mayor valor que no supere el resto del cambio. La función de viabilidad verifica que la suma de las monedas elegidas no exceda el total a devolver. La función objetivo minimiza el número total de monedas utilizadas.

Dato curioso: Este enfoque voraz funciona perfectamente para el sistema monetario actual (1, 5, 10, 25), pero falla en otros sistemas. Si tuviéramos monedas de 1, 3 y 4 unidades y quisieramos devolver 6, el algoritmo voraz elegiría 4, luego 1, luego 1 (tres monedas), mientras que la solución óptima es 3 y 3 (dos monedas).

Aplicando el algoritmo a nuestro ejemplo de 41 unidades: primero seleccionamos la moneda de 25 (la más grande que cabe). Quedan 16 unidades. La función de selección elige la de 10. Quedan 6 unidades. La siguiente opción válida es la de 5. Queda 1 unidad, que se cubre con la moneda de 1. El resultado es cuatro monedas: 25, 10, 5 y 1.

La eficiencia de este método radica en su simplicidad computacional. No requiere comparar múltiples ramas de decisión simultáneamente. Sin embargo, su principal debilidad es la dependencia de la estructura del problema. Si la elección local óptima no garantiza la optimalidad global, el algoritmo puede dejar la mejor opción sobre la mesa. Entender estos cuatro componentes permite identificar cuándo aplicar esta estrategia y cuándo buscar alternativas más complejas.

¿Qué diferencia a los algoritmos voraces de otros métodos?

Los algoritmos voraces (greedy) se distinguen por su toma de decisiones locales e irreversibles. A diferencia de otros paradigmas, no miran hacia atrás ni consideran el futuro lejano más allá de la decisión inmediata. Esta característica define su eficiencia y sus limitaciones. No todos los problemas responden a esta lógica simplista.

Comparación con otros paradigmas

La diferencia fundamental radica en cómo se explora el espacio de soluciones. Los algoritmos voraces eligen la mejor opción disponible en cada paso. La Programación Dinámica (PD) evalúa múltiples subproblemas y guarda resultados para evitar cálculos repetidos. El método Divide y Vencimiento (Divide et Impera) separa el problema en partes independientes, las resuelve y combina los resultados. La Fuerza Bruta prueba todas las posibilidades posibles.

La Programación Dinámica sacrifica velocidad por precisión. Guarda estados intermedios en una tabla o matriz. Esto permite encontrar la solución óptima global, pero consume más memoria. Los algoritmos voraces ignoran la memoria pasada. Solo necesitan la decisión actual y la siguiente. La consecuencia es directa: mayor velocidad, menor uso de memoria.

El método Divide y Vencimiento funciona bien cuando las subpartes son independientes. En los algoritmos voraces, cada decisión afecta a las siguientes. No hay independencia total. La elección de hoy restringe las opciones de mañana. Esta dependencia secuencial es clave para entender su comportamiento.

Característica Algoritmo Voraz Programación Dinámica Fuerza Bruta
Estrategia Decisión local óptima Subproblemas superpuestos Todas las combinaciones
Memoria Baja (estado actual) Media/Alta (tabla de estados) Variable (según estructura)
Velocidad Muy rápida Moderada Lenta (exponencial)
Optimalidad No siempre garantizada Generalmente óptima Siempre óptima
Complejidad típica O(n log n) o O(n) O(n²) o O(n³) O(2ⁿ) o O(n!)

La tabla muestra las diferencias estructurales. Los algoritmos voraces ganan en velocidad. Su complejidad suele ser lineal o casi lineal. La Programación Dinámica requiere más tiempo y memoria. La Fuerza Bruta es precisa pero costosa. Elegir el método correcto depende del problema y de los recursos disponibles.

Dato curioso: El algoritmo de Dijkstra para caminos más cortos es voraz. Elige el nodo más cercano en cada paso. Funciona porque las distancias son aditivas y positivas. Si hubieran pesos negativos, fallaría. Esto muestra que la optimalidad del método voraz depende de la estructura del problema.

Las ventajas de los algoritmos voraces son claras. Son rápidos y fáciles de implementar. Requieren poca memoria. Son ideales para problemas grandes donde la solución perfecta no es urgente. Pero hay un matiz: no siempre dan la mejor solución. En algunos casos, una decisión local óptima lleva a un resultado global mediocre.

La desventaja principal es la falta de garantía de optimalidad. No todos los problemas tienen la propiedad de "elección voraz". Esta propiedad significa que la mejor opción local conduce a la mejor opción global. Sin ella, el algoritmo puede quedarse atrapado en un óptimo local. La Programación Dinámica evita esto al explorar múltiples rutas.

Un ejemplo clásico es el problema de la mochila. Con objetos fraccionarios, el método voraz funciona bien. Se toman los objetos con mayor valor por unidad de peso. Con objetos enteros, falla. A veces, dejar un objeto grande permite meter dos pequeños más valiosos. Aquí, la Programación Dinámica gana. El método voraz es rápido, pero no siempre preciso.

La elección entre métodos depende del contexto. Si la velocidad es crítica y una solución casi óptima basta, el algoritmo voraz es ideal. Si la precisión es esencial y los recursos permiten el costo, la Programación Dinámica es mejor. La Fuerza Bruta sirve como referencia cuando el espacio de soluciones es pequeño.

Los algoritmos voraces no son superiores ni inferiores. Son herramientas específicas. Su poder está en la simplicidad y la velocidad. Su debilidad es la falta de visión global. Entender esta diferencia permite elegir el enfoque correcto para cada problema. La clave está en analizar la estructura del problema antes de elegir el método.

Propiedades clave: elección voraz y subestructura óptima

Los algoritmos voraces no resuelven todo. Su éxito depende de dos propiedades matemáticas estrictas. Si faltan, la solución final puede ser solo una buena aproximación, no la mejor absoluta. Estas condiciones son la propiedad de la elección voraz y la subestructura óptima. Entenderlas permite distinguir cuándo usar este enfoque y cuándo recurrir a la programación dinámica.

La propiedad de la elección voraz

Esta propiedad establece que una decisión local óptima conduce a una solución global óptima. El algoritmo elige la mejor opción disponible en cada paso sin mirar hacia atrás. Una vez tomada, la decisión se considera definitiva. No hay retroceso ni reevaluación posterior.

El riesgo principal es la miopía. Una decisión que parece ideal en el momento puede bloquear opciones mejores más adelante. Para que funcione, la elección actual debe ser tan buena que ninguna combinación futura pueda superarla en conjunto. La confianza en el presente es fundamental.

Debate actual: A menudo se confunde la elección voraz con la intuición simple. La diferencia radica en la prueba formal: demostrar que la elección local no "empuja" la solución fuera del óptimo global requiere rigor matemático, no solo observación empírica.

Subestructura óptima

La subestructura óptima significa que la solución óptima del problema contiene dentro de sí las soluciones óptimas de sus subproblemas. Al resolver una parte del todo, esa parte debe estar resuelta de la mejor manera posible para que el conjunto final sea el mejor.

Esta característica es compartida con la programación dinámica, pero se utiliza de forma distinta. En los algoritmos voraces, al tomar una decisión, el problema original se reduce a un subproblema más pequeño de la misma naturaleza. La estructura se mantiene intacta a medida que se avanza.

Ejemplo: Árbol de Cubrimiento Mínimo

El problema del Árbol de Cubrimiento Mínimo (MST) ilustra ambas propiedades claramente. Dado un grafo conexo y ponderado, el objetivo es encontrar un subconjunto de aristas que conecte todos los vértices con el menor peso total posible.

Considérese el algoritmo de Kruskal. Este método ordena todas las aristas por peso y las añade una a una, siempre que no formen un ciclo. La elección voraz aquí es seleccionar la arista de menor peso disponible. Esta decisión local es irreversibles una vez incorporada al árbol parcial.

La subestructura óptima se manifiesta porque el MST del grafo completo contiene los MSTs de los subgrafos formados por los conjuntos de vértices conectados. Si la arista más ligera que conecta dos componentes no estuviera en el árbol mínimo, podríamos intercambiarla por otra más pesada y el peso total aumentaría, contradiciendo la optimalidad.

La consecuencia es directa: si ambas propiedades se cumplen, el algoritmo voraz garantiza la solución óptima. Si falta una, el resultado puede ser solo una aproximación. Verificar estas condiciones es el primer paso antes de implementar cualquier estrategia voraz.

Aplicaciones y ejemplos prácticos

Los algoritmos voraces no son meras abstracciones teóricas; son herramientas fundamentales en la ingeniería de software y la investigación de operaciones. Su fuerza radica en la simplicidad: toman la mejor decisión local disponible en cada paso, esperando que esto lleve a una solución global óptima. Aunque no siempre garantizan el resultado perfecto, en problemas específicos ofrecen eficiencia y elegancia difíciles de superar.

Rutas más cortas: Algoritmo de Dijkstra

El algoritmo de Dijkstra es el estándar para encontrar la ruta más corta desde un nodo origen a todos los demás en un grafo con pesos no negativos. El enfoque voraz aquí consiste en seleccionar repetidamente el nodo no visitado con la distancia acumulada mínima. Una vez seleccionado, se "fija" su distancia y se actualizan sus vecinos. Este proceso asume que, al llegar al nodo más cercano, no habrá una ruta más corta a través de nodos aún lejanos.

Dato curioso: Edsger Dijkstra desarrolló este algoritmo en solo 20 minutos en 1956 para demostrar las capacidades de la computadora ARMAC. La simplicidad del enfoque refleja su genio para la claridad computacional.

La actualización de distancias sigue una lógica directa. Si la distancia al nodo actual más el peso de la arista hacia el vecino es menor que la distancia conocida del vecino, se actualiza. Esta propiedad de subestructura óptima es clave para el éxito del método.

Conectividad eficiente: Árboles de Cubrimiento Mínimo

Los algoritmos de Kruskal y Prim resuelven el problema de conectar todos los nodos de un grafo con el menor costo total de aristas, formando un árbol de cubrimiento mínimo. Ambos son voraces, pero difieren en su estrategia de selección.

Kruskal ordena todas las aristas por peso y las añade una a una, siempre que no formen un ciclo. Es como construir puentes entre islas empezando por los más cortos. Prim, en cambio, crece desde un nodo inicial, añadiendo siempre la arista más barata que conecte el árbol existente con un nodo nuevo. Ambos garantizan la optimalidad gracias a la propiedad del corte en los grafos.

Compresión de datos: Codificación de Huffman

La codificación de Huffman es un ejemplo clásico en la compresión sin pérdida de datos. Asigna códigos binarios de longitud variable a los símbolos según su frecuencia de aparición. El algoritmo construye un árbol binario desde las hojas hacia la raíz. En cada paso, combina los dos nodos con menor frecuencia en un nuevo nodo padre. Los símbolos más frecuentes quedan más cerca de la raíz, recibiendo códigos más cortos, mientras que los menos frecuentes tienen códigos más largos. Esta asignación voraz minimiza la longitud media del código.

Optimización de recursos: La Mochila Fraccionada

El problema de la mochila fraccionada permite tomar fracciones de los objetos, a diferencia de la versión clásica donde cada objeto se toma completo o se deja. El enfoque voraz funciona perfectamente aquí: se ordenan los objetos por su relación valor-peso y se llenan los espacios disponibles empezando por el más eficiente. Si el último objeto no cabe completo, se toma la fracción necesaria. Esto maximiza el valor total dentro de la capacidad límite.

La fórmula del valor total V se calcula sumando los valores de los objetos completos más la fracción del último objeto. Este método es óptimo porque la flexibilidad de dividir los objetos elimina la necesidad de mirar hacia adelante más allá de la relación inmediata valor-peso.

Estos casos ilustran cómo la estrategia voraz, al enfocarse en la mejor opción inmediata, puede resolver problemas complejos con eficiencia notable. La clave está en identificar cuándo la decisión local conduce inevitablemente a la global.

Ejercicios resueltos

La teoría de los algoritmos voraces cobra sentido cuando se aplica a problemas concretos. A continuación, se analizan dos casos clásicos que ilustran cómo una decisión local óptima conduce a una solución global eficiente.

Problema del cambio de monedas

Considere un sistema monetario con las siguientes denominaciones: 1, 5, 10, 25 y 50. El objetivo es devolver un cambio de 63 unidades utilizando el menor número de monedas posible. La estrategia voraz consiste en seleccionar siempre la moneda de mayor valor que no supere el resto pendiente.

El proceso se desarrolla así:

El resultado final es un conjunto de monedas: {50, 10, 1, 1, 1}. El número total de monedas es 5. Este enfoque funciona porque el sistema es "canónico", es decir, cada moneda es múltiplo o suma eficiente de las anteriores. Si el sistema incluyera una moneda de 4, la solución podría variar.

Selección de actividades

Este problema busca maximizar el número de actividades no solapadas que puede realizar una sola persona, dado un conjunto de actividades con tiempos de inicio (si​) y fin (fi​). La clave es ordenar las actividades por su hora de finalización.

Supongamos las siguientes actividades:

Actividad Inicio Fin
A 1 4
B 3 5
C 0 6
D 5 7
E 8 9
F 5 9

Primero, ordenamos por hora de fin: A(4), B(5), D(7), E(9), F(9), C(6) —corregido: C termina en 6, así que el orden correcto es A(4), B(5), C(6), D(7), E(9), F(9).

Aplicamos la selección:

  1. Seleccionamos la primera en terminar: A (fin 4). Tiempo libre hasta 4.
  2. La siguiente es B (fin 5), pero empieza en 3. Como 3 < 4, hay solapamiento. Descartamos B.
  3. La siguiente es C (fin 6), empieza en 0. 0 < 4. Solapamiento. Descartamos C.
  4. La siguiente es D (fin 7), empieza en 5. 5≥4. No hay solapamiento. Seleccionamos D. Tiempo libre hasta 7.
  5. La siguiente es E (fin 9), empieza en 8. 8≥7. No hay solapamiento. Seleccionamos E. Tiempo libre hasta 9.
  6. La siguiente es F (fin 9), empieza en 5. 5 < 9. Solapamiento. Descartamos F.

El conjunto óptimo es {A, D, E} con 3 actividades. La decisión local de elegir la que termina antes deja la mayor cantidad de tiempo libre para las siguientes.

Dato curioso: En el problema de cambio de monedas, si añadimos una moneda de valor 4 al sistema {1, 5, 10, 25}, el algoritmo voraz falla para el cambio de 8. El voraz elegiría {5, 1, 1, 1} (4 monedas), pero la solución óptima es {4, 4} (2 monedas). Esto demuestra que la estrategia voraz no es universalmente óptima sin verificar la estructura del conjunto.

Limitaciones y cuándo fallan

Los algoritmos voraces no son una solución universal. Su principal debilidad radica en la suposición de que la mejor decisión local conduce inevitablemente a la mejor decisión global. Esta estrategia de "mirada corta" funciona de maravilla en problemas con estructura específica, pero falla estrepitosamente cuando las elecciones tempranas restringen demasiado las opciones futuras sin garantizar la recompensa máxima.

El problema de la mochila 0/1

El ejemplo clásico donde el enfoque voraz falla es el problema de la mochila 0/1. En este escenario, tienes una mochila con una capacidad de peso limitada y una serie de objetos, cada uno con un peso y un valor asociados. El objetivo es maximizar el valor total de los objetos en la mochila sin exceder su capacidad. La restricción "0/1" significa que cada objeto es indivisible: o lo tomas completo (1) o lo dejas fuera (0).

Un algoritmo voraz típico ordenaría los objetos por su relación valor/peso y los iría añadiendo uno a uno. Supongamos una mochila de capacidad 10 kg. Tenemos tres objetos: A (peso 6 kg, valor 30), B (peso 5 kg, valor 25) y C (peso 5 kg, valor 25). El objeto A tiene la mejor relación valor/peso (5 por kg), seguido de B y C (5 por kg). Un enfoque voraz tomaría A primero. Quedan 4 kg de capacidad. Ni B ni C caben enteros. El valor total sería 30.

Sin embargo, si hubiéramos dejado A y tomamos B y C, el peso total sería 10 kg y el valor total sería 50. El algoritmo voraz, al obsesionarse con la mejor relación inicial, dejó atrás una combinación superior. La consecuencia es directa: la elección local óptima bloqueó el camino a la solución global óptima.

Dato curioso: Si los objetos fueran divisibles (problema de la mochila fraccional), el algoritmo voraz sí daría la solución óptima. Podrías tomar todo A y medio B, llenando exactamente la capacidad y maximizando el valor. La indivisibilidad es lo que rompe la lógica voraz.

Falta de retroceso

La razón fundamental del fallo es la falta de mecanismo de retroceso. Una vez que un algoritmo voraz toma una decisión, la considera irreversiblemente correcta. No mira hacia atrás para corregir errores, ni mira lo suficientemente lejos para anticipar consecuencias. Esto lo hace muy eficiente en tiempo de ejecución, pero le cuesta precisión en problemas donde las variables están altamente interconectadas.

En problemas como la mochila 0/1, la programación dinámica suele ser superior porque evalúa subproblemas y guarda resultados intermedios. Esto permite considerar múltiples combinaciones y elegir la mejor globalmente, a costa de mayor complejidad computacional. El algoritmo voraz es rápido, pero a veces demasiado rápido para ver el bosque por los árboles.

Preguntas frecuentes

¿Por qué se llaman "voraces" estos algoritmos?

Se les llama "voraces" porque "devoran" la mejor opción disponible en cada paso sin mirar demasiado lejos hacia el futuro. No guardan opciones para después ni reconsideran decisiones pasadas; toman lo mejor que tienen justo en ese instante.

¿Siempre dan la solución perfecta?

No. Un algoritmo voraz da la solución perfecta (óptima) solo si el problema tiene dos propiedades clave: la "elección voraz" y la "subestructura óptima". En otros casos, puede dar una solución muy buena, pero no necesariamente la mejor de todas.

¿Cuál es la diferencia principal con la "fuerza bruta"?

La fuerza bruta prueba casi todas las combinaciones posibles para asegurar la mejor respuesta, lo cual puede ser lento. El algoritmo voraz toma una decisión rápida en cada paso, lo que suele hacer que la solución sea mucho más rápida, aunque a veces menos precisa si no se planifica bien.

¿Dónde se usan en la vida real?

Se utilizan en muchas aplicaciones cotidianas, como en el algoritmo de Dijkstra para encontrar la ruta más corta en mapas (GPS), en la compresión de archivos (Código de Huffman) y en la gestión de tareas en sistemas operativos.

¿Son difíciles de implementar?

Generalmente son más fáciles de implementar que otros métodos como la programación dinámica. Su lógica es directa: evaluar, elegir la mejor opción, añadirla a la solución y repetir. La dificultad está más en probar matemáticamente que esa elección sea correcta.

Resumen

Los algoritmos voraces son una técnica eficiente para resolver problemas de optimización mediante la toma de decisiones locales óptimas. Su éxito depende de que el problema permita que la suma de las mejores elecciones paradas conduzca a la mejor solución global.

Comprender cuándo aplicar esta estrategia, identificando propiedades como la subestructura óptima y la elección voraz, es esencial para diseñar soluciones computacionales rápidas y efectivas en campos que van desde la teoría de grafos hasta la compresión de datos.