Atlasingeniería

Diseño de algoritmosAlgoritmos en grafosTema 3

Kruskal y Prim

Dos algoritmos golosos para conectar todo al menor costo posible. Uno ordena las aristas y evita ciclos; el otro hace crecer un árbol desde un vértice. Los dos son correctos por la misma propiedad.

Para este tema conviene tener claro:Conjuntos disjuntos (union-find)

Conectar un conjunto de puntos —ciudades, nodos de una red, componentes de un circuito— gastando lo menos posible en enlaces. La respuesta es un árbol generador mínimo, y hay dos formas clásicas de construirlo, las dos golosas y las dos correctas por la misma razón de fondo.

Conectar todo con el menor peso total

Dado un grafo no dirigido, conexo y con pesos, se busca el subconjunto de aristas que conecte todos los vértices con peso total mínimo. Ese subconjunto no puede tener ciclos: sacar una arista de un ciclo mantiene la conexión y baja el costo.

Un árbol sobre VV vértices tiene exactamente V1V-1 aristas. El problema es elegir cuáles, y hacerlo bien entre una cantidad astronómica de árboles posibles.

Antes de seguir, predecí

Un árbol generador mínimo, ¿contiene el camino mínimo entre dos vértices cualesquiera?

La propiedad del corte, que justifica a los dos

Lo que justifica a los dos algoritmos es la propiedad del corte. Si se parte el conjunto de vértices en dos, la arista más liviana que cruza esa división pertenece a algún árbol generador mínimo.

La demostración es un intercambio: si un árbol óptimo no la incluye, agregarla forma un ciclo que necesariamente cruza la división por otra arista más pesada; cambiarla por la liviana da un árbol igual de válido y no más caro. Kruskal y Prim son dos maneras de elegir el corte.

Cinco vértices y seis aristas con sus pesos. Buscamos las cuatro que conecten todo con el menor peso total.

1 / 6
Kruskal mira las aristas ordenadas por peso y toma la que no cierre ciclo. Fijate el paso donde saltea la de peso 5: no es que sea cara, es que sus dos puntas ya estaban conectadas y agregarla no conectaría a nadie nuevo.

Kruskal: ordenar aristas y evitar ciclos

Kruskal ordena todas las aristas por peso creciente y las va tomando, salteando las que conectarían dos vértices del mismo grupo —que formarían ciclo—. Termina con V1V-1 aristas.

La pregunta “¿ya están conectados?” es exactamente lo que responde union-find. Ordenar domina el costo: O(ElogE)O(E\log E), y las consultas de conjuntos disjuntos son prácticamente constantes. Durante la ejecución hay varios fragmentos sueltos que recién al final se unen en un árbol.

Prim: hacer crecer un solo árbol

Prim arranca de un vértice cualquiera y hace crecer un único árbol: en cada paso agrega la arista más liviana que conecta el árbol con un vértice de afuera.

La estructura que hace falta es una cola de prioridad con los vértices pendientes, ordenados por el costo de engancharlos. El costo es O((V+E)logV)O((V+E)\log V), y el parecido con Dijkstra es más que superficial: la única diferencia es qué se guarda en la cola —el peso de la arista de conexión, no la distancia acumulada desde el origen—.

Cuál conviene según la densidad

En grafos ralos suele convenir Kruskal, sobre todo si las aristas ya vienen ordenadas o se pueden ordenar barato: ahí el algoritmo es casi lineal. En grafos densos conviene Prim, que no necesita mirar todas las aristas de entrada y con una implementación matricial da O(V2)O(V^2).

Hay un detalle sobre la unicidad: si todos los pesos son distintos, el árbol generador mínimo es único y los dos algoritmos devuelven el mismo. Con pesos repetidos pueden dar árboles distintos, ambos óptimos.

Dónde aparece, además del tendido de redes

Más allá del tendido de redes, el árbol generador mínimo aparece en clustering: sacarle las k1k-1 aristas más pesadas parte los datos en kk grupos separados, que es el clustering de enlace simple.

También sirve como cota inferior en branch and bound para el viajante de comercio. Y una advertencia importante: conectar todo al mínimo costo no es lo mismo que minimizar la distancia entre dos puntos. El camino entre dos vértices dentro del árbol puede ser mucho peor que el camino mínimo del grafo.

Cuál de los dos, y para qué sirve un árbol generador

KruskalPrim
Cómo eligela arista más liviana que no cierre ciclola más liviana que salga del árbol actual
Estructura que necesitaunion-find, y ordenar las aristascola de prioridad
CostoO(E log E)O(E log V) con heap
Conviene cuandoel grafo es raloel grafo es denso
Si el grafo no es conexoda un bosque: sirve igualsólo cubre la componente del origen
Se puede parar a la mitadno: las aristas están sueltassí: siempre hay un árbol válido
La fila del grafo no conexo es la que más decide en la práctica: los grafos reales casi nunca son conexos, y Kruskal devuelve el bosque generador mínimo sin que haya que hacerle nada.
Más a fondo · formalUnicidad, y qué pasa con pesos repetidos

Si todos los pesos son distintos, el árbol generador mínimo es único. La demostración es por contradicción: si hubiera dos, la arista de menor peso que está en uno y no en el otro produce, al agregarla al segundo, un ciclo con alguna arista más pesada, y el intercambio daría un árbol más liviano que el supuesto óptimo.

Con pesos repetidos puede haber varios óptimos, todos del mismo peso total, y ahí aparece un detalle que muerde en producción: dos corridas del mismo algoritmo sobre los mismos datos pueden devolver árboles distintos si el criterio de desempate depende del orden de iteración, que en algunos lenguajes no está garantizado. Si el resultado se compara entre corridas —en un test, en un despliegue— conviene fijar el desempate explícitamente.

Cierre

Kruskal ordena aristas y evita ciclos con union-find; Prim hace crecer un árbol con una cola de prioridad. Los dos son golosos y correctos por la propiedad del corte: la arista más liviana que cruza una división siempre entra en algún óptimo.

Autoevaluación

¿Lo entendiste?

¿Qué propiedad justifica a los dos algoritmos?
En Kruskal, ¿qué responde union-find?
¿Por qué el árbol generador mínimo no puede tener ciclos?
Durante la ejecución de Kruskal, ¿qué se va formando?