algorithme de Kruskal

publicité
Algorithmique et Programmation
Projet : Arbres couvrants minimaux par l’algorithme de Kruskal
et application au problème du voyageur de commerce
Ecole normale supérieure
Département d’informatique
[email protected]
2011-2012
Dans un graphe non-orienté pondéré G = (V, E, w), un arbre couvrant minimal A est un ensemble
d’arêtes de G qui :
i) est un arbre (c’est-à-dire connexe et acyclique)
ii) touche tous les sommets de G (c’est “couvrant”)
X
w(u, v) est minimal
iii) est de poids minimal, c’est-à-dire que
{u,v}∈A
1
Algorithme de Kruskal pour l’arbre couvrant minimal
L’algorithme de Kruskal (voir [1, §24.2]) est un algorithme glouton. Il construit une forêt dont tous
les éléments finissent par fusionner pour former un arbre couvrant. Pour cela, il maintient une partition
P ⊆ P(S) des nœuds S. Voici le pseudo-code de l’algorithme de Kruskal :
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
function ACM-Kruskal(G)
A0 ← ∅
P ←∅
for each x ∈ V do MakeSet(P, x)
trier E par ordre croissant de poids w
for all {s, t} ∈ E (dans l’ordre) do
if Find(P, s) 6= Find(P, t) then
A0 ← A0 ∪ {{s, t}}
Union(P, s, t)
end if
end for
end function
return A
Je vous renvoie au Cormen pour une preuve de correction.
Union-Find. L’algorithme de Kruskal est efficacement implémenté avec la structure de donnée UnionFind (voir [1, §22.3]). Cette structure de donnée gère une collection d’ensemble disjoints contenant des
éléments d’un même univers. Elle supporte les trois opérations suivantes :
i) MakeSet(P, x) : créée dans P un ensemble singleton contenant x.
ii) Union(P, x, y) : indique qu’en fait x et y appartiennent au même ensemble.
iii) Find(P, x) : renvoie le représentant “canonique” de l’ensemble contenant x.
Si on utilise une telle structure, ainsi qu’un algorithme de tri en O (n log n), l’algorithme de Kruskal est
en O (|E| log |E|).
Tri efficace. Pour trier la liste des arêtes, on implémentera un tri rapide (a.k.a. “QuickSort”), mais
on utilisera un tri par insertion pour les appels récursifs en deçà d’une certaine taille (par exemple 100,
mais il faut ajuster expérimentalement). Pour choisir le pivot dans le QuickSort, on choisira 3 élements
du tableau et on prendra
celui du milieu (c’est la technique dite “median of three”). Strictement parlant,
ce tri est en O n2 dans le pire des cas, mais en pratique il est bien plus rapide que le tri fusion ou que
les tri en O (n log n).
1
2
Application au problème du voyageur de commerce
Une des applications classique des arbres couvrant minimaux est un algorithme de 2-approximation
pour le problème du voyageur de commerce.
Soit G = (V, E, w) un graphe complet (E = S 2 ), pondéré, non orienté et qui vérifie l’inégalité
triangulaire : ∀s, t, u ∈ V, w({s, u}) ≤ w({s, t}) + w({t, u}). On cherche à trouver un circuit,
c’est à dire
Pn
un chemin s1 , . . . , sn ∈ V passant une et une seule fois par chaque sommet, de poids i=1 w({si , si+1 })
minimal (avec la convention sn+1 = s1 ). On peut montrer (ce qu’on ne demande pas, voir [1, §37.2.1])
que le parcours préfixe d’un arbre couvrant minimal de G est un circuit de poids au plus double du poids
minimal.
Vous écrirez une fonction C qui prend en argument un graphe complet de taille arbitraire et calcule
un circuit grâce à cette heuristique. La sortie sur écran sera la liste, dans l’ordre, des sommets à visiter
suivie du poids du circuit. Les poids seront de type double.
Vous écrirez également un programme de démonstration qui teste votre fonction sur des graphes
aléatoires. Le programme prendra en argument en ligne de commande le nombre de sommets des graphes
à générer. Les graphes seront générés de la manière suivante (afin de s’assurer que l’inégalité triangulaire
est vérifié) : pour chaque sommet si ∈ S, on choisit une position aléatoire (xi , yi ) ∈ R2 dans le plan ;
chaque arête a alors pour poids la distance euclidienne entre ses extrémités :
q
w({si , sj }) = (xi − xj )2 + (yi − yj )2
3
Travail demandé
Vous écrirez une fonction C qui calcule un arbre couvrant minimal par l’algorithme de Kruskal.
L’argument de la fonction sera un graphe connexe de taille arbitraire sous forme de listes d’adjacence
(efficace pour représenter les graphes peu denses). Les poids seront de type double. La sortie sur écran sera
la liste des arêtes qui forment l’arbre (dans un ordre quelconque) suivie du poids de l’arbre. Votre fonction
utilisera la structure de données Union-Find ainsi que l’algorithme de tri que vous devrez également
programmer.
Vous écrirez également un programme de démonstration qui calcule des arbres couvrants minimaux
sur des graphes connexes générés aléatoirement. Le programme prendra en argument en ligne de commande le nombre de sommets V et le nombre d’arêtes E des graphes à générer.
Enfin, vous ferez quelques expériences en variant |E| et |V | pour vérifier expérimentalement le coût
théorique (attention au coût de la génération du graphe et de la sortie sur écran).
Références
[1] Thomas H. Cormen, Clifford Stein, Ronald L. Rivest, and Charles E. Leiserson. Introduction à
l’algorithmique. Dunod, 2nd edition, 2001.
2
Téléchargement