Complexité des algorithmes
Algorithmique 1 - 2021-2022
Stéphane Grandcolas
Aix-Marseille Université
Contact : [email protected]
Algorithmique 1
Objectifs :
I introduire les structures de données et les techniques de
conception de base de l’algorithmique,
I étudier les outils d’analyse et de preuve de correction des
algorithmes.
Modalités de contrôle :
session 1 : NF = 0, 8 × ET + 0, 2 × CC
session 2 : NF = ET
Contrôle continu : quatre TP évalués.
Algorithmique 1
Programme :
I algorithmes : complexité, tris,
I structures de données : arbres, tas, dictionnaires,
I méthodes : backtracking, programmation dynamique,
I graphes : plus courts chemins, arbres couvrants.
Livre de référence : Introduction à l’algorithmique, Thomas H. Cormen, Charles E.
Leiserson, Ronald L. Rivest, Clifford Stein.
Algorithmes et structures de données
P : un problème
M : une méthode pour résoudre le problème P
Algorithme : description de la méthode M dans un
langage algorithmique
du nom du mathématicien perse Al Khuwarizmi (780 - 850)
Algorithmes et structures de données
P : un problème
M : une méthode pour résoudre le problème P
Algorithme : description de la méthode M dans un
langage algorithmique
du nom du mathématicien perse Al Khuwarizmi (780 - 850)
Structure de données : manière d’organiser et de stocker
les données (supposée rendre efficace certaines opérations)
Structures algorithmiques
Structures de contrôle
I séquence
I embranchement (ou sélection)
I boucle (ou itération)
Structures de données : supports
I constantes, variables
I tableaux
I structures récursives (listes, arbres, graphes)
Complexité des algorithmes
On veut
I Evaluer l’efficacité de la méthode M
I Comparer M avec une autre méthode M0
indépendamment de l’environnement (machine, système,
compilateur, . . . )
Complexité des algorithmes
Evaluation du nombre d’opérations élémentaires en fonction
I de la taille des données (par ex. le nombre d’éléments à trier)
I de la nature des données (provoquant par ex. la sortie d’une
boucle)
Notations :
I n : taille des données,
I T (n) : nombre d’opérations élémentaires
Configurations caractéristiques :
I meilleur cas,
I pire des cas,
I cas moyen.
Evaluation de T (n) (séquence)
Somme des coûts.
Traitement1 T1 (n)
T (n) = T1 (n) + T2 (n)
Traitement2 T2 (n)
Evaluation de T (n) (embranchement)
Max des coûts.
si < condition > alors Tc (n)
Traitement1 T1 (n)
sinon
Traitement2 T2 (n)
Tc (n) + max(T1 (n), T2 (n))
Evaluation de T (n) (boucle)
Somme des coûts des passages successifs
tant que < condition > faire Ci (n)
Traitement Ti (n)
fin faire
k
X
(Ci (n) + Ti (n)) + Ck+1 (n)
i=1
Ti (n) : coût de la i ième itération
souvent défini par une équation récursive
Evaluation de T (n) (fonctions récursives : exemple)
fonction FUNCTIONRECURSIVE (n)
1 si (n > 1) alors
2 FUNCTIONRECURSIVE(n/2), coût T (n/2)
3 Traitement(n), coût C (n)
4 FUNCTIONRECURSIVE(n/2), coût T (n/2)
Equation récursive
T (n) = 1 + 2 × T (n/2) + C (n)
si C (n) = 1 alors T (n) = K × n
si C (n) = n alors T (n) = K × n × log n
...
Notation de Landau O(f (n))
c × f (n )
T (n) = O(f (n))
n0 n
Caractérise le comportement asymptotique (i.e. qd n → ∞).
T (n) = O(f (n)) si ∃c ∃n0 tels que ∀n > n0 , T (n) ≤ c × f (n)
Notation Θ(f (n))
c1 × f (n)
T (n )
c2 × f (n)
n0 n
T (n) = Θ(f (n)) si ∃c1 , c2 , n0 tels que
∀n > n0 , c1 × f (n) ≤ T (n) ≤ c2 × f (n)
Exemples
T (n) = n3 + 2 n2 + 4 n + 2 = O(n3 )
(si n ≥ 1 alors T (n) ≤ 9 × n3 )
Exemples
T (n) = n3 + 2 n2 + 4 n + 2 = O(n3 )
(si n ≥ 1 alors T (n) ≤ 9 × n3 )
T (n) = n log n + 12 n + 2 = O(n log n)
Exemples
T (n) = n3 + 2 n2 + 4 n + 2 = O(n3 )
(si n ≥ 1 alors T (n) ≤ 9 × n3 )
T (n) = n log n + 12 n + 2 = O(n log n)
2n
T (n) = 2 n10 + n7 + 12 n4 + = O(2n )
100
Les principales classes de complexité
O(1) temps constant
O(log n) logarithmique
O(n) linéaire
O(n × log n) tris par comparaisons optimaux
O(n2 ) quadratique, polynomial
O(n3 ) cubique, polynomial
O(2n ) exponentiel (problèmes très difficiles)
Exemple : permutation dans un tableau
fonction PERMUTATION (S, i, j)
1 tmp := S[i], coût c1
2 S[i] := S[j], coût c2
3 S[j] := tmp, coût c3
4 renvoyer S coût c4
Coût total
T (n) = c1 + c2 + c3 + c4 = O(1)
Exemple : recherche séquentielle
fonction RECHERCHE_SEQUENTIELLE(x, S[1, . . . , n])
1 i := 1, (c1 )
2 tant que ((i < n) et (S[i] 6= x)) faire (c2 )
3 i := i + 1, (c3 )
4 renvoyer (S[i] = x) (c4 )
Pire des cas : n fois la boucle
T (n) = c1 + c2 + ni=1 (c3 + c2 ) + c4 = O(n)
P
Exemple : tri à bulle
fonction TRI_A_BULLES (S[1, . . . , n])
1 pour i := n à 2 faire
2 pour j := 1 à i − 1 faire
3 si (S[j] > S[j + 1]) alors i − 1 fois
4 PERMUTER(S, j, j + 1), Cperm
n−1
X n × (n − 1)
T (n) = (1 + Cperm ) × i = (1 + Cperm ) × = O(n2 )
2
i=1
Equations récursives
Boucles itératives, fonctions récursives, approches de type
diviser pour régner
Cas général
T (n) = a × T (n/b) + f (n)
Différentes techniques :
I méthode par substitution : intuition/vérification,
I méthode par développement itératif,
I en utilisant le théorème général.
Méthode par substitution
Principe : on vérifie une intuition
T (n) = a × T (n/b) + f (n) et T (1) = c
Intuition
T (n) = O(g (n))
A démontrer en fixant les constantes
Méthode par substitution [recherche dichotomique]
fonction RECHERCHE_DICHOTOMIQUE (x, S[1, . . . , n])
1 g := 0, d := n + 1,
2 tant que (g < d − 1) faire
3 si (x > S[(g + d)/2]) alors
4 g := (g + d)/2,
5 sinon
6 d := (g + d)/2,
7 renvoyer d,
Renvoie position, la position de x s’il apparaît dans S[], la position
à laquelle il faudrait l’insérer sinon
Méthode par substitution [recherche dichotomique]
fonction RECHERCHE_DICHOTOMIQUE (x, S[1, . . . , n])
1 g := 0, d := n + 1, g < position ≤ d
2 tant que (g < d − 1) faire termine quand g ≥ d − 1
3 si (x > S[(g + d)/2]) alors g < (g + d)/2 < d puisque g < d − 1
4 g := (g + d)/2, et donc g < position ≤ d
5 sinon
6 d := (g + d)/2, et donc g < position ≤ d
7 renvoyer d, g < position ≤ d et g ≥ d − 1
Renvoie position, la position de x s’il apparaît dans S[], la position
à laquelle il faudrait l’insérer sinon
Méthode par substitution [recherche dichotomique]
fonction RECHERCHE_DICHOTOMIQUE (x, S[1, . . . , n])
1 g := 0, d := n + 1, g < position ≤ d
2 tant que (g < d − 1) faire termine quand g ≥ d − 1
3 si (x > S[(g + d)/2]) alors g < (g + d)/2 < d puisque g < d − 1
4 g := (g + d)/2, et donc g < position ≤ d
5 sinon
6 d := (g + d)/2, et donc g < position ≤ d
7 renvoyer d, g < position ≤ d et g ≥ d − 1
Après la boucle, d − 1 < position ≤ d et donc position = d.
Méthode par substitution [recherche dichotomique]
fonction RECHERCHE_DICHOTOMIQUE (x, S[1, . . . , n])
1 g := 0, d := n + 1,
2 tant que (g < d − 1) faire
3 si (x > S[(g + d)/2]) alors
4 g := (g + d)/2,
5 sinon
6 d := (g + d)/2,
7 renvoyer d,
Nombre d’itérations : T (n) = 1 + T (dn/2e)
A chaque itération le nombre d’éléments considérés (entre les indices g et d) est divisé
par deux.
Méthode par substitution [recherche dichotomique]
Nombre d’itérations
T (n) = 1 + T (n/2) et T (1) = 1
I T (1) = 1 car s’il y a un seul élément on fera une itération
I T (dn/2e) = T (n/2)
(pour simplifier on considère que n est de la forme 2k )
Méthode par substitution [recherche dichotomique]
T (n) = 1 + T (n/2) et T (1) = 1
Intuition T (n) = O(log2 n)
Méthode par substitution [recherche dichotomique]
T (n) = 1 + T (n/2) et T (1) = 1
Intuition T (n) = O(log2 n)
Hypothèse T (n) = k1 × log2 n + k2
Méthode par substitution [recherche dichotomique]
T (n) = 1 + T (n/2) et T (1) = 1
Intuition T (n) = O(log2 n)
Hypothèse T (n) = k1 × log2 n + k2
donc T (n/2) = k1 × log2 n − k1 + k2 car log2 (n/2) = log2 n − 1
donc T (n) = 1 + T (n/2) = 1 + k1 × log2 n − k1 + k2
donc 1 − k1 + k2 = k2 et donc k1 = 1,
enfin, puisque T (1) = 1, on a k2 = 1
Méthode par substitution [recherche dichotomique]
T (n) = 1 + T (n/2) et T (1) = 1
Intuition T (n) = O(log2 n)
Hypothèse T (n) = k1 × log2 n + k2
donc T (n/2) = k1 × log2 n − k1 + k2 car log2 (n/2) = log2 n − 1
donc T (n) = 1 + T (n/2) = 1 + k1 × log2 n − k1 + k2
donc 1 − k1 + k2 = k2 et donc k1 = 1,
enfin, puisque T (1) = 1, on a k2 = 1
Conclusion T (n) = log2 n + 1 = O(log2 n)
(en fait Θ(log2 n))
Méthode par développement itératif : sommations
Pn−1 n×(n−1)
I
i=1 i= 2
= Θ(n2 ) = O(n2 )
x n+1 −1
Pn
I
i=0 xi = x−1
en particulier quand x vaut 2
Pn
i=0 2i = 2n+1 − 1
Tri par fusion
I diviser pour régner 69412537
décompositions
I décomposition
6423 9157
I fusion
62 43 95 17
6 2 4 3 9 5 1 7
26 34 59 17
fusions 1579
2346
12345679
Tri par fusion
fonction TRI_PAR_FUSION (S)
1 si (longueur (S) > 1) alors
2 décomposer S −→ (S1 , S2 ),
3 S1 := TRI_PAR_FUSION(S1 ),
4 S2 := TRI_PAR_FUSION(S2 ),
5 S := FUSIONNER(S1 , S2 ),
6 renvoyer S
Tri par fusion
fonction TRI_PAR_FUSION (S)
1 si (longueur (S) > 1) alors
2 décomposer S −→ (S1 , S2 ), (n)
3 S1 := TRI_PAR_FUSION(S1 ), (T (dn/2e))
4 S2 := TRI_PAR_FUSION(S2 ), (T (bn/2c))
5 S := FUSIONNER(S1 , S2 ), (n)
6 renvoyer S
T (n) = 1 + n + T (dn/2e) + T (bn/2c) + n et T (1) = 1
Tri par fusion
fonction TRI_PAR_FUSION (S)
1 si (longueur (S) > 1) alors
2 décomposer S −→ (S1 , S2 ), (n)
3 S1 := TRI_PAR_FUSION(S1 ), (T (dn/2e))
4 S2 := TRI_PAR_FUSION(S2 ), (T (bn/2c))
5 S := FUSIONNER(S1 , S2 ), (n)
6 renvoyer S
T (n) = 2n + 1 + 2 × T (n/2)
on suppose que n est de la forme 2k
Méthode par développement itératif [tri par fusion]
T (n) = 2n + 1 + 2 × T (n/2)
Méthode par développement itératif [tri par fusion]
T (n) = 2n + 1 + 2 × T (n/2)
donc T (n/2) = n + 1 + 2 × T (n/4)
Méthode par développement itératif [tri par fusion]
T (n) = 2n + 1 + 2 × T (n/2)
donc T (n/2) = n + 1 + 2 × T (n/4)
= (2n + 1) + (2n + 2) + 4 × T (n/4)
Méthode par développement itératif [tri par fusion]
T (n) = 2n + 1 + 2 × T (n/2)
donc T (n/2) = n + 1 + 2 × T (n/4)
= (2n + 1) + (2n + 2) + 4 × T (n/4)
or T (n/4) = n/2 + 1 + 2 × T (n/8)
Méthode par développement itératif [tri par fusion]
T (n) = 2n + 1 + 2 × T (n/2)
donc T (n/2) = n + 1 + 2 × T (n/4)
= (2n + 1) + (2n + 2) + 4 × T (n/4)
or T (n/4) = n/2 + 1 + 2 × T (n/8)
= (2n + 1) + (2n + 2) + (2n + 4) + 8 × T (n/8)
...
Méthode par développement itératif [tri par fusion]
T (n) = 2n + 1 + 2 × T (n/2)
donc T (n/2) = n + 1 + 2 × T (n/4)
= (2n + 1) + (2n + 2) + 4 × T (n/4)
or T (n/4) = n/2 + 1 + 2 × T (n/8)
= (2n + 1) + (2n + 2) + (2n + 4) + 8 × T (n/8)
...
Plog n−1
T (n) = i=0 (2n + 2i ) + 2log n × T (1)
Méthode par développement itératif [tri par fusion]
T (n) = 2n + 1 + 2 × T (n/2)
donc T (n/2) = n + 1 + 2 × T (n/4)
= (2n + 1) + (2n + 2) + 4 × T (n/4)
or T (n/4) = n/2 + 1 + 2 × T (n/8)
= (2n + 1) + (2n + 2) + (2n + 4) + 8 × T (n/8)
...
Plog n−1
T (n) = i=0 (2n + 2i ) + 2log n × T (1)
P n−1 i
= 2n log n + logi=0 2 +n
Méthode par développement itératif [tri par fusion]
T (n) = 2n + 1 + 2 × T (n/2)
donc T (n/2) = n + 1 + 2 × T (n/4)
= (2n + 1) + (2n + 2) + 4 × T (n/4)
or T (n/4) = n/2 + 1 + 2 × T (n/8)
= (2n + 1) + (2n + 2) + (2n + 4) + 8 × T (n/8)
...
Plog n−1
T (n) = i=0 (2n + 2i ) + 2log n × T (1)
P n−1 i
= 2n log n + logi=0 2 +n
Plog n−1
or i=0 2i = 2log n − 1 = n − 1
= 2n log n + 2n − 1 = Θ(n log n)
Tri par fusion
n 2×n
n/2 n/2 2×n
hauteur dlog ne
n/4 n/4 n/4 n/4 2×n
n/8 n/8 n/8 n/8 n/8 n/8 n/8 n/8
1 1 1 ....
décomposition ou fusion d’une suite de longueur n/k : coût O(n/k)
T (n) = 2 × n × dlog2 ne
Master theorem [Equations récursives]
T (n) = a × T (n/b) + f (n)
avec a ≥ 1, b > 1 et f (n) est positive asymptotiquement.
a−
I si ∃ > 0, f (n) = O(nlogb ) alors T (n) = Θ(nlogb a ),
I si f (n) = Θ(nlogb a ) alors T (n) = Θ(nlogb a
× log n),
logb a+
I si ∃ > 0, f (n) = Ω(n ) et
si ∃c < 1, ∃n0 , ∀n > n0 , a × f (n/b) ≤ c × f (n) alors
T (n) = Θ(f (n))
Master theorem [Equations récursives]
T (n) = a × T (n/b) + f (n)
avec a ≥ 1, b > 1 et f (n) est positive asymptotiquement.
a−
I si ∃ > 0, f (n) = O(nlogb ) alors T (n) = Θ(nlogb a ),
I si f (n) = Θ(nlogb a ) alors T (n) = Θ(nlogb a
× log n),
logb a+
I si ∃ > 0, f (n) = Ω(n ) et
si ∃c < 1, ∃n0 , ∀n > n0 , a × f (n/b) ≤ c × f (n) alors
T (n) = Θ(f (n))
Tri par fusion (T (n) = 2 × T (n/2) + 2n + 1) cas 2 :
a = b = 2 et f (n) = 2n + 1 = Θ(n)
et donc T (n) = Θ(n log n)
Méthode par substitution [tri par fusion]
T (n) = 2n + 1 + 2 × T (n/2) et T (1) = 1
Hypothèse : T (n) = O(n log n) = an log n + bn + c
et donc T (n/2) = an/2 log n + (b − a)n/2 + c
T (n) = 2n + 1 + 2T (n/2) = 2n + 1 + an log n + (b − a)n + 2c
= an log n + (b − a + 2)n + 2c + 1
(1) b = b − a + 2 et donc a = 2
(2) c = 2c + 1 et donc c = −1
(3) T (1) = b + c = 1 et donc b = 2
et finalement T (n) = 2n log n + 2n − 1 = O(n log n)
Temps de calcul [simulation]
Combien de temps pour traiter un problème ?
Taille log2 n n n log2 n n2 2n
10 0.003 ms 0.01 ms 0.03 ms 0.1 ms 1 ms
100 0.006 ms 0.1 ms 0.6 ms 10 ms 1014 siecles
1000 0.01 ms 1 ms 10 ms 1s
104 0.013 ms 10 ms 0.1 s 100 s
105 0.016 ms 100 ms 1.6 s 3 heures
106 0.02 ms 1s 20 s 10 jours
pour une machine qui effectue 106 traitements par seconde
Temps de calcul [simulation]
Quel problème peut-on traiter en une seconde ?
nTs 2n n2 n log2 n n log2 n
106 20 1000 63000 106
10300000
107 23 3162 600000 107 103000000
109 30 31000 4.107 109
1012 40 106 3.1010
nTs = nombre d’instructions effectuées chaque seconde