Vous êtes sur la page 1sur 38

Analyse des algorithmes

Façons d’analyse d’un algorithme

1) On compte, de façon exacte, le nombre d’opérations élémentaires


exécutées par l’algorithme et on trouve l’ordre de complexité associé en
simplifiant à l’aide des propriétés des notations asymptotique.

2) On approxime, à l’aide d’un ordre de complexité, chacune des parties


de l’algorithme. On combine et simplifie ensuite ces différents ordres
de grandeur à l’aide des propriétés (par ex., O(f) + O(g) = O(max{f,g})).

3) On identifie une opération barométrique appropriée et on calcule le


nombre (exact) de fois où cette opération est exécutée, d’où l’on déduit
ensuite l’ordre de complexité.
Opération élémentaire VS barométrique

• Opération élémentaire = une opération dont le temps d’exécution


peut être borné par un constante qui dépend seulement de la mise
en œuvre.

• Opération barométrique = opération (élémentaire) qui est exécutée


au moins aussi souvent que n’importe quelle autre instruction de
l’algorithme.
 Lorsqu’on identifie cette opération barométrique, il est
important de justifier brièvement pourquoi cette opération est
effectivement exécutée au moins aussi souvent que les autres
opérations.
Exemple avec les différentes façons d’analyse

PROCEDURE sum( n: Nat, S: Entier[] ): Entier


DEBUT result <- 0; i <- 1;
Tant que (i < n) FAIRE
result <- result + S[i];
i <- i+1;
FIN
RETOURNER( result )
FIN

Opérations élémentaires : comparaisons, affectations ainsi que


l’instruction de retour du résultat.
1. Nombre exact d’opérations élémentaires

T(n) =
1 +1 (initialisation de result et i)
+2*n (n itérations, avec deux affectations (i et result)
+1*n (n itérations, avec une comparaison (test de la boucle) par itération)
+1 (instruction RETOURNER)
= 3n+3 O(n)
2. Approximation de chacune des étapes
PROCEDURE sum( n: Nat, S: Entier[] ): Entier
DEBUT result <- 0; i <- 1;
Tant que (i < n) FAIRE
result <- result + S[i];
i <- i+1;
FIN
RETOURNER( result )
FIN

• En termes d’opérations élémentaires :


T(n) = Θ(1) + Θ(1) + Θ(n) ∗ Θ(1) + Θ(1) = Θ(1) + Θ(n) + Θ(1)
= Θ(n)
3. Nombre d’exécutions d’une opération
barométrique appropriée
• Choisissons l’affectation à result comme opération barométrique :

• Comme on s’intéresse au comportement asymptotique (donc pour


de grands n), on peut voir que cette opération sera exécutée plus
souvent que les instructions à l’extérieur de la boucle ou qu’elle sera
exécutée aussi souvent que les autres.

• Le nombre d’exécutions de cette opération sera alors le suivant :


T(n) = n ∈ Θ(n)
Remarques sur le calcul de complexité
• Ce qui est intéressant dans l'analyse du coût est le comportement
en fonction de n (notion de passage a l‘échelle)
 Comportement asymptotique.
On dit asymptotique parce que ça décrit le comportement des
fonctions au-delà d'un certain seuil.

• Pour cela on compare la fonction de coût à des fonctions de


référence appelées fonctions d‘échelle.

• Typiquement les fonctions puissance n^x, exponentielles 2^n ou


logarithmiques log(n) sont des fonctions d‘échelle.
Comportement asymptotique
Si un algorithme A est de complexité O(f(n))
 f(n) est le comportement asymptotique du temps d’exécution de A.
La complexité asymptotique d’un algorithme est indépendante de son
implémentation (choix de la machine, du langage, ...).
 Deux implémentations différentes d’un même algorithme ne peuvent
différer en efficacité que par une constante multiplicative près.
c1*f(n) pour l’un et c2*f(n) pour l’autre.
• Modification du matériel informatique / logiciel :
Affecte T(n) par un facteur constant.
Ne modifie pas le taux de croissance de T(n).
Calcul de complexité
par approximation des
étapes
Règles de calcul de la complexité
d’une manière générale
• O(1) pour toute opération élémentaire

• La complexité d’une séquence de deux modules M1 de complexité


O(f(n)) et M2 de complexité O(g(n)) est égale à la plus grande des
complexité des deux modules : O( max(f(n),g(n)) )

• La complexité d’une conditionnelle (Si cond Alors M1 Sinon M2 Fsi)


est le max entre les complexités de cond, M1 et M2 (cette
simplification s’effectue dans une analyse en pire cas)

• La complexité d’une boucle est égale à la somme sur toutes les


itérations de la complexité du corps de la boucle
Estimation du coût d’un algorithme itératif

• Règle 1 : la complexité d’un ensemble d’instructions est la


somme des complexités de chacune d’elles.

• Somme des coûts de traitements successifs:


Traitement_1 = coût_1
Traitement_2 = coût_2

Traitement_i = coût_i

Coût_total = coût_1+coût_2+…coût_i+…
Estimation du coût d’un algorithme itératif

• Règle 2 : Les opérations élémentaires telle que l’affectation,


test, accès à un tableau, opérations logiques et
arithmétiques, lecture ou écriture d’une variable simple ...
etc, sont en O(1) (ou en O(1))

• Affectation, lecture, écriture se mesurent par : Ο(1)


Estimation du coût d’un algorithme itératif

• Règle 3: Instruction if : maximum entre le then et le else


switch : maximum parmi les différents cas

• Instruction conditionnelle:
f(…)
{ si (…) alors g(…) ;
Sinon h(…) ;
finsi ;
}
Coût de f(…)=MAX (coût de g(…), coût de h(…))
Estimation du coût d’un algorithme itératif
Règle 4: Instructions de répétition

1. la complexité de la boucle for est calculée par la complexité du


corps de cette boucle multipliée par le nombre de fois qu’elle est
répétée.

2. En règle générale, pour déterminer la complexité d’une boucle


while, il faudra avant tout déterminer le nombre de fois que cette
boucle est répétée, ensuite le multiplier par la complexité du corps
de cette boucle.
Estimation du coût d’un algorithme itératif
• Instruction itérative « pour » (Boucle pour):

f(…, n :entier)
{ pour i :=1 à n faire
g(…) ;
}
Coût de f(…,n)= n x coût de g(…)

• Si g(…) dépend de la valeur de i alors:


Coût de (f … , n) = 𝐧𝐢=𝟏 ܿ‫݋‬û‫ … ࢍ(ݐ‬, ࢏ )
Estimation du coût d’un algorithme itératif

• Instruction itérative « tantque » :


f(…, n :entier)
{ tantque <condition> faire
traitement g(…) ;
}

𝑘
• 𝑪𝒐û𝒕 𝒅𝒆 (𝒇 … , 𝒏) = 𝑖=1 ܿ‫݋‬û‫(ࢍ(ݐ‬n) )
Estimation du coût d’un algorithme itératif

Règle 5 : Procédure et fonction

Leur complexité est déterminée par celui de leur corps.

L’appel à une fonction est supposé prendre un temps constant en


O(1) par exemple.

Notons qu’on fait la distinction entre les fonctions récursive et


celles qui ne le sont pas.
Calcul de la complexité des algorithmes
récursifs
• Dans le cas de la récursivité, le temps de calcul est exprimé
comme une relation de récurrence.

• Résoudre une équation de récurrence.

• Plusieurs approches :
a) éliminer la récurrence par substitution de proche en proche
b) deviner une solution est la démontrer par récurrence
c) utiliser la solution de certaines équations connues
Etc…
Remarque sur les fonctions de coût à
plusieurs arguments
• Lorsque les données d’un algorithme sont caractérisées par plusieurs
arguments de tailles possiblement différentes, alors la fonction de
complexité résultante peut très bien dépendre de plusieurs arguments.
• Par exemple, soit la procédure suivante :

PROCEDURE proc( s1: sequence{Nat}, s2: sequence{Nat} ) res: Nat


PRECONDITION n = length(s1) m = length(s2)
DEBUT Initialiser res
POUR i <- 1 A n FAIRE
POUR j <- 1 A m FAIRE
Traiter s1[i] et s2[j] et mettre à jour res (en temps Θ(1))
FIN FIN FIN
Remarque sur les fonctions de coût à
plusieurs arguments

• Dans cet algorithme, on distingue entre la taille de s1 et la taille


de s2 par exemple, parce que ces séquences jouent des rôles
différents, malgré leur type semblable.

• La fonction donnant le temps d’exécution de l’algorithme devrait


dépendre de ces deux arguments

 d’où l’on conclurait que le temps d’exécution de cet algorithme


est le suivant :
T(n, m) ∈ Θ(n × m)
Notations
asymptotiques
Complexité asymptotique c’est quoi ?

• Quand nous calculons la complexité d’un algorithme, nous ne


nous calculerons pas sa complexité exacte mais son ordre de
grandeur.

• La complexité asymptotique décrit le comportement de


l’algorithme quand la taille n des données du problème traité
devient de plus en plus grande.

• Pour ce faire, nous avons besoin de notations asymptotiques.


Complexité asymptotique

• Des notations sont utilisées pour représenter la complexité.


Principalement :
Big O, Omega, Theta.

• Les notations asymptotiques sont utilisées parce que les différentes


implémentations d’un même algorithme peuvent être différentes en
efficacité.

• L’efficacité de deux algorithmes donnés est reliée par un facteur d’une


constante multiplicative appelée constante cachée (hidden constant).
Analyse de la complexité
• La comparaison asymptotique est exprimée par la notation de Landau :
f= O(g) c > 0 :  n0 > 0 :  n ≥ n0 : f(n) ≤ c g(n)
f= Oméga(g) c > 0 :  n0 > 0 :  n ≥ n0 : c*g(n) ≤ f(n)
f= Oméga(g) c1,c2 > 0 :  n0 > 0 :  n ≥ n0 : c1*g(n) ≤ f(n) ≤ c2*g(n)
Notation grand-O
Définition:
Soit T(n) une fonction non négative.
T(n) est en O(f(n)) s’il existe deux constante positives c et n0 telles que:
T(n) <= cf(n) pour tout n >= n0.

Utilité: Le temps d’exécution est borné


Signification: Pour toutes les grandes entrées (i.e., n >= n0), on est assuré
que l’algorithme ne prend pas plus de cf(n) étapes.
 Borne supérieure.
Notation grand-O
• Pour une fonction g(n), on définit O(g(n)), (big-O of n), comme:
• L’ensemble de toutes les fonctions dont le taux de croissance est le
même ou inférieur que celui de g(n).
• g fait partie d’une échelle de fonctions simples (n, log(n), n^2, …)
destinée à informer sur le comportement asymptotique d’une fonction.

• F=O(g) On dit que f est en O(g) (f est dominée par g)


Si T(n) ≤ c n
pour une constante c et toutes les valeurs de n>n0, on dit
« T(n) est dans O(n) » ou bien
T(n)  O(n) ou, par abus d’écriture,
T(n) = O(n)
Notation grand-O : propriétés

1) O(f(n)) = O(g(n)) ssi f(n) ϵ O(g(n)) et g(n) ϵ O(f(n))


2) O(f(n)) ⊂ O(g(n)) ssi f(n) ϵ O(g(n)) et g(n) ∉ O(f(n))
3) O(f(n)+g(n)) = O(max(f(n),g(n)))
If T1(n) = O(f(n)) and T2(n) = O(g(n))
Then T1(n) + T2(n) = max(O(f(n)), O(g(n)))
And T1(n) * T2(n) = O(f(n) * g(n))

4) Si lim (f(n)/g(n)) = k (k > 0) Alors O(f(n)) = O(g(n))


5) Si lim (f(n)/g(n)) = 0 Alors O(f(n)) ⊂ O(g(n))
Transitivité
Si f(n)  O(g(n))
et g(n)  O(h(n)),
alors f(n)  O(h(n)). La notation Grand-O est transitive

Résumé des règles :

Si f(n) = O(g(n)) et g(n) = O(h(n)), alors f(n) = O(h(n)).

Si f(n) = O(kg(n)) où k > 0 est une constante, alors f(n) = O(g(n)).

Si f1(n) = O(g1(n)) et f2(n) = O(g2(n)), alors (f1 + f2)(n) = O(max(g1(n), g2(n)))

Si f1(n) = O(g1(n)) et f2(n) = O(g2(n)) alors f1(n)f2(n) = O(g1(n) g2(n))


Grand-Omega
• Pour une fonction g(n), on définit Ω(g(n)), comme:
• L’ensemble de toutes les fonctions dont le taux de croissance est le
même ou supérieur à celui de g(n).

Définition: Soit T(N), une fonction non négative.


On a T(n) = Ω(g(n)) s’il existe deux constantes positives c et n0 telles que
T(n) >= cg(n) pour n > n0.

Signification: Pour de grandes entrées, l’exécution de l’algorithme


nécessite au moins cg(n) étapes.  Borne inférieure.
• On notera souvent f = Ω(g). On dit que f domine g  On a alors g = O(f ).
Grand-Omega: exemple
• 5n2 = (n)

 c, n0 tel que: 0  cn  5n2


 cn  5n2  c = 1 and n0 = 1

• Devoir : n = (2n), n3 = (n2), n = (logn)


La notation Thêta

Lorsque le grand-O et le grand-omega d’une fonction coïncident, on utilise


alors la notation grand-theta.
• On dit que f et g sont de même ordre de grandeur asymptotique.

• On a f = (g) Ssi f = O(g) et f = Ω(g).


Le temps d’exécution d’un algorithme est dans (g(n)) s’il est à la fois en
O(g(n)) et Ω(g(n)).

• Pour une fonction g(n), on définit (g(n)), comme:


• L’ensemble de toutes les fonctions dont le taux de croissance est le
même que celui de g(n)  égalité asymptotique
Exemple Théta
• f(n) = (g(n)) Donc c1g(n) <= f(n) <= c2g(n)
avec c1 et c2 constantes > 0 pour tout n>=n0, n0>=1

• f(n) = 3n +2 et g(n) = n
3n +2 <= c2n  c2=4 n0>=1
3n +2 >= c1n  c1=1 n0>=1

Remarque : theta est généralement asymptotiquement égal à la fonction


donc si le terme dominant est en n la fonction va être en théta de n,
pareil pour n^2 par exemple.
Règle de la limite
Devoir :
Θ(n3): n3
5n3+ 4n
105n3+ 4n2 + 6n

Θ(n2): n2
5n2+ 4n + 6
n2 + 5

Θ(log n): log n


log n2
Efficacité des algorithmes
• Définition: Un algorithme est dit efficace si sa complexité (temporelle)
asymptotique est dans O(P(n)) où P(n) est un polynôme et n la taille
des données du problème considéré.

• Définition: On dit qu’un algorithme A est meilleur qu’un algorithme B


si et seulement si:

t A (n)  O(t B (n)); et


tB (n)  O(t A (n))

Où t A (n) et t B (n) sont les complexités des algorithmes A et B,


respectivement.
Références
• T.H. Cormen, C.E. Leiserson, and R.L. Rivest. Introduction to Algorithms.
McGraw-Hill, 1990.
• S. Baase and A. Van Gelder. Computer Algorithms: Introduction to Design
and Analysis. Addison-Wesley, third edition, 2000.
• J.J. Levy: Notes de cours d’algorithmique, École Polytechnique, France
• D. Rebaine : Cours ACA, Université du Québec à Chicoutimi.
• D. Rebaïne (2000): une introduction à l’analyse des algorithmes, ENAG
Édition.
• Cours Analyse et conception d'algorithmes. École polytechnique de Montréal
Références

• L. Sais : Cours algorithmique avancée, Université d’Artois, France.


• F. vivien : Algorithmique avancée, u-strasbourg, 2002.
• Cours Algorithmes : efficacité, analyse et ordre de complexité. Université du
québec à Montréal.
http://www.lacim.uqam.ca/~chauve/Enseignement/Enseignement/INF7440/H
04/index.html

Vous aimerez peut-être aussi