--- title: "Arbres couvrants" date: "2025-06-06" --- # Arbres couvrants \Huge Les arbres couvrants # Trouver un réseau électrique pour  # Solution: pas optimale  * La longueur totale des câbles est super longue! # Solution: optimale  # Formalisation: Les arbres couvrants ## Application: minimisation des coûts * Équipement d'un lotissement avec des lignes électriques/téléphoniques, des canalisations, ... . . . * Pour réduire les coûts, on cherche à minimiser la longueur totale des câbles/tuyaux. . . . * Les lignes/tuyaux forment un *arbre couvrant*. . . . * La meilleure option est un *arbre couvrant minimal*. # Formalisation: Les arbres couvrants * Qu'est-ce qu'un arbre couvrant? Des idées? De quel objet part-on? Où va-t-on? . . . * Un arbre couvrant d'un graphe non-orienté et connexe est: * un arbre inclus dans le graphe qui connecte tous les sommets du graphe. . . .  # Arbres couvrants * Quels algorithmes que nous avons déjà vus, permettent de construire des arbres couvrants? . . . * Les parcours en largeur et en profondeur! . . .  # Arbres couvrants minimaux * Un *arbre couvrant minimal* est un sous-graphe d'un graphe non-orienté pondéré $G(V,E)$ tel quel: * C'est un arbre (graphe acyclique); * Il couvre tous les sommets de $G$ et contient $|V|-1$ arêtes; * Le coût total associé aux arêtes de l'arbre est minimum parmi tous les arbres couvrants possibles. . . . * Est-il unique? . . . * Pas forcément. # Arbres couvrants minimaux * Comment générer un arbre couvrant minimal?  # Algorithme de Prim ::: columns :::: column ## Un exemple  :::: :::: column ## On part de `e` (au hasard)  :::: ::: # Algorithme de Prim ::: columns :::: column ## On choisit comment?  . . . * L'arête la plus courte sortant d'un sommet déjà visité, et entrant dans un sommet non-visité. :::: :::: column . . . ## L'arête `e->d`  :::: ::: # Algorithme de Prim ::: columns :::: column ## On choisit comment?  . . . * L'arête la plus courte sortant d'un sommet déjà visité, et entrant dans un sommet non-visité. :::: :::: column . . . ## L'arête `d->a`  :::: ::: # Algorithme de Prim ::: columns :::: column ## On choisit comment?  . . . * L'arête la plus courte sortant d'un sommet déjà visité, et entrant dans un sommet non-visité. :::: :::: column . . . ## L'arête `d->c`  :::: ::: # Algorithme de Prim ::: columns :::: column ## On choisit comment?  . . . * L'arête la plus courte sortant d'un sommet déjà visité, et entrant dans un sommet non-visité. :::: :::: column . . . ## L'arête `e->b`  * Game over! :::: ::: # Exemple d'algorithme de Prim ::: columns :::: {.column width="40%"} ## Un exemple  :::: :::: column ``` FP | e | d | b | c | a | ---------------------------------- D | 0 | inf | inf | inf | inf | | e | d | b | c | a | ---------------------------------- P | - | - | - | - | - | ``` ## Devient? . . . ``` FP | d | b | c | a | ---------------------------- D | 4 | 5 | 5 | inf | | e | d | b | c | a | ---------------------------------- P | - | e | e | e | - | ``` :::: ::: # Exemple d'algorithme de Prim ::: columns :::: {.column width="40%"} ## Un exemple  :::: :::: column ``` FP | d | b | c | a | ---------------------------- D | 4 | 5 | 5 | inf | | e | d | b | c | a | ---------------------------------- P | - | e | e | e | - | ``` ## Devient? . . . ``` FP | a | c | b | ---------------------- D | 2 | 4 | 5 | | e | d | b | c | a | ---------------------------------- P | - | e | e | d | d | ``` :::: ::: # Exemple d'algorithme de Prim ::: columns :::: {.column width="40%"} ## Un exemple  :::: :::: column ``` FP | a | c | b | ---------------------- D | 2 | 4 | 5 | | e | d | b | c | a | ---------------------------------- P | - | e | e | d | d | ``` ## Devient? . . . ``` FP | c | b | ---------------- D | 4 | 5 | | e | d | b | c | a | ---------------------------------- P | - | e | e | d | d | ``` :::: ::: # Exemple d'algorithme de Prim ::: columns :::: {.column width="40%"} ## Un exemple  :::: :::: column ``` FP | c | b | ---------------- D | 4 | 5 | | e | d | b | c | a | ---------------------------------- P | - | e | e | d | d | ``` ## Devient? . . . ``` FP | b | ---------- D | 5 | | e | d | b | c | a | ---------------------------------- P | - | e | e | d | d | ``` :::: ::: # Exemple d'algorithme de Prim ::: columns :::: {.column width="40%"} ## Un exemple  :::: :::: column ``` FP | b | ---------- D | 5 | | e | d | b | c | a | ---------------------------------- P | - | e | e | d | d | ``` ## Devient? . . . ``` FP | ---- D | | e | d | b | c | a | ---------------------------------- P | - | e | e | d | d | ``` :::: ::: # Algorithme de Prim ## Structures de données * Dans quoi allons-nous stocker les sommets? . . . * File de priorité min. * Autre chose? . . . * Tableau des distances (comme pour Dijkstra). * Autre chose? . . . * Tableau des parents (presque comme pour Dijkstra). * Autre chose? . . . * Non. # Algorithme de Prim ## Initialisation: Pseudo-code (2min) . . . ```C file_priorité, distance, parent initialisation(graphe) s_initial = aléatoire(graphe) distance[s_initial] = 0 fp = file_p_vide() pour s_courant dans sommets(graphe) si s_courant != s_initial distance[s_courant] = infini parent[s_courant] = indéfini fp = enfiler(fp, s_courant, distance[s_courant]) retourne fp, distance, parent ``` # Algorithme de Prim \footnotesize ## Algorithme: Pseudo-code (5min) . . . ```C distance, parent prim(graphe) fp, distance, parent initialisation(graphe) sommets = vide tant que !est_vide(fp) s_courant, fp = défiler(fp) sommets = insérer(sommets, s_courant) pour s_voisin dans voisinage(s_courant) et pas dans sommets // ou dans fp si poids(s_courant, s_voisin) < distance[s_voisin] parent[s_voisin] = s_courant distance[s_voisin] = poids(s_courant, s_voisin) fp = changer_priorité(fp, s_voisin, poids(s_courant, s_voisin)) retourne distance, parent ``` # Exercice: algorithme de Prim ## Appliquer l'algorithme de Prim à (15min):  # Exercice: algorithme de Prim ## Solution  # Complexité de l'algorithme de Prim \footnotesize ```C file_priorité, distance, parent initialisation(graphe) // choix r et initialisation pour v dans sommets(graphe) // initialisation distance et parent en O(|V|) fp = enfiler(fp, v, distance[v]) retourne fp, distance, parent distance, parent prim(graphe) fp, distance, parent initialisation(graphe) // O(|V|) sommets = vide tant que !est_vide(fp) u, fp = défiler(fp) // O(|V|) sommets = insérer(sommets, u) pour v dans voisinage de u et pas dans sommets si poids(u, v) < distance[v] // O(|E|) // màj distance + parent fp = changer_priorité(fp, v, poids(u, v)) // O(|V|) retourne distance, parent ``` * $O(|V|)+O(|E|)+O(|V|^2)=O(|E|+|V|^2)$ * Remarque: $O(|E|)$ n'est pas mutliplié par $O(|V|)$, car les arêtes ne sont traitées qu'une fois en **tout**. # Algorithme de Kruskal * On ajoute les arêtes de poids minimal: * si cela ne crée pas de cycle; * on s'arrête quand on a couvert tout le graphe. . . . * Comment on fait ça? . . . * Faisons un exemple pour voir. # Algorithme de Kruskal: exemple ::: columns :::: column ## Un exemple  :::: :::: column ## On part de `(a, d)` (poids le plus faible)  :::: ::: # Algorithme de Kruskal: exemple ::: columns :::: column ## On continue avec `(c, d)`  :::: :::: column ## Résultat  :::: ::: # Algorithme de Kruskal: exemple ::: columns :::: column ## On continue avec `(d, e)`  :::: :::: column ## Résultat  :::: ::: # Algorithme de Kruskal: exemple ::: columns :::: column ## On continue avec `(b, e)`  :::: :::: column ## Résultat  :::: ::: # Algorithme de Kruskal: exemple ::: columns :::: column ## Mais pourquoi pas `(c, e)`?  :::: :::: column ## Résultat: un cycle  :::: ::: * Comment faire pour empêcher l'ajout de `(c, e)` ou `(a, c)`? . . . * Si les deux sommets sont déjà couverts nous sommes sauvés (presque)! # Algorithme de Kruskal ## L'initialisation * Créer un ensemble de sommets pour chaque de sommet du graphe ($V_1$, $V_2$, ...): * $V_1=\{v_1\}$, $V_2=\{v_2\}$, ... * S'il y a $n$ sommets, il y a $n$ $V_i$. * Initialiser l'ensemble $A$ des arêtes "sûres" constituant l'arbre couvrant minimal, $A=\emptyset$. * Initialiser l'ensemble des sommets couverts $F=\emptyset$. * Trier les arêtes par poids croissant dans l'ensemble $E$. ## Mise à jour * Tant qu'il reste plus d'un $V_i$: * Pour $(u,v)\in E$ à poids minimal: * Retirer $(u,v)$ de $E$. * Si $u\in V_i$ et $v\in V_j$ avec $V_i\cap V_j=\emptyset$: * Ajouter $(u,v)$ à $A$; * Fusionner $V_i$ et $V_j$ dans $F$. # Algorithme de Kruskal: exemple ::: columns :::: column  :::: :::: column :::: ::: # Algorithme de Kruskal: solution  # Algorithme de Kruskal: exercice ::: columns :::: column  :::: :::: column :::: ::: # Algorithme de Kruskal: solution 