Calcul d’un arbre de recouvrement minimal Ahmed Bouajjani ENS Cachan, Magistère STIC, 2004-05 Problème de l’arbre de recouvrement minimal • Soit G = (S, A) un graphe non orienté, et soit p : A → R une fonction de pondération des arêtes. • Problème : trouver un sous-ensemble T ⊆ A tel que – T est acyclique (un arbre) – T connecte tous les sommets de G – T a un poids total p(T ) = X (u,v)∈T minimal p(u, v) Un algorithme générique • Calculer progressivement un ensemble E d’arêtes en maintenant l’invariant E est un sous-ensemble d’un arbre de recouvrement minimal • ⇒ Ajouter à chaque itération une arête (u, v) admissible pour E : E ∪ {(u, v)} satisfait toujours l’invariant ARM-Gen(G, p) = E←∅ tant que E n’est pas un ARM faire Trouver une arête (u, v) admissible pour E E ← E ∪ {(u, v)} retourner E Trouver un arc admissible • Une coupure de G = (S, A) est une partition (U, S − U ) de S. • Un arc (u, v) traverse une coupure (U, S − U ) si une des extrémités est dans U et l’autre dans S − U . • Une coupure respecte un ensemble d’arcs E si aucun des arcs de E ne traverse la coupure. • Un arc est léger pour une coupure (U, S − U ) s’il traverse la coupure, et si son poids est le poids minimum parmi ceux des arcs traversant cette coupure. Trouver un arc admissible • Une coupure de G = (S, A) est une partition (U, S − U ) de S. • Un arc (u, v) traverse une coupure (U, S − U ) si une des extrémités est dans U et l’autre dans S − U . • Une coupure respecte un ensemble d’arcs E si aucun des arcs de E ne traverse la coupure. • Un arc est léger pour une coupure (U, S − U ) s’il traverse la coupure, et si son poids est le poids minimum parmi ceux des arcs traversant cette coupure. Thm 1: Soit G = (S, A) un graphe connexe non orienté, et soit p : A → R une fonction de pondération. Soit E ⊆ A inclus dans un ARM de G, soit (U, S − U ) une coupure de G qui respecte E, et soit (u, v) un arc léger pour la coupure (U, S − U ). Alors, l’arc (u, v) est admissible pour E. Trouver un arc admissible (suite) Thm 1: Soit G = (S, A) un graphe connexe non orienté, et soit p : A → R une fonction de pondération. Soit E ⊆ A inclus dans un ARM de G, soit (U, S − U ) une coupure de G qui respecte E, et soit (u, v) un arc léger pour la coupure (U, S − U ). Alors, l’arc (u, v) est admissible pour E. • Soit T un ARM contenant E, et supposons qu’il ne contient pas E ∪ {(u, v)}, • Soit c le chemin de u à v dans T . L’arc (u, v) forme un cycle avec c. • (u, v) traverse (U, S − U ) ⇒ il existe un arc (x, y) de T qui traverse (U, S − U ), • Soit T 0 = T − {(x, y)} ∪ {(u, v)}, • T 0 est un arbre de recouvrement • T 0 est de poids minimal – (u, v) est léger pour (U, S − U ) ⇒ p(u, v) ≤ p(x, y), – p(T 0) = p(T ) − p(x, y) + p(u, v) ≤ p(T ) Algorithme de Kruskal (version abstraite) • E est une forêt, • E est étendue en connectant deux arbres différents par un des arcs de poids min pouvant les relier. Kruskal-Abst(G, p) = E←∅ (initialement, la forêt représentée par E est constituée de l’ensemble des singletons {s}, pour tout s ∈ S) tant que E n’est pas réduit à un seul arbre faire Trouver une arête (u, v) de poids min reliant deux arbres de E E ← E ∪ {(u, v)} retourner E Algorithme de Kruskal • E est implémenté comme une partition de S (structure union-find) • Le choix des arcs admissibles : tri des arcs selon leur poids croissants. Kruskal(G, p) = E←∅ pout tout s ∈ S faire Créer-classe(s) Trier les arcs de A dans l’ordre croissant de leurs poids p pour tout (u, v) ∈ A pris dans l’ordre croissant du poids faire si Trouver-classe(u) = Trouver-classe(v) alors E ← E ∪ {(u, v)} Union-classe(u, v) retourner E Algorithme de Kruskal (Complexité) Kruskal(G, p) = E←∅ pout tout s ∈ S faire Créer-classe(s) Trier les arcs de A dans l’ordre croissant de leurs poids p pour tout (u, v) ∈ A pris dans l’ordre croissant du poids faire si Trouver-classe(u) = Trouver-classe(v) alors E ← E ∪ {(u, v)} Union-classe(u, v) retourner E • Tri des arcs : O(A log(A)) • S “Créer” + A “Trouver” et “Union” : O((S + A)α(S)), avec α(S) en O(log(S)), • G connexe ⇒ |A| ≥ |S − 1| • |A| ≤ |S|2 ⇒ log(A) = O(log(S) • Complexité : O(A log(S)) Algorithme de Prim • On part d’un sommet fixé r. • E est toujours un arbre. Algorithme de Prim • On part d’un sommet fixé r. • E est toujours un arbre. π[u] désigne le père de u dans cet arbre • E étendu par un arc de poids min vers un sommet isolé de (S, E) • Pour chaque sommet u 6∈ E, d[u] désigne la distance min à un point de E Prim(G, p, r) = pout tout u ∈ S faire d[u] = ∞ π[u] = nil d[r] = 0 Q←S tant que Q 6= ∅ faire u ← Extraire-min(Q) pout tout v ∈ Adj(u) faire si v ∈ Q et p(u, v) < d[v] alors π[v] ← u d[v] ← p(u, v) Algorithme de Prim (Complexité) Prim(G, p, r) = pout tout u ∈ S faire d[u] = ∞ ; π[u] = nil d[r] = 0 Q←S tant que Q 6= ∅ faire u ← Extraire-min(Q) pout tout v ∈ Adj(u) faire si v ∈ Q et p(u, v) < d[v] alors π[v] ← u ; d[v] ← p(u, v) retourner E • Implémentation de Q par un tas binaire – Initialisation (création d’un tas) : O(|S|) – Opérations “Extraire-min” : S × log(S) – Boucle “for” : 2|A| × log(S) (modification du tas) – Compléxité : O(A log(S)) Algorithme de Prim (Complexité) Prim(G, p, r) = pout tout s ∈ S faire d[u] = ∞ ; π[u] = nil d[r] = 0 Q←S tant que Q 6= ∅ faire u ← Extraire-min(Q) pout tout v ∈ Adj(u) faire si v ∈ Q et p(u, v) < d[v] alors π[v] ← u ; d[v] ← p(u, v) retourner E • Implémentation de Q par un tas binaire : O(A log(S)) • Implémentation de Q par un tas de Fibonacci : – Initialisation (création d’un tas) : O(|S|) – Opérations “Extraire-min” : S × log(S) – Boucle “for” : 2|A| × 1 (modification du tas) – Compléxité : O(S log(S) + A)