MP* Lycée Masséna TP 5 : Graphes pondérés Télécharger le fichier sur le site web contenant des fonctions et quelques exemples de graphes. 1 Implémentation de l’algorithme de Floyd-Warshall Question 1. Implémentez l’algorithme de Floyd Warshall, rappelé ci-dessous. On utilisera pour cela le type zbar. #floyd_warshall g1 ;; - : zbar vect vect = [|[|Z 0; Z 1; Z -3; Z 2; Z -4|]; [|Z 3; Z 0; Z -4; Z 1; Z -1|]; [|Z 7; Z 4; Z 0; Z 5; Z 3|]; [|Z 2; Z -1; Z -5; Z 0; Z -2|]; [|Z 8; Z 5; Z 1; Z 6; Z 0|]|] #floyd_warshall gneg ;; - : zbar vect vect = [|[|Z -1; Z 1; Z -2; Inf; Inf|]; [|Z -3; Z -1; Z -4; Inf; Inf|]; [|Z -1; Z 1; Z -2; Inf; Inf|]; [|Z 1; Z 3; Z 0; Z 0; Z 5|]; [|Z -3; Z -1; Z -4; Inf; Z 0|]|] Algorithme 1 : Algorithme de Floyd-Warshall Entrée : Un graphe pondéré G = (V, E, ω) donné par sa matrice d’adjacence M Sortie : La matrice des plus courtes (δ(i, j))0≤i,j<n distances entre deux sommets quelconques du graphe A ← copie(M ); pour tout k entre 0 et n − 1 faire faire pour tout i entre 0 et n − 1 faire faire pour tout j entre 0 et n − 1 faire faire ai,j ← min(ai,j , ai,k + ak,j ) Renvoyer A −5 1 4 2 3 7 3 8 1 0 6 4 -4 2 Figure 1: Le graphe du cours (graphe g1) Question 2. Modifiez l’algorithme pour tester l’existence d’un circuit de poids total strictement négatif. #floyd_warshall gneg ;; Uncaught exception: Failure "cycle de poids strictement negatif" Question 3. Modifier l’algorithme pour calculer en même temps la matrice de liaison Π = (πi,j )0≤i,j<n , définie par : le prédecesseur de j dans un plus court chemin de i à j, s’il en existe un πi,j = −1 sinon (on pourrait mettre autre chose) Svartz Page 1/4 2015/2016 MP* Lycée Masséna #floyd_warshall g1 ;; - : zbar vect vect * int vect vect = [|[|Z 0; Z 1; Z -3; Z 2; Z -4|]; [|Z 3; Z 0; Z -4; Z 1; Z -1|]; [|Z 7; Z 4; Z 0; Z 5; Z 3|]; [|Z 2; Z -1; Z -5; Z 0; Z -2|]; [|Z 8; Z 5; Z 1; Z 6; Z 0|]|], [|[|0; 2; 3; 4; 0|]; [|3; 1; 3; 1; 0|]; [|3; 2; 2; 1; 0|]; [|3; 2; 3; 3; 0|]; [|3; 2; 3; 4; 4|]|] Question 4. Écrire une fonction impression pi i j d’impression à l’écran d’un plus court chemin entre i et j s’il en existe un. #imprimer_chemin pi 0 1 ;; 0 4 3 2 1 - : unit = () Question 5. Pour G = (V, E) un graphe (non pondéré), on appelle fermeture transitive de G le graphe Gf = (V, Ef ) avec (i, j) ∈ Ef si et seulement si il existe un chemin de i à j dans G. Écrire un algorithme capable de calculer la fermeture transitive d’un graphe avec un algorithme inspiré de l’algorithme de Floyd-Warshall, mais travaillant sur des matrices de booléens. #warshall g2 ;; - : int vect vect = [|[|0; 1; 1; 0; 0; [|0; 0; 1; 0; 0; [|0; 0; 0; 0; 0; [|1; 1; 1; 0; 1; [|1; 1; 1; 1; 0; [|0; 1; 1; 0; 0; [|0; 1; 1; 0; 0; [|0; 0; 1; 0; 0; [|0; 0; 1; 0; 0; [|0; 0; 0; 0; 0; [|0; 1; 1; 0; 0; [|0; 1; 1; 0; 0; [|0; 0; 0; 0; 0; [|0; 0; 0; 0; 0; 2 1; 1; 0; 1; 1; 0; 1; 0; 0; 0; 1; 1; 0; 0; 1; 1; 0; 1; 1; 1; 0; 0; 0; 0; 1; 1; 0; 0; 1; 1; 0; 1; 1; 1; 1; 0; 0; 0; 1; 1; 0; 0; 1; 1; 1; 1; 1; 1; 1; 1; 0; 0; 1; 1; 0; 0; 0; 0; 0; 1; 1; 0; 0; 0; 0; 0; 0; 0; 0; 0; 1; 1; 0; 1; 1; 1; 1; 0; 0; 0; 0; 1; 0; 0; 1; 1; 0; 1; 1; 1; 1; 0; 0; 0; 1; 0; 0; 0; 1; 1; 0; 1; 1; 1; 1; 0; 0; 0; 1; 1; 0; 1; 1|]; 1|]; 0|]; 1|]; 1|]; 1|]; 1|]; 0|]; 0|]; 0|]; 1|]; 1|]; 1|]; 0|]|] Implémentation de l’algorithme de Dijkstra Les graphes sous représentations creuses sont donnés sous le type (int * int) list vect, un couple (u, ω) dans la liste d’adjacence d’un somme t signifie qu’il y a un arc (t, u) dans le graphe, de poids ω. Algorithme 2 : Algorithme de Dijkstra Entrée : Un graphe pondéré G = (V, E, ω) donné par listes d’adjacences, avec ω(E) ⊂ R+ , un sommet s Sortie : Les distances δ(s, t) pour tout t ∈ V d[t] ← +∞ pour tout t ∈ V ; d[s] ← 0; E ← ∅ ; F ← {s}; tant que F 6= ∅ faire u ← Retirer de F un sommet v vérifiant d[v] minimal parmi les sommets de F ; pour tout voisin v de u faire si v n’est ni dans F ni dans E alors Ajouter v à F d[v] ← min(d[v], d[u] + ω(u, v)) Ajouter u à E Renvoyer d Vous trouverez sur le site web une implémentation des files de priorité spécialement adaptée à l’algorithme de Dijkstra. Le type associé est le suivant : type fp_min = {tas: (int * int) vect ; indice: int vect ; mutable nb: int } ;; Svartz Page 2/4 2015/2016 MP* Lycée Masséna Les fonctions sont les suivantes : — creer_fp : int -> fp_min : prend en paramètre un entier n, et retourne une file de priorité min de capacité n, vide. — est_vide_fp : fp_min -> bool — inserer : fp_min -> int -> int -> unit : l’appel inserer f t dt rajoute le sommet t, supposé non présent, à la file de priorité, avec priorité dt. — diminuer_cle : fp_min -> int -> int -> unit : l’appel diminuer_cle f t dt diminue la clé du sommet t, supposé présent. — retirer_min : fp_min -> int * int : retire le minimum de la file de priorité (i.e renvoie (t, dt ) avec dt minimal). Exceptée la création, toutes ces fonctions s’exécutent en temps constant, sauf les opérations classiques de files de priorité qui s’exécutent en temps O(log n)) (n étant la capacité de la file). Remarquez l’utilisation d’un vecteur indice : à tout moment indice.(v) contient l’indice dans le tas du couple associé au nœud v (s’il est dans la file). Ceci est transparent pour l’utilisateur. 0 1 0 10 2 5 7 3 4 9 2 4 2 6 3 Figure 2: Un graphe pondéré à poids positifs (g3) Question 6. Implémentez l’algorithme de Dijkstra. La fonction dijkstra g s doit renvoyer un tableau de distances de type int vect contenant les distances entre s et tous les nœuds accessibles du graphe, et −1 pour les non accessibles. (Comme les poids sont positifs, il n’y a pas ambiguïté). # dijkstra g3 0;; - : int vect * int vect = [|0; 8; 9; 7; 5|], [|0; 4; 1; 4; 0|] #dijkstra g4 0 ;; - : int vect * int vect = [|0; 5; 1; 8; 3; 6|], [|0; 4; 0; 4; 2; 1|] #dijkstra g4 3 ;; - : int vect * int vect = [|-1; -1; -1; 0; -1; -1|], [|-1; -1; -1; 3; -1; -1|] Question 7. Rajouter la production d’un tableau de prédecesseurs, comme ci-dessus. Question 8. Profitez ensuite de l’implémentation pour réviser la structure de file de priorité. 3 Project-Euler 83 Dans la matrice ci-dessous, la somme minimale d’un chemin entre le coin en haut à gauche et le coin en bas à droite est 2297. Un trajet correspondant est indiqué en gras (naturellement, on ne peut se déplacer qu’horizontalement et verticalement, pas en diagonale). 131 201 630 537 805 Svartz 673 96 803 699 732 234 342 746 497 524 103 965 422 121 37 Page 3/4 18 150 111 956 331 2015/2016 MP* Lycée Masséna Sur mon site web, vous trouverez une matrice au format ml de taille 80 × 80. Déterminez la somme minimale d’un chemin entre le coin en haut à gauche et le coin en bas à droite, et résolvez ainsi le problème 83 du site Project-Euler. Indication : écrire une fonction permettant de construire le graphe associé à une matrice à coefficients positifs (il est très creux !), et lancez Dijkstra depuis 0. Ne pas oublier de rajouter le coefficient en haut à gauche à votre longueur de chemin. Vous pouvez aussi résoudre les problèmes 81 et 82. Ils sont en fait plus simples et une solution basée sur la programmation dynamique est possible, ce qui évite le recours aux graphes. C’est un bon exercice de révision. Svartz Page 4/4 2015/2016