Vous êtes sur la page 1sur 51

CHAPITRE 1 :

LA COMPLEXITÉ
ALGORITHMIQUE
Comment peut-on comparer les algorithmes ?
Imaginez qu’on a deux algorithmes A1 et A2, les deux peuvent
résoudre le même problème P.
 Les deux nous donnent des résultats corrects.

La question qui vient en tête,


• lequel d’entre eux nous allons exploiter ?
• Le meilleur ?
• Comment peut-on mesurer l’efficacité d’un algorithme ?
Example de comparaison des voitures

Si on a deux voitures V1 et V2 , on peut les comparer en se


basant sur leurs caractéristiques, par exemple selon
• la puissance du moteur,
• le poids,
• le carburant utilisé
• etc.
Paramètre d'évaluation
En Algorithmique, on peut comparer les algorithmes selon
deux paramètres :

• le premier c’est l’espace mémoire, ceci veut dire, combien


d’espace mémoire va être utilisé par l’algorithme pour
résoudre le problème.
• Le deuxième paramètre est le temps d’exécution. En fait, un

algorithme peut donner le résultat avant l’autre, lorsqu’on les

exécute sur la même machine. Il n’est pas possible de comparer

des algorithmes lorsqu’on a pas la même combinaison matérielles

(processeur, bus de la carte mère, vitesse et capacité de la RAM),

car cette dernière influence sur le temps d’exécution.


Il est important de ne pas confondre performances et
complexité. Lorsqu’on mesure les performances d’un
algorithme on cherche qu’elle est l’implémentation la plus
efficace, car par exemple, une implémentation en java sera
moins rapide qu’une implémentation en langage natif tel que
le C.
• De ce fait en algorithmique au lieu de calculer le temps
d’exécution, on se base sur le calcul du nombre
d’instructions qu’exécute l’algorithme pour arriver au
résultat.
Complexité en temps
Soit l’algorithme suivant qui permet de chercher l’existence d’un
élément x dans un tableau tab
Si on analyse ensemble cet algorithme, on peut compter les

instructions de base, il est à noter que ces instructions seront

après traduites par le compilateur en instructions assembleur,

bien sûr selon le jeux d’instruction du processeur (exemple

MOV, ADD, SUB, etc.).


Car notre algorithme une fois programmé, il peut être

exécuté sur plusieurs type de processeur : Intel, AMD ou

même sur un processeur d’un téléphone Android ou iPhone


• Au début de la fonction
• on a deux affectations b=0 et i=0 (ligne 2 et 3), donc on a
déjà 2 instructions,
• après on a deux comparaisons i<10 et b==0 plus une
opération logique le « et » logique représenté par l’opérateur
&&, donc on a 3 instructions.
• Dans le corps de la boucle on a l’accès à un élément dans
le tableau, une comparaison et une affectation plus
l’incrémentation du i et son affectation, ceci fait 5
instructions,
• à la fin nous avons le retour qui peut être vu comme étant
une opération d’affectation, donc on a une dernière
instruction
Si on suppose que notre algorithme reçoit un tableau de n
cellules donc la boucle sera exécutée au maximum n fois.
de ce fait, on peut résumer le nombre d’instruction
qu’exécute notre algorithme par une fonction mathématique
qui dépende de n la taille du tableau, et on aura

f(n) = 2+ 8 n +1 = 8 n+ 3.
D’après notre analyse on a pu approximativement compter le
nombre d’instruction que fait notre algorithme, cette
approximation reste encore dépendante du contenu de
tableau T, en fait, la façon avec laquelle ce dernier est
remplis peut affecter le nombre d’instructions exécutées par
notre algorithme.
Imaginez les cas suivants :
1. L’élément X qu’on cherche figure dans la première cellule
du tableau Tab,
2. L’élément X se trouve au milieu du tableau.
3. L’élément X figure dans la dernière cellule ou bien n’existe
pas dans le tableau.
En analysant ensemble ces trois cas,

1. le cas A signifié que notre algorithme exécutera le corps


de la boucle uniquement une fois seulement, est ceci
quel que soit la taille du table n. Quand on calcule la
complexité en temps, ce cas est appelé le meilleur cas
(the best case, en anglais).
1. Le deuxième cas B est nommé le cas moyen,

2. le dernier cas C est appelé le pire des cas (worst


case en Anglais).
Il est important de garder en tête que la complexité en

temps d’un algorithme dépend de la taille et la

distribution des données reçus en entrée.


La notation asymptotique

Lorsqu’on calcule la complexité d’un algorithme, le


plus intéressant pour nous c’est le pire des cas, c’est à
dire le scénario durant lequel notre algorithme recevra
les données qui montreront ses pires performances. On
compare après les algorithmes selon ce cas.
• Une fois qu’on a la fonction qui permet d’approximer le
nombre d’instruction (le comportement) de notre algorithme,
le but est de voir l’évolution de ce nombre lorsqu’on tend n
vers l’infinie, mathématiquement parlant, on cherche a trouvé
la limite de la fonction lorsque n tend vers l’infinie.
Dans notre exemple f(n) = 8 n+3, imaginez que n = 1000,
f(n) = 8* 1000 +3=8003, ici on peut ignorer la constante 3,
de même on peut aussi ignorer les facteurs (8) on dit que le
comportement asymptotique de F(n)= 8 n +3 et f(n)= n, car
lorsque n tend ver l’infinie, F(n) tend aussi vers l’infinie
donc on écrit f(n)=n, c’est ce terme qui « domine » la
fonction.
• De la même façon on peut examiner les fonctions suivantes :

• f(n)= 5 n + 12 donnera f(n)= n

• f(n) = 200 donnera f(n) =1 , ici on remplace 200 par 1 pour

dire que la fonction n’est pas nulle mais elle est constante.
• f(n) = n2+3n + 100 , ce qui donnera f(n) = n2, ici n2 croit
plus vite que 3n , donc on ignore 3n et aussi la constante
100.

• f(n) = n + √𝑛, ici f(n) = n, vu que elle croît plus vite que
la racine carrée de n, donc c’est ce terme qui domine.
L’algorithme décrit dans la figure 1, suit une stratégie nommée recherche

linéaire (linear search en Anglais). Comme on peut remarquer sa fonction

qui approxime son nombre d’instructions est aussi une fonction linéaire.
La notation thêta

Une fois qu’on a pu avoir la fonction qui calcule le


nombre d’instruction on peut après suivre la notation
est dire que notre algorithme est Θ(f(n)). Dans notre
cas , l’algorithme décrit dans la figure 1 on dit qu’il est
Θ(n), d’autres exemples :
n2 + 3n ∈ Θ( n2 )

5n + 12 ∈ Θ( 5n )

6n + 10n ∈ Θ(10n)
Complexité :

Une fois qu’on a pu déduire la fonction asymptotique la


fonction qui approxime le nombre d’instructions que fait
un algorithme. On peut dorénavant déduire sa
complexité.
Simplement,

• un programme qui ne possède aucune boucle aura la


fonction f(n)=1,

• un programme avec une boucle aura f(n)=n,

• un programme avec deux boucles imbriquées, aura


f(n)=n2,

• de même trois boucles imbriquées est à f(n)=n3.


Questions : Que pensez-vous d’un algorithme qui
possède

• Deux boucles qui ne sont pas imbriquées,

• Ou bien un autre avec 3 boucles, deux imbriquées et

l’autre indépendante?
• De même si on a un algorithme avec trois boucles
consécutives, laquelle sera elle considérée pour estimer
son comportement asymptotique ?
• Le cas d’un algorithme qui dans une boucle appel une
fonction et cette dernière est implémentée à l’aide d’une
boucle, que faire dans ce cas ?
La notation Big O (appelée aussi la notation de
Landau) :
La notation thêta permet d’exprimer la complexité actuelle
d’un algorithme, il existe une autre notation qui permet de
décrire la limite maximale de la complexité d’un
algorithme, ceci veut dire qu’en modifiant l’algorithme pour
le rendre plus pire on ne peut obtenir au maximum O.
exemple l’algorithme de la figure 1 est Θ(n) et aussi O(n).
• Exemple de complexités couramment trouvées, dans la figure 3
on peut voir comment ces complexités évoluent :
La complexité constante O(1) :

Un algorithme est dit a complexité constante, lorsque sa


complexité ne dépend pas du paramètre n.
ceci veut dire qu’en modifiant la valeur de n le nombre
d’instructions de l’algorithme restera le même.
f(n+1)=f(n)=1 (une constante) .
Exemple : l’algorithme qui permet de permuter les valeurs de
deux cases d’un tableau, quel que soit la taille du tableau
seules trois instructions seront exécuté.
Voir le listing dans la figure 2.
Complexité linéaire O(n) :
Le nombre d’instructions f(n+1)=f(n)+1, ceci signifie une
complexité linéaire, en incrémentant n le nombre
d’instruction s’incrémente linéairement. L’algorithme de la
figure 1 a une complexité linéaire O(n). En augmentant la
taille du tableau, le nombre d’itérations augmente aussi.
Complexité logarithmique O(log n)
Une autre sous-catégorie de ce niveau de complexité est la
complexité logarithmique. Par exemple la recherche dans un
tableau trié, il suffit de pouvoir comparer l’élément cherché
avec l’élément de la position n/2 (le milieu du tableau), ce
qui permet d’ignorer la partie qui soit supérieure à X, et
diviser à chaque fois la taille du tableau en deux. Cette
recherche est nommée recherche dichotomique.
Complexité linéaire logarithmique O( n log(n))

Lorsqu’on a une fonction évolue selon n * log(n),


l’algorithme est dit a complexité linéaire
logarithmique.
Complexité polynomiale O(nc) ,

Si le nombre d’instructions évolue selon une fonction


polynomiale on dit que l’algorithme est de complexité
polynomiale. Un exemple est un algorithme avec deux
boucles imbriquées dans ce cas c=2, on parle d’une
complexité quadratique O(n2).
Complexité exponentielle O(cn),
• Lorsque la fonction de nombre d’instructions d’un
algorithme évolue selon une loi exponentielle, on
parle d’une complexité O(cn).
• Exemple d’un algorithme de parcours d’un arbre
binaire, dans ce cas, chaque nœud de l’arbre donnera
au maximum un accès à deux autres nœuds, dans ce
cas c=2, est la complexité est d’ordre O(2n)
Complexité factorielle O (n!)

Mathématiquement parlant la fonction qui permet de


calculer n! est une fonction qui croit très rapidement.
Série de TD de premier chapitre : la complexité
algorithmique
Exercice 1
Examiner le comportement asymptotique des fonctions
suivantes :
1. F( N ) = N6 + 3N
2. F( N ) = 2N + 12
3.F( N ) = 3N + 2N
4. F( N ) = NN + N
5. F ( N) = N+ √𝑛
EXERCICE 2
Examiner le comportement asymptotique des algorithms suivants :

for i = 1 to n do

for j = 1 to 2n + 1 do

print (« Hello World)

end for

end for
for i = 1 to 10 do

for j = 1 to n do

print (« Hello World »)

end for

end for
for i = 1 to n do

for j = i to n do

print (« Hello World »)

end for

end for
for i = 1 to n do

for j = 1 to 2 ∗ i + 1 do

print (« Hello World »)

end for

end for
for i = 1 to n ∗ n do

for j = 1 to i do

print (« Hello World »)

end for

end for
• for (i = 0 to m) do
•      t ← 1
•      while (t < m) do
•         print (« Hello world »)
•           t ← t ∗ 2
•      end while
• end for
Exercice 2
Soit deux tableaux T1 et T2 , de différentes tailles, contenants
des entiers:
• Ecrire un algorithme qui calcule la somme des éléments
d’un tableau. Compter son nombre d’instructions et
déterminer sa complexité.
• Ecrire un algorithme qui mit dans un troisième tableau T3

les éléments pairs de T1 et T2. Déterminer le nombre

d’instructions et sa complexité.

• Ecrire un algorithme qui calcule l’intersection entre T1 et

T2, compter le nombre d’instructions et déterminer la

complexité.
FIN

Vous aimerez peut-être aussi