M1algo Synthese C5 2425
M1algo Synthese C5 2425
M1algo Synthese C5 2425
15 octobre 2024
François Laroussinie
NB : Ces synthèses ont pour but de compléter les notes prises en cours. Elles ne les remplacent
pas ! En particulier, la plupart des preuves n’y figurent pas. Rappel : il faut programmer les
algorithmes vus en cours.
On a bien Di [q] = δ ≤i (s, q) (preuve vue en cours) et Dn−1 [q] = δ(s, q).
Le calcul effectué par l’algorithme de Bellman-Ford (cf algorithme 1) est un peu différent.
Il utilise un tableau unidimensionnel (même technique que précédemment pour économiser
de la mémoire) et la mise à jour de ce ses coefficients permet juste de s’assurer que l’on a
D[q] ≤ δ ≤i (s, q) après la i-ème itération (l’algorithme peut donc converger plus vite mais cela
ne sera pas détecté), ce qui nous suffit pour obtenir in fine D[q] = δ(s, q).
1
Procédure PCC-Bellman-Ford (G, s)
//G = (S, A, w) : un graphe orienté, valué avec w : A → R.
//s ∈ S : un sommet origine.
begin
pour chaque u ∈ S faire
Π[u] := nil
(
0 si u = s
d[u] :=
∞ sinon
pour i = 1 à |S| − 1 faire
pour chaque (u, v) ∈ A faire
si d[v] > d[u] + w(u, v) alors
d[v] := d[u] + w(u, v)
Π[v] := u
pour chaque (u, v) ∈ A faire
si d[v] > d[u] + w(u, v) alors
return (⊥, −, −)
return (⊤, d, Π)
Algorithme 1 : algorithme de Bellman-Ford
On note δij la distance δ(xi , xj ). L’idée clé est de calculer des coefficients dkij correspon-
dant à la distance d’un PCC entre xi et xj d’intérieur inclus dans {x1 , . . . , xk } (l’intérieur
d’un chemin est l’ensemble des sommets intermédiaires). La solution recherchée est donc les
coefficients dnij pour tout i, j.
Le point clé sur lequel est basé l’algorithme de Floyd-Warshall est la relation suivante
liant les durées minimales pour aller de i à j par des chemins d’intérieur {1, . . . , k} et celles
basées sur les chemins d’intérieur {1, . . . , k − 1} :
Cette relation est correcte car un PCC entre i et j d’intérieur inclu dans {1, . . . , k} est :
— soit d’intérieur inclu dans {1, . . . , k − 1} (et ne passe pas par k),
— soit il passe par k et alors la portion entre i et k n’a pas besoin de passer par k (sinon
il y aurait un cycle strictement négatif !) et c’est donc un PCC d’intérieur inclu dans
{1, . . . , k − 1}, et on a de même pour la portion entre k et j.
Comme pour les autres problèmes de programmation dynamique étudiés ici, on peut
améliorer la complexité en espace mémoire, et n’utiliser qu’une seule matrice. C’est ce qui est
fait dans l’algorithme 2. La complexité (en temps) de cet algorithme est en O(n3 ) (et utilise
un espace mémoire en O(n2 )).
2
Procédure PCC-Floyd (G)
//avec M = (αij )1≤i,j≤n la matrice corresp. à G
begin
pour i = 1 . . . n faire
pour j = 1 . . . n faire dij := αij
pour k = 1 . . . n faire
pour i = 1 . . . n faire
pour j = 1 . . . n faire
si dij > dik + dkj alors
dij := dik + dkj
return D, Π
Algorithme 2 : algorithme de Floyd-Warshall (avec économie de mémoire)
On peut distinguer deux variantes selon que l’on autorise à mettre plusieurs fois un même
élément dans le sac (on a, dans ce cas, plutôt une liste de types d’objets) ou pas. La variante
la plus classique est celle sans répétition.
Par exemple avec W = 10 et l’ensemble {(5, 20), (5, 2), (6, 3)}, la valeur maximale pour
un sac de capacité 10 est 22 pour la version sans répétition (avec les objets 1 et 2) et 40 si on
autorise des répétitions (avec deux objets 1).
En effet, dans un sac ”optimal” de capacité w avec les objets 1, . . . , i, il y a deux possibilités :
soit l’objet i est absent et la valeur du sac est alors K[i − 1, w], soit l’objet i est dedans et
alors sa valeur est vi + K[i − 1, w − wi ].
On obtient donc l’algorithme 3
Exemple : avec W = 10 et l’ensemble d’objets {(2, 5), (8, 22), (3, 8), (4, 5)} (NB : ce sont
des paires (w, v)). On obtient le tableau K ci-dessous :
0 1 2 3 4 5 6 7 8 9 10 11 12
− 0 0 0 0 0 0 0 0 0 0 0 0 0
(2, 5) 0 0 5 5 5 5 5 5 5 5 5 5 5
(8, 22) 0 0 5 5 5 5 5 5 22 22 27 27 27
(3, 8) 0 0 5 8 8 13 13 13 22 22 27 30 30
(4, 5) 0 0 5 8 8 13 13 13 22 22 27 30 30
On peut améliorer l’algorithme précédent en utilisant une seule ligne du tableau (en
veillant à énumérer correctement les indices !) : c’est l’algorithme 4.
3
Procédure SaDssRepet (O, W )
begin
//O correspond à la liste des n objets de poids wi et de valeur vi
K : tableau d’entiers de taille (n + 1) × (W + 1)
pour w = 1 . . . W faire K[0, w] := 0
pour i = 0 . . . n faire K[i, 0] := 0
pour i = 1 . . . n faire
pour w = 1 . . . W faire
si wi ≤ w alors K[i, w] := max(K[i − 1, w], vi + K[i − 1, w − wi ])
else K[i, w] := K[i − 1, w]
return K[n, W ]
Algorithme 3 : algorithme de base pour le SAD sans répétition.
Et l’on peut adapter l’algorithme pour calculer aussi un ensemble d’objets optimal (c-à-d.
dont la valeur totale est K[n, W ] et dont le poids est inférieur ou égal à W ). C”st l’algorithme 5.
NB : c’est de prendre K[i, w − wi ] (et non K[i − 1, w − wi ]) qui permet de mettre plusieurs
occurrences du même objet i !
On en déduit l’algorithme 6 dont la complexité en temps et en mémoire est en O(n · W ).
Exemple : avec W = 10 et l’ensemble d’objets {(2, 5), (8, 22), (3, 8), (4, 5)}. On obtient le
tableau K ci-dessous :
0 1 2 3 4 5 6 7 8 9 10 11 12
− 0 0 0 0 0 0 0 0 0 0 0 0 0
(2, 5) 0 0 5 5 10 10 15 15 20 20 25 25 30
(8, 22) 0 0 5 5 10 10 15 15 22 22 27 27 32
(3, 8) 0 0 5 8 10 13 16 18 22 24 27 30 32
(4, 5) 0 0 5 8 10 13 16 18 22 24 27 30 32
4
Procédure SaDssRepet3 (O, W )
begin
//O correspond à la liste des n objets de poids wi et de valeur vi
K : tableau d’entiers de taille W + 1
Sol : tableau d’ensembles (listes) de taille W + 1
pour w = 0 . . . W faire
K[0, w] := 0
Sol[0, w] := ∅
pour i = 1 . . . n faire
pour w = W . . . wi faire
si K[i − 1, w] < vi + K[i − 1, w − wi ] alors
K[i, w] := vi + K[i − 1, w − wi ]
Sol[i, w] = Sol[i − 1, w − wi ] ∪ {(wi , vi )}
else
K[i, w] := K[i − 1, w]
Sol[i, w] = Sol[i − 1, w]
return K[n, W ]
Algorithme 5 : algorithme par le SAD sans répétition mais avec calcul de la solution.
5
Procédure SaDavecRepet2 (O, W )
begin
//O correspond à la liste des n objets de poids wi et de valeur vi
K : tableau d’entiers de taille W + 1
pour w = 0 . . . W faire K[w] := 0
pour i = 1 . . . n faire
pour w = wi . . . W faire
K[w] := max(K[w], vi + K[w − wi ])
return K[n, W ]
Algorithme 7 : algorithme pour le SAD avec répétition avec économie de mémoire.