Skip to content
Snippets Groups Projects
Verified Commit 6e4b959e authored by orestis.malaspin's avatar orestis.malaspin
Browse files

maj 2024

parent a2e0cbb2
Branches
No related tags found
No related merge requests found
Pipeline #32300 passed
......@@ -718,7 +718,7 @@ pour sommet dans graphe et sommet non-visité
## Remarque
* `i` est la distance de plus cours chemin entre `v` et les sommets en cours de visite.
* `i` est la distance de plus courts chemin entre `v` et les sommets en cours de visite.
# Le parcours en largeur
......@@ -955,471 +955,3 @@ $$
* 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 (1 à 5)
* $D$ est le tableau des distances au sommet $1$: $D[7]$ est la distance de 1 à 7.
* Le chemin est pas forcément direct.
* $S$ est le tableau des sommets visités.
::: columns
:::: column
![Initialisation.](figs/dijkstra_0.png)
::::
:::: column
. . .
![1 visité, `D[2]=1`, `D[4]=3`.](figs/dijkstra_1.png)
::::
:::
# Algorithme de Dijkstra (1 à 5)
::: columns
:::: column
![Plus court est 2.](figs/dijkstra_1.png)
::::
:::: column
. . .
![2 visité, `D[3]=2`, `D[7]=3`.](figs/dijkstra_2.png)
::::
:::
# Algorithme de Dijkstra (1 à 5)
::: columns
:::: column
![Plus court est 3.](figs/dijkstra_2.png)
::::
:::: column
. . .
![3 visité, `D[7]=3` inchangé, `D[6]=6`.](figs/dijkstra_3.png)
::::
:::
# Algorithme de Dijkstra (1 à 5)
::: columns
:::: column
![Plus court est 4 ou 7.](figs/dijkstra_3.png)
::::
:::: column
. . .
![4 visité, `D[7]=3` inchangé, `D[5]=9`.](figs/dijkstra_4.png)
::::
:::
# Algorithme de Dijkstra (1 à 5)
::: columns
:::: column
![Plus court est `7`.](figs/dijkstra_4.png)
::::
:::: column
. . .
![7 visité, `D[5]=7`, `D[6]=6` inchangé.](figs/dijkstra_5.png)
::::
:::
# Algorithme de Dijkstra (1 à 5)
::: columns
:::: column
![Plus court est 6.](figs/dijkstra_5.png)
::::
:::: column
. . .
![`6` visité, `D[5]=7` inchangé.](figs/dijkstra_6.png)
::::
:::
# Algorithme de Dijkstra (1 à 5)
::: columns
:::: column
![Plus court est 5 et c'est la cible.](figs/dijkstra_6.png)
::::
:::: column
. . .
![The end, tous les sommets ont été visités.](figs/dijkstra_7.png)
::::
:::
# 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)
\footnotesize
. . .
```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)
// sélection de u t.q. la distance dans q est min
u = min(q, distance)
si u == t // on a atteint la cible
retourne distance
q = remove(q, u)
// voisin de u encore dans q
pour chaque v dans voisinage(u, q)
// on met à jour la distance du voisin en passant par u
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 `precedent`.
## Modifier le pseudo-code ci-dessus pour ce faire (3min matrix)
# Algorithme de Dijkstra
\footnotesize
```C
tab, tab dijkstra(graph, s, t)
pour chaque v dans graphe
distance[v] = infini
precedent[v] = indéfini
q = ajouter(q, v)
distance[s] = 0
tant que non_vide(q)
// sélection de u t.q. la distance dans q est min
u = min(q, distance)
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
precedent[v] = u
retourne distance, precedent
```
# Algorithme de Dijkstra
## Comment reconstruire un chemin ?
. . .
```C
pile parcours(precedent, s, t)
sommets = vide
u = t
// on a atteint t ou on ne connait pas de chemin
si u != s && precedent[u] != indéfini
tant que vrai
sommets = empiler(sommets, u)
u = precedent[u]
si u == s // la source est atteinte
retourne sommets
retourne sommets
```
# 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(element) // triviale
element enfiler(element, data, priorite)
data defiler(element)
rien changer_priorite(element, data, priorite)
nombre priorite(element) // utilitaire
```
## Pseudo-implémentation: structure (1min)
. . .
```C
struct element
data
priorite
element suivant
```
# Les files de priorité
## Pseudo-implémentation: enfiler (2min)
. . .
```C
element enfiler(element, data, priorite)
n_element = creer_element(data, priorite)
si est_vide(element)
retourne n_element
si priorite(n_element) > priorite(element)
n_element.suivant = element
retourne n_element
sinon
tmp = element
prec = element
tant que !est_vide(tmp) && priorite < priorite(tmp)
prec = tmp
tmp = tmp.suivant
prev.suivant = n_element
n_element.suivant = tmp
retourne element
```
# Les files de priorité
## Pseudo-implémentation: defiler (2min)
. . .
```C
data, element defiler(element)
si est_vide(element)
retourne AARGL!
sinon
tmp = element.data
n_element = element.suivant
liberer(element)
retourne tmp, n_element
```
# Algorithme de Dijkstra avec file de priorité min
```C
distance, precedent dijkstra(graphe, s, t):
distance[source] = 0
fp = file_p_vide()
pour v dans sommets(graphe)
si v != s
distance[v] = infini
precedent[v] = indéfini
fp = enfiler(fp, v, distance[v])
tant que !est_vide(fp)
u, fp = defiler(fp)
pour v dans voisinage de u
n_distance = distance[u] + w(u, v)
si n_distance < distance[v]
distance[v] = n_distance
precedent[v] = u
fp = changer_priorite(fp, v, n_distance)
retourne distance, precedent
```
# 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 = defiler(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_priorite(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)
![L'exercice.](figs/dijkstra_exo.png){width=60%}
* Donner la liste de priorité, puis...
## A chaque étape donner:
* Le tableau des distances à `a`;
* Le tableau des prédécesseurs;
* L'état de la file de priorité.
# Algorithme de Dijkstra (corrigé)
![Le corrigé partie 1.](figs/dijkstra_ex_0.png)
# Algorithme de Dijkstra (corrigé)
![Le corrigé partie 2.](figs/dijkstra_ex_1.png)
# Algorithme de Dijkstra (corrigé)
![Le corrigé partie 3.](figs/dijkstra_ex_2.png)
# Algorithme de Dijkstra (corrigé)
![Le corrigé partie 4.](figs/dijkstra_ex_3.png)
# Algorithme de Dijkstra (corrigé)
![Le corrigé partie 5.](figs/dijkstra_ex_4.png)
# Algorithme de Dijkstra (corrigé)
![Le corrigé partie 6.](figs/dijkstra_ex_5.png)
# Limitation de l'algorithme de Dijkstra
## Que se passe-t-il pour?
![Exemple.](figs/exemple_neg.png){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.
---
title: "Théorie des graphes: plus court chemin"
date: "2024-05-28"
---
# Rappel
## Comment représente-t-on un graphe?
. . .
* Matrice ou liste d'ajdacence
# Rappel: Matrice d'adjacence
::: columns
:::: column
## Exemple
```{.mermaid format=pdf width=400 loc=figs/}
graph LR;
1---2;
1---4;
2---5;
4---5;
5---3;
```
::::
:::: column
\footnotesize
## Quelle matrice d'adjacence?
. . .
```
|| 1 | 2 | 3 | 4 | 5
===||===|===|===|===|===
1 || 0 | 1 | 0 | 1 | 0
---||---|---|---|---|---
2 || 1 | 0 | 0 | 0 | 1
---||---|---|---|---|---
3 || 0 | 0 | 0 | 0 | 1
---||---|---|---|---|---
4 || 1 | 0 | 0 | 0 | 1
---||---|---|---|---|---
5 || 0 | 1 | 1 | 1 | 0
```
::::
:::
# Rappel: La liste d'adjacence
::: columns
:::: column
## Exemple
![Un graphe non-orienté.](figs/ex_graph_adj.pdf){width=80%}
::::
:::: column
## Quelle liste d'adjacence?
. . .
![La liste d'adjacence.](figs/ex_graph_list_adj.pdf)
::::
:::
# Algorithmes de plus courts chemins
\Huge
Algorithmes de plus courts chemins
# 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`.
# 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 (1 à 5)
* $D$ est le tableau des distances au sommet $1$: $D[7]$ est la distance de 1 à 7.
* Le chemin est pas forcément direct.
* $S$ est le tableau des sommets visités.
::: columns
:::: column
![Initialisation.](figs/dijkstra_0.png)
::::
:::: column
. . .
![1 visité, `D[2]=1`, `D[4]=3`.](figs/dijkstra_1.png)
::::
:::
# Algorithme de Dijkstra (1 à 5)
::: columns
:::: column
![Plus court est 2.](figs/dijkstra_1.png)
::::
:::: column
. . .
![2 visité, `D[3]=2`, `D[7]=3`.](figs/dijkstra_2.png)
::::
:::
# Algorithme de Dijkstra (1 à 5)
::: columns
:::: column
![Plus court est 3.](figs/dijkstra_2.png)
::::
:::: column
. . .
![3 visité, `D[7]=3` inchangé, `D[6]=6`.](figs/dijkstra_3.png)
::::
:::
# Algorithme de Dijkstra (1 à 5)
::: columns
:::: column
![Plus court est 4 ou 7.](figs/dijkstra_3.png)
::::
:::: column
. . .
![4 visité, `D[7]=3` inchangé, `D[5]=9`.](figs/dijkstra_4.png)
::::
:::
# Algorithme de Dijkstra (1 à 5)
::: columns
:::: column
![Plus court est `7`.](figs/dijkstra_4.png)
::::
:::: column
. . .
![7 visité, `D[5]=7`, `D[6]=6` inchangé.](figs/dijkstra_5.png)
::::
:::
# Algorithme de Dijkstra (1 à 5)
::: columns
:::: column
![Plus court est 6.](figs/dijkstra_5.png)
::::
:::: column
. . .
![`6` visité, `D[5]=7` inchangé.](figs/dijkstra_6.png)
::::
:::
# Algorithme de Dijkstra (1 à 5)
::: columns
:::: column
![Plus court est 5 et c'est la cible.](figs/dijkstra_6.png)
::::
:::: column
. . .
![The end, tous les sommets ont été visités.](figs/dijkstra_7.png)
::::
:::
# 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)
\footnotesize
. . .
```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)
// sélection de u t.q. la distance dans q est min
u = min(q, distance)
si u == t // on a atteint la cible
retourne distance
q = remove(q, u)
// voisin de u encore dans q
pour chaque v dans voisinage(u, q)
// on met à jour la distance du voisin en passant par u
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 `precedent`.
## Modifier le pseudo-code ci-dessus pour ce faire (3min matrix)
# Algorithme de Dijkstra
\footnotesize
```C
tab, tab dijkstra(graph, s, t)
pour chaque v dans graphe
distance[v] = infini
precedent[v] = indéfini
q = ajouter(q, v)
distance[s] = 0
tant que non_vide(q)
// sélection de u t.q. la distance dans q est min
u = min(q, distance)
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
precedent[v] = u
retourne distance, precedent
```
# Algorithme de Dijkstra
## Comment reconstruire un chemin ?
. . .
```C
pile parcours(precedent, s, t)
sommets = vide
u = t
// on a atteint t ou on ne connait pas de chemin
si u != s && precedent[u] != indéfini
tant que vrai
sommets = empiler(sommets, u)
u = precedent[u]
si u == s // la source est atteinte
retourne sommets
retourne sommets
```
# 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(element) // triviale
element enfiler(element, data, priorite)
data defiler(element)
rien changer_priorite(element, data, priorite)
nombre priorite(element) // utilitaire
```
## Pseudo-implémentation: structure (1min)
. . .
```C
struct element
data
priorite
element suivant
```
# Les files de priorité
## Pseudo-implémentation: enfiler (2min)
. . .
```C
element enfiler(element, data, priorite)
n_element = creer_element(data, priorite)
si est_vide(element)
retourne n_element
si priorite(n_element) > priorite(element)
n_element.suivant = element
retourne n_element
sinon
tmp = element
prec = element
tant que !est_vide(tmp) && priorite < priorite(tmp)
prec = tmp
tmp = tmp.suivant
prev.suivant = n_element
n_element.suivant = tmp
retourne element
```
# Les files de priorité
## Pseudo-implémentation: defiler (2min)
. . .
```C
data, element defiler(element)
si est_vide(element)
retourne AARGL!
sinon
tmp = element.data
n_element = element.suivant
liberer(element)
retourne tmp, n_element
```
# Algorithme de Dijkstra avec file de priorité min
```C
distance, precedent dijkstra(graphe, s, t):
distance[source] = 0
fp = file_p_vide()
pour v dans sommets(graphe)
si v != s
distance[v] = infini
precedent[v] = indéfini
fp = enfiler(fp, v, distance[v])
tant que !est_vide(fp)
u, fp = defiler(fp)
pour v dans voisinage de u
n_distance = distance[u] + w(u, v)
si n_distance < distance[v]
distance[v] = n_distance
precedent[v] = u
fp = changer_priorite(fp, v, n_distance)
retourne distance, precedent
```
# 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 = defiler(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_priorite(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)
![L'exercice.](figs/dijkstra_exo.png){width=60%}
* Donner la liste de priorité, puis...
## A chaque étape donner:
* Le tableau des distances à `a`;
* Le tableau des prédécesseurs;
* L'état de la file de priorité.
# Algorithme de Dijkstra (corrigé)
![Le corrigé partie 1.](figs/dijkstra_ex_0.png)
# Algorithme de Dijkstra (corrigé)
![Le corrigé partie 2.](figs/dijkstra_ex_1.png)
# Algorithme de Dijkstra (corrigé)
![Le corrigé partie 3.](figs/dijkstra_ex_2.png)
# Algorithme de Dijkstra (corrigé)
![Le corrigé partie 4.](figs/dijkstra_ex_3.png)
# Algorithme de Dijkstra (corrigé)
![Le corrigé partie 5.](figs/dijkstra_ex_4.png)
# Algorithme de Dijkstra (corrigé)
![Le corrigé partie 6.](figs/dijkstra_ex_5.png)
# Limitation de l'algorithme de Dijkstra
## Que se passe-t-il pour?
![Exemple.](figs/exemple_neg.png){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.
# Plus cours chemin pour toute paire de sommets
## Comment faire pour avoir toutes les paires?
. . .
* Appliquer Dijkstra sur tous les sommets d'origine.
* Complexité:
* Graphe dense: $\mathcal{O}(|V|)\mathcal{O}(|V|^2\log|V|)=\mathcal{O}(|V|^3\log|V|)$.
* Graphe peu dense: $\mathcal{O}(|V|)\mathcal{O}(|V|\log|V|)=\mathcal{O}(|V|^2\log|V|)$.
. . .
## Solution alternative: Floyd--Warshall
* Pour toutes paires de sommets $u,v\in V$, trouver le chemin de poids minimal reliant $u$ à $v$.
* Complexité $\mathcal{O}(|V|^3)$, indiqué pour graphes denses.
* Fonctionne avec la matrice d'adjacence.
# Algorithme de Floyd--Warshall
## Idée générale
* Soit l'ensemble de sommets $V=\{1, 2, 3, 4, ..., n\}$.
* Pour toute paire de sommets, $i,j$, on considère tous les chemins passant par les sommets intermédiaires $\in\{1, 2, ..., k\}$ avec $k\leq n$.
* On garde pour chaque $k$ la plus petite valeur.
## Principe
* A chaque étape, $k$, on vérifie s'il est plus court d'aller de $i$ à $j$ en passant par le sommet $k$.
* Si à l'étape $k-1$, le coût du parcours est $p$, on vérifie si $p$ est plus petit que $p_1+p_2$, le chemin de $i$ à $k$, et $k$ à $j$ respectivement.
# Algorithme de Floyd--Warshall
## The algorithme
Soit $d_{ij}(k)$ le plus court chemin de $i$ à $j$ passant par les sommets $\in\{1,2,...,k\}$
$$
d_{ij}(k)=\left\{
\begin{array}{ll}
w(i,j), & \mbox{si } k=0,\\
\min(d_{ij}(k-1),d_{ik}(k-1)+d_{kj}(k-1)), & \mbox{sinon}.
\end{array}
\right.
$$
# Algorithme de Floyd--Warshall (exemple)
::: columns
:::: column
![Le graphe, $D=w$.](figs/floyd_exemple.png)
::::
:::: column
## Que vaut $D^{(0)}$ (3min)?
. . .
$$
D^{(0)}=\begin{bmatrix}
0 & 2 & 4 & \infty & 3 \\
2 & 0 & 8 & \infty & 1 \\
6 & 2 & 0 & 4 & 3 \\
1 & \infty & \infty & 0 & 5 \\
\infty & \infty & \infty & 1 & 0 \\
\end{bmatrix}
$$
::::
:::
# Algorithme de Floyd--Warshall (exemple)
::: columns
:::: column
## On part de $D^{(0)}$?
$$
D^{(0)}=\begin{bmatrix}
0 & 2 & 4 & \infty & 3 \\
2 & 0 & 8 & \infty & 1 \\
6 & 2 & 0 & 4 & 3 \\
1 & \infty & \infty & 0 & 5 \\
\infty & \infty & \infty & 1 & 0 \\
\end{bmatrix}
$$
::::
:::: column
## Que vaut $D^{(1)}$ (3min)?
. . .
$$
D^{(0)}=\begin{bmatrix}
0 & 2 & 4 & \infty & 3 \\
2 & 0 & \mathbf{6} & \infty & 1 \\
6 & 2 & 0 & 4 & 3 \\
1 & \mathbf{3} & \mathbf{5} & 0 & \mathbf{4} \\
\infty & \infty & \infty & 1 & 0 \\
\end{bmatrix}
$$
::::
:::
# Algorithme de Floyd--Warshall (exemple)
::: columns
:::: column
## On part de $D^{(0)}$
$$
D^{(0)}=\begin{bmatrix}
0 & 2 & 4 & \infty & 3 \\
2 & 0 & 8 & \infty & 1 \\
6 & 2 & 0 & 4 & 3 \\
1 & \infty & \infty & 0 & 5 \\
\infty & \infty & \infty & 1 & 0 \\
\end{bmatrix}
$$
::::
:::: column
## Que vaut $D^{(1)}$ (3min)?
. . .
$$
D^{(1)}=\begin{bmatrix}
0 & 2 & 4 & \infty & 3 \\
2 & 0 & \mathbf{6} & \infty & 1 \\
6 & 2 & 0 & 4 & 3 \\
1 & \mathbf{3} & \mathbf{5} & 0 & \mathbf{4} \\
\infty & \infty & \infty & 1 & 0 \\
\end{bmatrix}
$$
## Exemple
$$
D_{42}^{(1)}=D_{41}^{(0)}+D_{12}^{(0)}=1+2<\infty.
$$
::::
:::
# Algorithme de Floyd--Warshall (exemple)
::: columns
:::: column
## On part de $D^{(1)}$
$$
D^{(1)}=\begin{bmatrix}
0 & 2 & 4 & \infty & 3 \\
2 & 0 & 6 & \infty & 1 \\
6 & 2 & 0 & 4 & 3 \\
1 & 3 & 5 & 0 & 4 \\
\infty & \infty & \infty & 1 & 0 \\
\end{bmatrix}
$$
::::
:::: column
## Que vaut $D^{(2)}$ (3min)?
. . .
$$
D^{(2)}=\begin{bmatrix}
0 & 2 & 4 & \infty & 3 \\
2 & 0 & 6 & \infty & 1 \\
\mathbf{4} & 2 & 0 & 4 & 3 \\
1 & 3 & 5 & 0 & 4 \\
\infty & \infty & \infty & 1 & 0 \\
\end{bmatrix}
$$
::::
:::
# Algorithme de Floyd--Warshall (exemple)
::: columns
:::: column
## On part de $D^{(2)}$
$$
D^{(2)}=\begin{bmatrix}
0 & 2 & 4 & \infty & 3 \\
2 & 0 & 6 & \infty & 1 \\
4 & 2 & 0 & 4 & 3 \\
1 & 3 & 5 & 0 & 4 \\
\infty & \infty & \infty & 1 & 0 \\
\end{bmatrix}
$$
::::
:::: column
## Que vaut $D^{(3)}$ (3min)?
. . .
$$
D^{(3)}=\begin{bmatrix}
0 & 2 & 4 & \mathbf{8} & 3 \\
2 & 0 & 6 & \mathbf{10} & 1 \\
4 & 2 & 0 & 4 & 3 \\
1 & 3 & 5 & 0 & 4 \\
\infty & \infty & \infty & 1 & 0 \\
\end{bmatrix}
$$
::::
:::
# Algorithme de Floyd--Warshall (exemple)
::: columns
:::: column
## On part de $D^{(3)}$
$$
D^{(3)}=\begin{bmatrix}
0 & 2 & 4 & 8 & 3 \\
2 & 0 & 6 & 10 & 1 \\
4 & 2 & 0 & 4 & 3 \\
1 & 3 & 5 & 0 & 4 \\
\infty & \infty & \infty & 1 & 0 \\
\end{bmatrix}
$$
::::
:::: column
## Que vaut $D^{(4)}$ (3min)?
. . .
$$
D^{(4)}=\begin{bmatrix}
0 & 2 & 4 & 8 & 3 \\
2 & 0 & 6 & 10 & 1 \\
4 & 2 & 0 & 4 & 3 \\
1 & 3 & 5 & 0 & 4 \\
\mathbf{2} & \mathbf{4} & \mathbf{6} & 1 & 0 \\
\end{bmatrix}
$$
::::
:::
# Algorithme de Floyd--Warshall (exemple)
::: columns
:::: column
## On part de $D^{(4)}$
$$
D^{(4)}=\begin{bmatrix}
0 & 2 & 4 & 8 & 3 \\
2 & 0 & 6 & 10 & 1 \\
4 & 2 & 0 & 4 & 3 \\
1 & 3 & 5 & 0 & 4 \\
2 & 4 & 6 & 1 & 0 \\
\end{bmatrix}
$$
::::
:::: column
## Que vaut $D^{(5)}$ (3min)?
. . .
$$
D^{(5)}=\begin{bmatrix}
0 & 2 & 4 & \mathbf{4} & 3 \\
2 & 0 & 6 & \mathbf{2} & 1 \\
4 & 2 & 0 & 4 & 3 \\
1 & 3 & 5 & 0 & 4 \\
2 & 4 & 6 & 1 & 0 \\
\end{bmatrix}
$$
::::
:::
# Algorithme de Floyd--Warshall
## The pseudo-code (10min)
* Quelle structure de données?
* Quelle initialisation?
* Quel est le code pour le calcul de la matrice $D$?
# Algorithme de Floyd--Warshall
## The pseudo-code
* Quelle structure de données?
```C
int distance[n][n];
```
. . .
* Quelle initialisation?
```C
matrice ini_floyd_warshall(distance, n, w)
pour i de 1 à n
pour j de 1 à n
distance[i][j] = w(i,j)
retourne distance
```
# Algorithme de Floyd--Warshall
## The pseudo-code
* Quel est le code pour le calcul de la matrice $D$?
```C
matrice floyd_warshall(distance, n, w)
pour k de 1 à n
pour i de 1 à n
pour j de 1 à n
distance[i][j] = min(distance[i][j],
distance[i][k] + distance[k][j])
retourne distance
```
# Algorithme de Floyd--Warshall
## La matrice de précédence
* On a pas encore vu comment reconstruire le plus court chemin!
* On définit, $P_{ij}^{(k)}$, qui est le prédécesseur du sommet $j$ depuis $i$ avec les sommets intermédiaires $\in\{1, 2, ..., k\}$.
$$
P^{(0)}_{ij}=\left\{
\begin{array}{ll}
\mbox{vide}, & \mbox{si } i=j\mbox{, ou }w(i,j)=\infty\\
i, & \mbox{sinon}.
\end{array}
\right.
$$
* Mise à jour
$$
P^{(k)}_{ij}=\left\{
\begin{array}{ll}
P^{(k-1)}_{\mathbf{i}j}, & \mbox{si } d_{ij}^{(k)}\leq d_{ik}^{(k-1)}+d_{kj}^{(k-1)}\\
P^{(k-1)}_{\mathbf{k}j}, & \mbox{sinon}.
\end{array}
\right.
$$
. . .
* Moralité: si le chemin est plus court en passant par $k$, alors il faut utiliser son prédécesseur!
# Algorithme de Floyd--Warshall
## La matrice de précédence (pseudo-code, 3min)
. . .
```C
matrice, matrice floyd_warshall(distance, n, w)
pour k de 1 à n
pour i de 1 à n
pour j de 1 à n
n_distance = distance[i][k] + distance[k][j]
if n_distance < distance[i][j]
distance[i][j] = n_distance
précédence[i][j] = précédence[k][j]
retourne distance, précédence
```
# Algorithme de Floyd--Warshall (exercice)
::: columns
:::: column
![Le graphe, $D=w$.](figs/floyd_exemple.png)
::::
:::: column
## Que vaut $P^{(0)}$ (3min)?
. . .
$$
P^{(0)}=\begin{bmatrix}
- & 1 & 1 & - & 1 \\
2 & - & 2 & - & 2 \\
3 & 3 & - & 3 & 3 \\
4 & - & - & - & 4 \\
- & - & - & 5 & - \\
\end{bmatrix}
$$
::::
:::
# Algorithme de Floyd--Warshall (exercice)
::: columns
:::: column
![Le graphe, $D=w$.](figs/floyd_exemple.png)
::::
:::: column
## Que vaut $P^{(5)}$ (10min)?
. . .
$$
P^{(5)}=\begin{bmatrix}
- & 1 & 1 & 5 & 1 \\
2 & - & 1 & 5 & 2 \\
2 & 3 & - & 3 & 3 \\
4 & 1 & 1 & - & 1 \\
4 & 1 & 1 & 5 & - \\
\end{bmatrix}
$$
::::
:::
# Exercice: retrouver le chemin entre 1 et 4 (5min)
$$
P=\begin{bmatrix}
- & 1 & 1 & 5 & 1 \\
2 & - & 1 & 5 & 2 \\
2 & 3 & - & 3 & 3 \\
4 & 1 & 1 & - & 4 \\
4 & 1 & 1 & 5 & - \\
\end{bmatrix}
$$
. . .
## Solution
* Le sommet $5=P_{14}$, on a donc, $5\rightarrow 4$, on veut connaître le prédécesseur de 5.
* Le sommet $1=P_{15}$, on a donc, $1\rightarrow 5\rightarrow 4$. The end.
# Exercice complet
## Appliquer l'algorithme de Floyd--Warshall au graphe suivant
![The exorcist.](figs/floyd_exercice.png){width=50%}
* Bien indiquer l'état de $D$ et $P$ à chaque étape!
* Ne pas oublier de faire la matrice d'adjacence évidemment...
# La suite
* Sans transition.... la suite!
# Trouver un réseau électrique pour
![Ces maisons n'ont pas d'électricité.](figs/arbre_couvrant_vide.png)
# Solution: pas optimale
![Le réseau simple, mais nul.](figs/arbre_couvrant_mal.png)
* La longueur totale des câbles est super longue!
# Solution: optimale
![Le meilleur réseau.](figs/arbre_couvrant_bien.png)
# 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 on part? 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.
. . .
![Exemple d'arbres couvrants d'un graphe connexe.](figs/arbre_couvrant_exemples.png)
# Arbres couvrants
* Quels algorithmes que nous avons déjà vus permettent de construire des arbres couvrants?
. . .
* Les parcours en largeur et en profondeur!
. . .
![Graphe, et parcours comme arbres couvrants.](figs/arbres_couvrants_parcours.png)
# 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?
![Un graphe, connexe, non-orienté, pondéré, et son arbre couvrant minimal.](figs/arbre_couvrant_minimal_exemple.png)
# Algorithme de Prim
::: columns
:::: column
## Un exemple
![Le graphe de départ.](figs/prim_0.png)
::::
:::: column
## On part de `e` (au hasard)
![Le sommet `e` est couvert.](figs/prim_1.png)
::::
:::
# Algorithme de Prim
::: columns
:::: column
## On choisit comment?
![Quelle arête choisir?](figs/prim_1.png)
. . .
* 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`
![Le sommet `d` est couvert.](figs/prim_2.png)
::::
:::
# Algorithme de Prim
::: columns
:::: column
## On choisit comment?
![Quelle arête choisir?](figs/prim_2.png)
. . .
* 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`
![Le sommet `a` est couvert.](figs/prim_3.png)
::::
:::
# Algorithme de Prim
::: columns
:::: column
## On choisit comment?
![Quelle arête choisir?](figs/prim_3.png)
. . .
* 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`
![Le sommet `c` est couvert.](figs/prim_4.png)
::::
:::
# Algorithme de Prim
::: columns
:::: column
## On choisit comment?
![Quelle arête choisir?](figs/prim_4.png)
. . .
* 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`
![Le sommet `b` est couvert.](figs/prim_5.png)
* Game over!
::::
:::
# 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)
r = aléatoire(graphe)
distance[r] = 0
parent[r] = indéfini
fp = file_p_vide()
pour v dans sommets(graphe)
si v != r
distance[v] = infini
parent[v] = indéfini
fp = enfiler(fp, v, distance[v])
retourne fp, distance, parent
```
# Algorithme de Prim
## Algorithme: Pseudo-code (5min)
. . .
```C
sommets, parent prim(file_priorité, distance, parent)
sommets = vide
tant que !est_vide(file_priorité)
u, fp = défiler(file_priorité)
sommets = insérer(sommets, u)
pour v dans voisinage de u et pas dans sommets
// ou dans file_priorité
si w(u, v) < distance[v]
parent[w] = u
distance[w] = w(u, v)
fp = changer_priorité(fp, w, w(u, v))
retourne sommets, parent
```
# Exemple d'algorithme de Prim
::: columns
:::: {.column width="40%"}
## Un exemple
![Étape 1.](figs/prim_1.png)
::::
:::: 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
![Étape 2.](figs/prim_2.png)
::::
:::: 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
![Étape 3.](figs/prim_3.png)
::::
:::: 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
![Étape 4.](figs/prim_4.png)
::::
:::: 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
![Étape 5.](figs/prim_4.png)
::::
:::: 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 |
```
::::
:::
# Exercice: algorithme de Prim
## Appliquer l'algorithme de Prim à (15min):
![En démarrant du sommet $V_1$.](figs/prim_exercice.png)
# Exercice: algorithme de Prim
## Solution
![](figs/prim_solution.png)
# Complexité de l'algorithme de Prim
\footnotesize
```C
file_priorité, distance, parent initialisation(graphe)
// choix r et initialisation
pour v dans sommets(graphe)
O(|V|) // initialisation distance et parent
fp = enfiler(fp, v, distance[v])
retourne fp, distance, parent
sommets, parent prim(file_priorité, distance, parent)
sommets = vide
tant que !est_vide(file_priorité)
O(|V|) u, fp = défiler(file_priorité)
sommets = insérer(sommets, u)
pour v dans voisinage de u et pas dans sommets
O(|E|) si w(u, v) < distance[v]
// màj dista + parent
O(|V|) fp = changer_priorité(fp, w, w(u, v))
retourne sommets, 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 parcourues qu'une fois en **tout**.
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment