diff --git a/slides/cours_24.md b/slides/cours_24.md new file mode 100644 index 0000000000000000000000000000000000000000..f20a9fd1e7c95a7280feda6d8a82e0fdce792dbf --- /dev/null +++ b/slides/cours_24.md @@ -0,0 +1,696 @@ +--- +title: "Graphes - Plus court chemin" +date: "2023-05-24" +--- + +# Rappel du dernier cours + +* Qu'est-ce qu'un graphe? Un graphe orienté? Un graphe pondéré? + +. . . + +* Ensemble de sommets et arêtes, avec une direction, possédant une pondération. +* Comment représenter un graphe informatiquement? + +. . . + +* Liste ou matrice d'adjacence. +* Quel est le parcours que nous avons vu? + +. . . + +* Le parcours en largeur. + +# Le parcours en largeur + +## Le pseudo-code + +* Utilisation d'une **file** + +```C +initialiser(graphe) // tous sommets sont non-visités +file = visiter(sommet, vide) // sommet est un sommet du graphe au hasard +tant que !est_vide(file) + v = défiler(file) + file = visiter(v, file) + +file visiter(sommet, file) + sommet = visité + pour w = chaque arête de sommet + si w != visité + file = enfiler(file, w) + retourne file +``` + +# Complexité du parcours en largeur + +## Étape 1 + +* Extraire un sommet de la file; + +## Étape 2 + +* Traîter tous les sommets adjacents. + +## Quelle est la complexité? + +. . . + +* Étape 1: $\mathcal{O}(|V|)$, +* Étape 2: $\mathcal{O}(2|E|)$, +* Total: $\mathcal{O}(|V| + |2|E|)$. + +# Exercice + +* Établir la liste d'adjacence et appliquer l'algorithme de parcours en largeur au graphe + +```{.mermaid format=pdf width=400 loc=figs/} +graph LR; + 1---2; + 1---3; + 1---4; + 2---3; + 2---6; + 3---6; + 3---4; + 3---5; + 4---5; +``` + + +# Illustration: parcours en profondeur + +{width=80%} + +# Parcours en profondeur + +## Idée générale + +* Initialiser les sommets comme non-lus +* Visiter un sommet +* Pour chaque sommet visité, on visite un sommet adjacent s'il est pas encore visité récursivement. + +## Remarque + +* La récursivité est équivalent à l'utilisation d'une **pile**. + +# Parcours en profondeur + +## Pseudo-code (5min) + +. . . + +```C +initialiser(graphe) // tous sommets sont non-visités +pile = visiter(sommet, vide) // sommet est un sommet du graphe au hasard +tant que !est_vide(pile) + v = dépiler(pile) + pile = visiter(v, pile) +``` + +## Que fait visiter? + +. . . + +```C +pile visiter(sommet, pile) + sommet = visité + pour w = chaque arête de sommet + si w != visité + pile = empiler(pile, w) + retourne pile +``` + + +# Exercice + +* Établir la liste d'adjacence et appliquer l'algorithme de parcours en profondeur au graphe + +```{.mermaid format=pdf width=400 loc=figs/} +graph LR; + 1---2; + 1---3; + 1---4; + 2---3; + 2---6; + 3---6; + 3---4; + 3---5; + 4---5; +``` + +# Interprétation des parcours + +* Un graphe vu comme espace d'états (sommet: état, arête: action); + * Labyrinthe; + * Arbre des coups d'un jeu. + +. . . + +* BFS (Breadth-First) ou DFS (Depth-First) parcourent l'espace des états à la recherche du meilleur mouvement. + * Les deux parcourent *tout* l'espace; + * Mais si l'arbre est grand, l'espace est gigantesque! + +. . . + +* Quand on a un temps limité + * BFS explore beaucoup de coups dans un futur proche; + * DFS explore peu de coups dans un futur lointain. + + +## Contexte: les réseaux (informatique, transport, etc.) + +* Graphe orienté; +* Source: sommet `s`; +* Destination: sommet `t`; +* Les arêtes ont des poids (coût d'utilisation, distance, etc.); +* Le coût d'un chemin est la somme des poids des arêtes d'un chemin. + +## Problème à résoudre + +* Quel est le plus court chemin entre `s` et `t`. + +# Exemples d'application de plus court chemin + +## Devenir riches! + +* On part d'un tableau de taux de change entre devises. +* Quelle est la meilleure façon de convertir l'or en dollar? + +{width=80%} + +. . . + +* 1kg d'or => 327.25 dollars +* 1kg d'or => 208.1 livres => 327 dollars +* 1kg d'or => 455.2 francs => 304.39 euros => 327.28 dollars + +# Exemples d'application de plus court chemin + +## Formulation sous forme d'un graphe: Comment (3min)? + +{width=80%} + + +# Exemples d'application de plus court chemin + +## Formulation sous forme d'un graphe: Comment (3min)? + +{width=60%} + +* Un sommet par devise; +* Une arête orientée par transaction possible avec le poids égal au taux de change; +* Trouver le chemin qui maximise le produit des poids. + +. . . + +## Problème + +* On aimerait plutôt avoir une somme... + + +# Exemples d'application de plus court chemin + +## Conversion du problème en plus court chemin + +* Soit `taux(u, v)` le taux de change entre la devise `u` et `v`. +* On pose `w(u,w)=-log(taux(u,v))` +* Trouver le chemin poids minimal pour les poids `w`. + +{width=60%} + +* Cette conversion se base sur l'idée que + +$$ +\log(u\cdot v)=\log(u)+\log(v). +$$ + +# Applications de plus courts chemins + +## Quelles applications voyez-vous? + +. . . + +* Déplacement d'un robot; +* Planificaiton de trajet / trafic urbain; +* Routage de télécommunications; +* Réseau électrique optimal; +* ... + +# Plus courts chemins à source unique + +* Soit un graphe, $G=(V, E)$, une fonction de pondération $w:E\rightarrow\mathbb{R}$, et un sommet $s\in V$ + * Trouver pour tout sommet $v\in V$, le chemin de poids minimal reliant $s$ à $v$. +* Algorithmes standards: + * Dijkstra (arêtes de poids positif seulement); + * Bellman-Ford (arêtes de poids positifs ou négatifs, mais sans cycles). +* Comment résoudre le problèmes si tous les poids sont les mêmes? + +. . . + +* Un parcours en largeur! + +# Algorithme de Dijkstra + +## Comment chercher pour un plus court chemin? + +. . . + +``` +si distance(u,v) > distance(u,w) + distance(w,v) + on passe par w plutôt qu'aller directement +``` + +# Algorithme de Dijkstra + +## Idée générale + +* On assigne à chaque noeud une distance $0$ pour $s$, $\infty$ pour les autres. +* Tous les noeuds sont marqués non-visités. +* Depuis du noeud courant, on suit chaque arête du noeud vers un sommet non visité et on calcule le poids du chemin à chaque voisin et on met à jour sa distance si elle est plus petite que la distance du noeud. +* Quand tous les voisins du noeud courant ont été visités, le noeud est mis à visité (il ne sera plus jamais visité). +* Continuer avec le noeud à la distance la plus faible. +* L'algorithme est terminé losrque le noeud de destination est marqué comme visité, ou qu'on a plus de noeuds qu'on peut visiter et que leur distance est infinie. + +# Algorithme de Dijkstra + +## Pseudo-code (5min, matrix) + +. . . + +```C +tab dijkstra(graph, s, t) + pour chaque v dans graphe + distance[v] = infini + q = ajouter(q, v) + distance[s] = 0 + tant que non_vide(q) + u = min(q, distance) // plus petite distance dans q + si u == t + retourne distance + q = remove(q, u) + // voisin de u encore dans q + pour chaque v dans voisinage(u, q) + n_distance = distance[u] + w(u, v) + si n_distance < distance[v] + distance[v] = n_distance + retourne distance +``` + +# Algorithme de Dijkstra + +* Cet algorithme, nous donne le plus court chemin mais... +* ne nous donne pas le chemin! + +## Comment modifier l'algorithme pour avoir le chemin? + +. . . + +* Pour chaque nouveau noeud à visiter, il suffit d'enregistrer d'où on est venu! +* On a besoin d'un tableau `précédent`. + +## Modifier le pseudo-code ci-dessus pour ce faire (3min matrix) + +# Algorithme de Dijkstra + +```C +tab, tab dijkstra(graph, s, t) + pour chaque v dans graphe + distance[v] = infini + précédent[v] = indéfini + q = ajouter(q, v) + distance[s] = 0 + tant que non_vide(q) + u = min(q, distance) // plus petite distance dans q + si u == t + retourne distance + q = remove(q, u) + // voisin de u encore dans q + pour chaque v dans voisinage(u, q) + n_distance = distance[u] + w(u, v) + si n_distance < distance[v] + distance[v] = n_distance + précédent[v] = u + retourne distance, précédent +``` + +# Algorithme de Dijkstra + +## Comment reconstruire un chemin ? + +. . . + +```C +pile parcours(précédent, s, t) + sommets = vide + u = t + // on a atteint t ou on ne connait pas de chemin + si u != s && précédent[u] != indéfini + tant que vrai + sommets = empiler(sommets, u) + u = précédent[u] + si u == s // la source est atteinte + retourne sommets + retourne sommets +``` + +# Algorithme de Dijkstra + +::: columns + +:::: column + + + +:::: + +:::: column + +. . . + +![1 visité, `D[2]=1`, `D[4]=3`.](figs/dijkstra_1.png) + +:::: + +::: + +# Algorithme de Dijkstra + +::: columns + +:::: column + + + + +:::: + +:::: column + +. . . + +![2 visité, `D[3]=2`, `D[7]=3`.](figs/dijkstra_2.png) + +:::: + +::: + +# Algorithme de Dijkstra + +::: columns + +:::: column + + + + +:::: + +:::: column + +. . . + +![3 visité, `D[7]=3` inchangé, `D[6]=6`.](figs/dijkstra_3.png) + +:::: + +::: + +# Algorithme de Dijkstra + + +::: columns + +:::: column + + + + +:::: + +:::: column + +. . . + +![4 visité, `D[7]=3` inchangé, `D[5]=9`.](figs/dijkstra_4.png) + +:::: + +::: + +# Algorithme de Dijkstra + +::: columns + +:::: column + + + + +:::: + +:::: column + +. . . + +![7 visité, `D[5]=7`, `D[6]=6` inchangé.](figs/dijkstra_5.png) + +:::: + +::: + +# Algorithme de Dijkstra + +::: columns + +:::: column + + + + +:::: + +:::: column + +. . . + +![`6` visité, `D[5]=7` inchangé.](figs/dijkstra_6.png) + +:::: + +::: + +# Algorithme de Dijkstra + +::: columns + +:::: column + + + +:::: + +:::: column + +. . . + + + +:::: + +::: + +# Algorithme de Dijkstra amélioré + +## On peut améliorer l'algorithme + +* Avec une file de priorité! + +## Une file de priorité est + +* Une file dont chaque élément possède une priorité, +* Elle existe en deux saveurs: `min` ou `max`: + * File `min`: les éléments les plus petits sont retirés en premier. + * File `max`: les éléments les plus grands sont retirés en premier. +* On regarde l'implémentation de la `max`. + +## Comment on fait ça? + +. . . + +* On insère les éléments à haute priorité tout devant dans la file! + +# Les files de priorité + +## Trois fonction principales + +```C +booléen est_vide(élément) // triviale +élément enfiler(élément, data, priorité) +data défiler(élément) +rien modifier_priorité(élément, data, priotié) +nombre priorité(data) // utilitaire +``` + +## Pseudo-implémentation: structure (1min) + +. . . + +```C +struct élément + data + priorité + élément suivant +``` + +# Les files de priorité + +## Pseudo-implémentation: enfiler (2min) + +. . . + +```C +élément enfiler(élément, data, priorité) + n_élément = créer_élément(data, priorité) + si est_vide(élément) + retourne n_élément + si priorité(d) > priorité(e.d) + n_élément.suivant = élément + retourne n_élément + sinon + tmp = élément + préc = élément + tant que !est_vide(tmp) && priorité < tmp.priorité + préc = tmp + tmp = tmp.suivant + prev.suivant = n_élément + n_élément.suivant = tmp + retourne élément +``` + +# Les files de priorité + +## Pseudo-implémentation: défiler (2min) + +. . . + +```C +data, élément défiler(élément) + si est_vide(élément) + retourne AARGL! + sinon + tmp = élément.data + n_élément = élément.suivant + libérer(élément) + retourne tmp, n_élément +``` + +# Algorithme de Dijkstra avec file de priorité min + +```C +distance, précédent dijkstra(graphe, s, t): + distance[source] = 0 + fp = file_p_vide() + pour v dans sommets(graphe) + si v != s + distance[v] = infini + précédent[v] = indéfini + fp = enfiler(fp, v, distance[v]) + tant que !est_vide(fp) + u, fp = défiler(fp) + pour v dans voisinage de u + n_distance = distance[u] + w(u, v) + si n_distance < distance[v] + distance[v] = n_distance + précédent[v] = u + fp = changer_priorité(fp, v, n_distance) + retourne distance, précédent +``` + +# Algorithme de Dijkstra avec file + +\footnotesize + +```C +distance dijkstra(graphe, s, t) +--------------------------------------------------------- + pour v dans sommets(graphe) +O(V) si v != s + distance[v] = infini +O(V) fp = enfiler(fp, v, distance[v]) // notre impl est nulle +------------------O(V * V)------------------------------- + tant que !est_vide(fp) +O(1) u, fp = défiler(fp) +--------------------------------------------------------- +O(E) pour v dans voisinage de u + n_distance = distance[u] + w(u, v) + si n_distance < distance[v] + distance[v] = n_distance +O(V) fp = changer_priorité(fp, v, n_distance) +--------------------------------------------------------- + retourne distance +``` + +* Total: $\mathcal{O}(|V|^2+|E|\cdot |V|)$: + * Graphe dense: $\mathcal{O}(|V|^3)$ + * Graphe peu dense: $\mathcal{O}(|V|^2)$ + +# Algorithme de Dijkstra avec file + +## On peut faire mieux + +* Avec une meilleure implémentation de la file de priorité: + * Tas binaire: $\mathcal{O}(|V|\log|V|+|E|\log|V|)$. + * Tas de Fibonnacci: $\mathcal{O}(|V|+|E|\log|V|)$ +* Graphe dense: $\mathcal{O}(|V|^2\log|V|)$. +* Graphe peu dense: $\mathcal{O}(|V|\log|V|)$. + +# Algorithme de Dijkstra (exercice, 5min) + +{width=60%} + +* Donner la liste de priorité, puis... + +## A chaque étape donner: + +* Le tableau des distances à `a`; +* Le tableau des prédécessueurs; +* L'état de la file de priorité. + +# Algorithme de Dijkstra (corrigé) + + + +# Algorithme de Dijkstra (corrigé) + + + +# Algorithme de Dijkstra (corrigé) + + + +# Algorithme de Dijkstra (corrigé) + + + +# Algorithme de Dijkstra (corrigé) + + + +# Algorithme de Dijkstra (corrigé) + + + +# Limitation de l'algorithme de Dijkstra + +## Que se passe-t-il pour? + +{width=50%} + +## Quel est le problème? + +. . . + +* L'algorithme n'essaiera jamais le chemin `s->x->y->v` et prendra direct `s->v`. +* Ce problème n'apparaît que s'il y a des poids négatifs. + diff --git a/slides/figs/dijkstra_0.png b/slides/figs/dijkstra_0.png new file mode 100644 index 0000000000000000000000000000000000000000..1febf9947095679582659dee7bbf4e689302d8f9 Binary files /dev/null and b/slides/figs/dijkstra_0.png differ diff --git a/slides/figs/dijkstra_1.png b/slides/figs/dijkstra_1.png new file mode 100644 index 0000000000000000000000000000000000000000..2f855cfe367646f1bf50efffddb085e273aed9b2 Binary files /dev/null and b/slides/figs/dijkstra_1.png differ diff --git a/slides/figs/dijkstra_2.png b/slides/figs/dijkstra_2.png new file mode 100644 index 0000000000000000000000000000000000000000..cbb7a2b5a0024d84a9122a7e1283e7eb0d918f34 Binary files /dev/null and b/slides/figs/dijkstra_2.png differ diff --git a/slides/figs/dijkstra_3.png b/slides/figs/dijkstra_3.png new file mode 100644 index 0000000000000000000000000000000000000000..4027bfa27dfb1d63d6aaaebf03eb609fe9102985 Binary files /dev/null and b/slides/figs/dijkstra_3.png differ diff --git a/slides/figs/dijkstra_4.png b/slides/figs/dijkstra_4.png new file mode 100644 index 0000000000000000000000000000000000000000..f3375e8e6b71c095a8bbb19fa14c6b6a05cbdd14 Binary files /dev/null and b/slides/figs/dijkstra_4.png differ diff --git a/slides/figs/dijkstra_5.png b/slides/figs/dijkstra_5.png new file mode 100644 index 0000000000000000000000000000000000000000..d3471865579113425840eb0bad9b472a61f674c9 Binary files /dev/null and b/slides/figs/dijkstra_5.png differ diff --git a/slides/figs/dijkstra_6.png b/slides/figs/dijkstra_6.png new file mode 100644 index 0000000000000000000000000000000000000000..531a6342f83e35d0b99343542075715e0dbec18a Binary files /dev/null and b/slides/figs/dijkstra_6.png differ diff --git a/slides/figs/dijkstra_7.png b/slides/figs/dijkstra_7.png new file mode 100644 index 0000000000000000000000000000000000000000..5ec0cf7df3d0357b19602b9af892cd96d44bd859 Binary files /dev/null and b/slides/figs/dijkstra_7.png differ diff --git a/slides/figs/dijkstra_ex_0.png b/slides/figs/dijkstra_ex_0.png new file mode 100644 index 0000000000000000000000000000000000000000..2419bf8cf77ec49ac2719621e2280323bf595f7c Binary files /dev/null and b/slides/figs/dijkstra_ex_0.png differ diff --git a/slides/figs/dijkstra_ex_1.png b/slides/figs/dijkstra_ex_1.png new file mode 100644 index 0000000000000000000000000000000000000000..d5775128a1c16691afa847486da9d7e767de1e37 Binary files /dev/null and b/slides/figs/dijkstra_ex_1.png differ diff --git a/slides/figs/dijkstra_ex_2.png b/slides/figs/dijkstra_ex_2.png new file mode 100644 index 0000000000000000000000000000000000000000..6b5857a170e77303c6ae95d5bfcc902df598344d Binary files /dev/null and b/slides/figs/dijkstra_ex_2.png differ diff --git a/slides/figs/dijkstra_ex_3.png b/slides/figs/dijkstra_ex_3.png new file mode 100644 index 0000000000000000000000000000000000000000..f5e39113cfb75cb06b1d418e1e044986433a9836 Binary files /dev/null and b/slides/figs/dijkstra_ex_3.png differ diff --git a/slides/figs/dijkstra_ex_4.png b/slides/figs/dijkstra_ex_4.png new file mode 100644 index 0000000000000000000000000000000000000000..1cde97d16dcbb54de1b68c16e6144efc77a932ef Binary files /dev/null and b/slides/figs/dijkstra_ex_4.png differ diff --git a/slides/figs/dijkstra_ex_5.png b/slides/figs/dijkstra_ex_5.png new file mode 100644 index 0000000000000000000000000000000000000000..7f3b1d73b3561fa964198133691b8b2cf59ba022 Binary files /dev/null and b/slides/figs/dijkstra_ex_5.png differ diff --git a/slides/figs/dijkstra_exo.png b/slides/figs/dijkstra_exo.png new file mode 100644 index 0000000000000000000000000000000000000000..2478a3febe25b5b5be58488edbd3bd7faf58acd3 Binary files /dev/null and b/slides/figs/dijkstra_exo.png differ diff --git a/slides/figs/exemple_neg.png b/slides/figs/exemple_neg.png new file mode 100644 index 0000000000000000000000000000000000000000..56fe75a23580b955eb62487b9aa3c7a97874bdb2 Binary files /dev/null and b/slides/figs/exemple_neg.png differ