Funciones avanzadas disponibles en la aplicación
L’algorithmique des graphes est un domaine fondamental en informatique, notamment pour résoudre des problèmes de plus court chemin, de parcours, et d’optimisation. Parmi les algorithmes les plus célèbres pour trouver le plus court chemin dans un graphe pondéré, l’algorithme de Dijkstra occupe une place centrale.
Cette fiche de révision présente une étude détaillée de l’algorithme de Dijkstra, suivi d’un comparatif entre trois algorithmes classiques de recherche de chemin en OCaml : Dijkstra, Bellman-Ford, et Floyd-Warshall.
L’algorithme de Dijkstra permet de résoudre le problème du plus court chemin à partir d’un sommet source vers tous les autres sommets dans un graphe orienté ou non orienté, avec des poids non négatifs.
Problème formel :
Étant donné un graphe [Formule] avec un poids [Formule], trouver la distance minimale [Formule] entre un sommet source [Formule] et chaque sommet [Formule].
Initialiser une liste des distances [Formule] avec [Formule] et [Formule] pour [Formule].
Maintenir un ensemble de sommets non visités.
À chaque itération, sélectionner le sommet non visité [Formule] avec la plus petite distance estimée [Formule].
Mettre à jour les distances des voisins [Formule] de [Formule] via la relaxation :
[Formule mathématique]
Répéter jusqu’à ce que tous les sommets soient visités ou que les distances ne puissent plus être améliorées.
let dijkstra graph source =
let dist = Array.make (Array.length graph) max_int in
dist.(source) <- 0;
let visited = Array.make (Array.length graph) false in
for _ = 0 to (Array.length graph - 1) do
(* Trouver le sommet u non visité avec la plus petite distance *)
let u = ref (-1) in
let min_dist = ref max_int in
for i = 0 to (Array.length graph - 1) do
if not visited.(i) && dist.(i) < !min_dist then (
u := i;
min_dist := dist.(i)
)
done;
if !u = -1 then () else (
visited.(!u) <- true;
(* Relaxation *)
List.iter (fun (v, w) ->
if dist.(!u) <> max_int && dist.(!u) + w < dist.(v) then
dist.(v) <- dist.(!u) + w
) graph.(!u)
)
done;
dist
Nous allons comparer les algorithmes suivants :
| Algorithme | Type de graphes | Poids négatifs | Complexité | Utilisation principale |
|---|---|---|---|---|
| Dijkstra | Graphes avec poids positifs | Non | [Formule] | Plus court chemin à partir d’une source |
| Bellman-Ford | Graphes avec poids négatifs | Oui | [Formule] | Plus court chemin avec détection de cycles négatifs |
| Floyd-Warshall | Graphes denses (matrice) | Oui | [Formule] | Plus court chemin entre toutes paires de sommets |
let bellman_ford graph source =
let dist = Array.make (Array.length graph) max_int in
dist.(source) <- 0;
for _ = 1 to (Array.length graph - 1) do
for u = 0 to (Array.length graph - 1) do
List.iter (fun (v, w) ->
if dist.(u) <> max_int && dist.(u) + w < dist.(v) then
dist.(v) <- dist.(u) + w
) graph.(u)
done
done;
(* Vérification des cycles négatifs *)
let has_negative_cycle = ref false in
for u = 0 to (Array.length graph - 1) do
List.iter (fun (v, w) ->
if dist.(u) <> max_int && dist.(u) + w < dist.(v) then
has_negative_cycle := true
) graph.(u)
done;
if !has_negative_cycle then failwith "Cycle négatif détecté";
dist
Pour tout [Formule],
[Formule mathématique]
où [Formule] est la distance minimale entre [Formule] et [Formule] en utilisant uniquement les sommets [Formule] à [Formule] comme intermédiaires.
let floyd_warshall graph =
let n = Array.length graph in
let dist = Array.init n (fun i -> Array.init n (fun j -> if i = j then 0 else graph.(i).(j))) in
for k = 0 to n - 1 do
for i = 0 to n - 1 do
for j = 0 to n - 1 do
if dist.(i).(k) + dist.(k).(j) < dist.(i).(j) then
dist.(i).(j) <- dist.(i).(k) + dist.(k).(j)
done
done
done;
dist
| Critère | Dijkstra | Bellman-Ford | Floyd-Warshall |
|---|---|---|---|
| Type de graphes | Graphes avec arêtes positives | Graphes avec poids négatifs | Graphes avec poids négatifs |
| Poids négatifs | Non | Oui | Oui |
| Complexité | [Formule] (avec tas) | [Formule] | [Formule] |
| Résultat | Plus court chemin depuis une source | Plus court chemin depuis une source + détection cycle négatif | Plus court chemin entre toutes paires |
| Mémoire | [Formule] | [Formule] | [Formule] |
| Cas d’utilisation | Graphes clairsemés, poids positifs | Graphes avec poids négatifs, détection cycle | Graphes denses ou toutes paires |
| Avantages | Rapide si pas de poids négatifs | Tolère poids négatifs + cycle | Résolution complète pour toutes paires |
| Inconvénients | Impossible avec poids négatifs | Plus lent que Dijkstra | Coût élevé en temps computation |
Considérons le graphe suivant, représenté sous forme de liste d’adjacence :
let graph = [|
[(1, 7); (2, 9); (5, 14)];
[(0, 7); (2, 10); (3, 15)];
[(0, 9); (1, 10); (3, 11); (5, 2)];
[(1, 15); (2, 11); (4, 6)];
[(3, 6); (5, 9)];
[(0, 14); (2, 2); (4, 9)]
|]
Pour calculer les plus courts chemins depuis le sommet 0 :
let distances = dijkstra graph 0;;
Array.iteri (fun i d -> Printf.printf "Distance de 0 à %d : %d\n" i d) distances;;
Résultat attendu :
Distance de 0 à 0 : 0
Distance de 0 à 1 : 7
Distance de 0 à 2 : 9
Distance de 0 à 3 : 20
Distance de 0 à 4 : 20
Distance de 0 à 5 : 11
[Diagramme]
L’algorithme de Dijkstra est un outil puissant et efficace pour résoudre le problème du plus court chemin dans les graphes à poids positifs. Cependant, selon la nature du graphe (présence de poids négatifs, nécessité de connaître tous les plus courts chemins), il est important de choisir l’algorithme adapté :
En OCaml, ces algorithmes peuvent être implémentés de manière concise et fonctionnelle, offrant à la fois lisibilité et efficacité.
Citation importante :
"Choisir le bon algorithme est essentiel pour optimiser à la fois la complexité temporelle et la mémoire selon les contraintes du problème et la nature du graphe."
