CSI2510 Structures de données et algorithmes Arbres couvrants

publicité
Arbre couvrant minimal (Minimum spanning
tree)
CSI2510
Structures de données et
algorithmes
Définitions
L'algorithme de Prim-Jarnik
L'algorithme de Kruskal
Arbres couvrants minimaux
1
Arbre couvrant minimal
ACM versus PCC
Sous-graphe couvrant
Sous-graphe d’un graphe G
contenant tous les sommets de
G
10
1
ORD
10
1
Arbre couvrant
6
9
3
Arbre couvrant d’un graphe
pondéré avec la somme
minimale de poids des arêtes
constituant l'arbre
8
DFW
Réseaux informatiques
Réseaux routiers
5
2
8
DFW
DCA
STL
4
STL
4
7
3
DCA
5
6
3
DFW
DCA
STL
4
8
ATL
7
9
2
5
2
ATL
ATL
Arbre des plus courts chemin
Arbre couvrant minimal
CSI2510 - ACM
PIT
DEN
6
9
7
10
1
PIT
DEN
PIT
DEN
Arbre couvrant minimal (ACM)
Applications
ORD
ORD
Sous-graphe couvrant qui est un
arbre
2
CSI2510 - ACM
3
CSI2510 - ACM
4
4
1
Propriété des cycles
Propriété des cycles
Soit T un arbre couvrant minimal
d’un graphe pondéré G
Soit e une arête de G
n’appartenant pas à T et soit C le
cycle obtenu lorsqu’on ajoute e à
T
Pour chaque arête f dans C,
poids(f) ≤ poids(e)
Preuve:
Par contradiction
Si poids(f) > poids(e) nous
pouvons obtenir un arbre
couvrant de plus petit poids en
remplaçant e par f
8
f
4
C
2
ORD
9
6
3
e
8
7
7
Remplacer f avec e produit un
meilleur arbre couvrant
PIT
DEN
6
7
9
3
DCA
STL
4
8
4
C
10
1
8
f
5
2
9
6
2
Dans tout cycle du graphe,
les arêtes faisant partie d’un
ACM ont des poids
inferieurs aux autres arêtes
(en pointillé)
DFW
3
e
8
ATL
7
7
5
CSI2510 - ACM
Propriété de partition
U
Preuve:
Soit T un arbre couvrant minimal de G
Si T ne contient pas e, soit C le cycle
formé par l’addition de e à T et soit f une
arête de C entre U et V
Par la propriété de cycles,
poids(f) ≤ poids(e)
Alors, poids(f) = poids(e)
Nous obtenons un autre ACM en
remplaçant f par e
CSI2510 - ACM
6
L’algorithme de Prim-Jarnik
Propriété de partition:
Considérons une partition des
sommets de G en deux ensembles U
et V. Soit e une arête de poids
minimal entre U et V. Alors, il existe
un arbre couvrant minimal de G
contenant e
CSI2510 - ACM
f
V
7
4
9
5
2
8
8
3
e
7
Remplacer f avec e
donne un autre ACM
U
f
2
V
7
L'algorithme de Prim-Jarnik pour calculer un ACM est
similaire à l'algorithme de Dijkstra
Nous supposons que le graphe est connexe
Nous choisissons un sommet arbitraire s et nous faisons
grossir l’ACM comme un nuage de sommets, commençant
de s
Nous emmagasinons avec chaque sommet v une étiquette
d(v) représentant le plus petit poids d'une arête reliant v à un
sommet du nuage (au lieu de la somme totale des poids sur
un chemin du sommet de départ jusqu’à v dans Dijkstra)
4
9
5
8
8
e
3
7
7
CSI2510 - ACM
8
2
L’algorithme de Prim-Jarnik
L’algorithme de Prim-Jarnik
Pour chaque étape
Utiliser une file à priorité Q dont les clés sont les étiquettes D, et dont
les éléments sont des paires sommet-arête
Nous ajoutons au nuage le sommet extérieur u ayant la
plus petite étiquette de distance
Nous mettons à jour les étiquettes des sommets adjacents
àu
Clé : distance
Élément: (sommet, arête de poids minimal)
Tout sommet v peut être le sommet de départ (D[v] =0)
On initialise toutes les valeurs de D[u] à “infini”, mais nous
initialisons aussi E[v] (l’arête associé à v) à “null”
Retourne l’arbre recouvrant minimal T.
Nous pouvons réutiliser le code produit par Dijkstra, et ne changer que quelques parties. Observons
le pseudo-code....
CSI2510 - ACM
9
Algorithm PrimJarnik(G):
Entrée: Un graphe pondéré G.
Sortie: Un ACM T pour G.
Choisir un sommer v de G
{bâtir l’arbre à partir de v}
T ← {∅}
D[v] ← 0
E[v] ← null
Pour chaque sommet u ≠ v faire
D[u] ← ∞
Soit Q une file à priorité contenant des sommets avec les étiquettes D comme clé
Tant que Q n’est pas vide
(u,E(u)) ← Q.removeMinElement()
ajouter le sommet u et l’arête E[u] à T
Pour chaque sommet z adjacent à u et dans Q
{effectuer la relaxation de l’arête (u, z) }
if poids(u, z) < D[z] then
D[z] ←weight(u, z)
E[z] ← (u, z)
mettre à jour D[z]
retourner l’arbre T
CSI2510 - ACM
11
CSI2510 - ACM
10
Prim-Jarnik: Exemple
2
D
7
4
9
8
C
5
F
8
A
2
0
9
5
C
A
2
∞
F
8
2
3
E
7
8
8
4
5
∞
F
3
E
7
7
B
8
A
D
7
4
9
5
C
5
2
7
7
2
B
3
E
7
D
7
∞
8
0
2
B
2
0
∞
0
CSI2510 - ACM
A
7
B
4
9
5
C
5
F
8
8
7
D
7
7
3
E
7
4
7
12
3
Prim-Jarnik: Exemple
0
ACM versus PCC
A
8
4
2
B
2
2
0
7
B
9
5
C
5
F
8
C
3
Djkstra
(Shortest Path Spanning Tree)
3
2
2
D
7
9
5
C
A
2
F
8
8
0
A
8
4
5
7
4
8
B
7
2
E
3
5
CSI2510 - ACM
C
3
3
E
1
D
9
∞
F
4
5
PRIM
(Minimum Spanning Tree)
0
7
B
2
E
3
E
7
4
7
∞
2
4
8
A
D
7
8
13
Prim-Jarnik
∞
0
4
2 1
A
8
D
4
3
B
7
7
8
9
5
F
∞
2
11
4
2
C
3
E
∞ 3
CSI2510 - ACM
2
1
D
F
5
1
4
9
∞ 9
14
Prim-Jarnik: Analyse de complexité
Opérations sur les graphes:
Méthode incidentEdges est appelée une fois pour chaque
sommet
Opérations d’étiquetage:
Nous plaçons/obtenons les étiquettes du sommet z O(deg(z))
fois
placer/obtenir les étiquettes prend O(1)
Opérations de file de priorité (implémenté par un tas):
Chaque sommet est inséré une fois et enlevé une fois de la file à
priorité, où chaque insertion ou suppression prend O(log n)
La clé d'un sommet w dans la file à priorité est modifiée au plus
deg(w) fois, où chaque changement de clé prend O(log n)
L’algorithme applique la propriété des cycles
Soit (u,v) l’arête minimale pour une itération donnée.
S’il y a un ACM ne contenant pas (u,v), alors (u,v)
complète un cycle. Puisque (u,v) a été préférée à toute
autres arête connectant v au groupe, elle doit avoir un
poids inférieur ou égal au poids de l’autre arête. Un
nouveau ACM peut être formé par permutation
CSI2510 - ACM
15
CSI2510 - ACM
16
4
Dijkstra vs. Prim-Jarnik
Prim-Jarnik: Analyse de complexité
Algorithm DijkstraShortestPaths(G, s)
Q ← new heap-based priority queue
L’algorithme de Prim-Jarnik est exécuté en O((n + m) log n)
si le graphe est représenté par la structure de liste d’adjacence (et en
utilisant un tas)
for all v ∈ G.vertices()
if v = s
setDistance(v, 0)
else
setDistance(v, ∞)
setParent(v, ∅)
l ← Q.insert(getDistance(v), v)
setLocator(v,l)
while !Q.isEmpty()
u ← Q.removeMin()
for all e ∈ G.incidentEdges(u)
z ← G.opposite(u,e)
r ← getDistance(u) + weight(e)
if r < getDistance(z)
setDistance(z,r)
setParent(z,e)
Q.replaceKey(getLocator(z),r)
Rappelez-vous que Sv deg(v) = 2m
Le temps d’exécution est O(m log n) parce que le graphe est
connexe
CSI2510 - ACM
17
L’algorithme de Kruskal
CSI2510 - ACM
18
L’algorithme de Kruskal
Chaque sommet est emmagasiné initialement comme son propre
groupe.
A chaque itération, l’arête de poids minimal dans le graphe est
ajoutée à l'arbre couvrant si elle relie 2 groupes distincts.
L'algorithme se termine quand tous les sommets sont dans le
même groupe.
Une file à priorité
emmagasine les arêtes
extérieures (hors du nuage)
clé: poids
élément: arête
À la fin de l’algorithme
On se retrouve avec un
nuage qui entoure
l’ACM
Un arbre T qui est notre
ACM
Ceci est une application de la propriété de partition!
Si l’arête minimale pour une itération donnée est (u,v), alors
si nous considérons une partition de G avec u dans un
groupe et v dans l’autre, alors la propriété de partition dit
qu’il doit y avoir un ACM contenant (u,v)
CSI2510 - ACM
Algorithm PrimJarnikMST(G)
Q ← new heap-based priority queue
s ← a vertex of G
for all v ∈ G.vertices()
if v = s
setDistance(v, 0)
else
setDistance(v, ∞)
setParent(v, ∅)
l ← Q.insert(getDistance(v), v)
setLocator(v,l)
while !Q.isEmpty()
u ← Q.removeMin()
for all e ∈ G.incidentEdges(u)
z ← G.opposite(u,e)
r ← weight(e)
if r < getDistance(z)
setDistance(z,r)
setParent(z,e)
Q.replaceKey(getLocator(z),r)
19
Algorithm KruskalMST(G)
Pour chaque sommet V dans G faire
définir Cloud(v) de {v}
Soit Q une file à priorité.
Insérer toutes les arête dans Q en
utilisant leur poids comme clé
T∅
Tant que T a moins que n-1 sommets
Faire
sommet e = Q.removeMin()
Soit u, v les extrémités de e
Si Cloud(v) ≠ Cloud(u) alors
ajouter arête e à T
Fusionner Cloud(v) et Cloud(u)
return T
CSI2510 - ACM
20
5
Structure de données pour l’algorithme
de Kruskal
Représentation d’une
partition:
L’algorithme maintient une forêt d’arbres
Une arête est acceptée, si elle relie deux arbres distincts
Nous avons besoin d’une structure de données qui maintient une
partition c.-à-d. une collection d’ensembles disjoints, avec les
opérations:
Chaque élément d’un ensemble est mis en mémoire dans
une séquence (l’ensemble pointe vers la séquence
contenant ces éléments)
Chaque élément à un pointeur vers l’ensemble
L’opération find(u) se fait en O(1) et retourne l’ensemble dont u fait
partie
Pour l’opération union(u,v), nous déplaçons les éléments du plus petit
ensemble dans la séquence du plus grand ensemble et nous mettons à
jour leur pointeur
La complexité en temps de union(u,v) est min(nu,nv), où nu et nv sont les
tailles des ensembles contenant u et v
find(u): retourne l’ensemble contenant u
union(u,v): remplace les ensembles contenant u et v
par leur union
Chaque fois qu’un élément est traité, il est mis dans un ensemble de
taille au moins double, donc chaque élément est traité au plus log n
fois
CSI2510 - ACM
21
Implémentation à base de partition
CSI2510 - ACM
Exemple
Une version à base de partition de l’algorithme de Kruskal
exécute les fusions des nuages comme des union(u,v) et les
tests comme des find(u)
23
9
5
1
G
8
B
Algorithm Kruskal(G):
Entrée: Un graphe pondéré G.
Sortie: Un ACM T pour G.
Soit P une partition des sommets de G, où chaque sommet est dans un ensemble séparé.
Soit Q une file à priorité gardant en mémoire les arêtes de G, ordonnées selon leur poids
Soit T un arbre initialement vide
Tant que T contient moins que n-1 arêtes faire
(u,v) ← Q.removeMinElement()
Si P.find(u) != P.find(v) alors
Temps: O((n+m)log n)
Ajouter (u,v) à T
P.union(u,v)
retourner T
CSI2510 - ACM
22
7
F
H
D
9
C 11
7
C 11
7
A
E
6
F
3
D
H
1
2
CSI2510 - ACM
F
3
H
D
2
G
9
5
C 11
7
A
10
6
8
B
4
4
E
10
G
8
5
9
5
1
2
10
B
A
6
3
C 11
A
1
E
G
8
B
4
E
4
6
F
3
D
H
2
10
24
6
Exemple (suite)
9
5
1
G
8
B
E
7
F
3
C 11
A
6
H
D
9
5
1
G
8
B
4
C 11
2
7
A
10
4
E
6
F
3
H
D
2
10
4 étapes
1
9
5
C 11
7
A
G
8
B
E
6
F
3
D
H
1
2
9
5
C 11
7
A
10
CSI2510 - ACM
G
8
B
4
E
4
6
F
3
D
H
2
10
25
7
Téléchargement