CPGE-AGADIR MP-PSI-TSI
COMPLEXITE ALGORITHMIQUE
1. INTRODUCTION
Exercice
Ecrire en python une fonction qui prend en argument une chaîne de caractères et détermine si le caractère 'a' est
présent dans la chaîne (on retourne soit True soit False).
Analysons plusieurs solutions :
Première solution Deuxième solution
def contienta1(chaine): def contienta2(chaine):
k=0 for i in range(len(chaine)):
N=len(chaine) if chaine[i]=='a':
trouve=False return True
while(trouve == False and k < N): return False
if chaine[k]=='a':
trouve = True
k=k+1
return trouve
Troisième solution Quatrième solution
def contienta3(chaine): def contienta4 (chaine):
n=chaine.count('a') return('a' in chaine)
return bool(n)
Quelques questions:
1. que remarquez-vous concernant cet exercice?
2. Le Coûte le plus court ! Est-il meilleur?
3. Comment peut-on désigner le meilleur Coûte parmi ces quatre solutions?
2. DÉFINITION DE LA COMPLEXITÉ D'UN ALGORITHME
La complexité d'un problème mathématique P est une mesure de la quantité de ressources nécessaires à la
résolution du problème P.
Cette mesure est basée sur une estimation du nombre d'opérations de base effectuées par l'algorithme en
fonction de la taille des données en entrée de l'algorithme.
Généralement, pour le même problème P, on peut trouver plusieurs algorithmes (Alg1; Alg2;...;Algn).
L'objectif de la complexité est d'évaluer le coût d'exécution de chaque algorithme afin de choisir le meilleur.
Problème:
Comment évaluer le coût d'exécution d'un algorithme donné?
Complexité 1 /12 M.GUEROIHI
3. TYPES DE COMPLEXITÉ
En fonction des ressources utilisées pour évaluer la complexité d'un algorithme, on sera amené à distinguer deux
types de complexité: complexité temporelle et complexité spatiale.
Complexité temporelle (en temps)
La complexité temporelle d'un algorithme est le nombre d'opérations élémentaires (affectations, comparaisons,
opérations arithmétiques) effectuées par un algorithme.
Complexité spatiale (en espace)
La complexité en mémoire donne le nombre d'emplacements mémoires occupés lors de l'exécution d'un
programme.
Remarque : Dans ce cours, nous nous intéressons uniquement à la complexité temporelle.
4. LA NOTATION O :(Complexité asymptotique)
Définition
Soit T(n) une fonction qui désigne le temps de calcul d'un algorithme A.
On dit que T(n) est en grand O de f(n): T(n)= O(f (n)) si et seulement si:
n0∈ N *, c ∈ℝ, n >= n0 :T(n) <= c *f (n).
La notation O(f (n)) est aussi appelée Notation de Landau : (Complexité asymptotique) (i.e.quand n →∞ )
Exemples
(on écrit aussi 3n +6∈O(n))
Exemple1:
Prouvons que la fonction T(n)=3n +6 est en O(n)
But: trouver une constante c ∈ℝet un seuil n0∈ N *, à partir du quel T(n) <c.n
On remarque3n+6<9n si n>2
3*2+6 <9*2
3*3+6 <9*3
3*4+6 <9*4 On déduit donc que c=9 fonctionne à partir d’un seuil n0=2
3*5+6 <9*5
.
.
.
Remarque:
On ne demande pas d'optimisation (le plus petit c ou n0qui fonctionne), juste de donner des valeurs qui fonctionnent;
c =10 et n0 =5 est donc aussi acceptable;
3*5+6 <10*2
3*6+6 <10*3
3*7+6 <10*4
Exemple2:
Complexité 2 /12 M.GUEROIHI
2
Prouvons que la fonction : T(n)= n +3n est en O(n2)
But: trouver une constante c ∈ℝet un seuil n0∈ N *, à partir duquel T(n) <c.n
Cherchons d'abord la constante c ; c =1 ne peut pas marcher, essayons donc c =2;
On doit alors trouver un seuil n0∈ N *, à partir du quel :n2 +3n <=2n2
On remarque n2 +3n <=2n2 si n>3
On déduit donc que c=2 fonctionne à partir d’un seuil n0=3
5. CLASSES DE COMPLEXITE LES PLUS USUELLES
On voit souvent apparaître les complexités suivantes:
Complexité Nom courant Description
O(1) Constante Le temps d'exécution ne dépend pas des données traitées, ce qui est assez rare!
O(log(n)) Logarithmique augmentation très faible du temps d'exécution quand le paramètre croit.
augmentation linéaire du temps d'exécution quand le paramètre croit(si le paramètre
O(n) Linéaire
double, le temps double).
O(nlog(n)) Quasi-linéaire augmentation un peu supérieure à O(n)
quand le paramètre double, le temps d'exécution est multiplié par4. Exemple:
O(n2) Quadratique
algorithmes avec deux boucles imbriquées.
ici, nk est le terme de plus haut degré d'un polynôme en n ; il n'est pas rare de voir des
O(nk ) Polynômiale
complexités en O(n3) ou O(n4).
O(kn) Exponentielle quand le paramètre double, letemps d'exécution est élevé à la puissance k avec k > 1.
O(n!) Factorielle asymptotiquement équivalente à nn
6. COMMENT MESURER LA COMPLEXITE D'UN ALGORITHME
a. Le coût des instructions élémentaires
Opération élémentaire.
On appelle opération de base, ou opération élémentaire, toute:
Affectation;
Test de comparaison: ==;<;<=;>=;!=
Opération de lecture(input)et d’écriture (print);
Opération arithmétique:+;- ; *;/ ; %
Lecoûtd'uneopération élémentaireest égal à 1.
Exemple1: Que vaut le coût de l'algorithme A
somme = n +1 #instr1
somme = somme * n #instr2
somme = somme/2 #instr3
Coût(A) =Coût(inst1)+Coût(instr2)+Coût(instr3)
= 2 +2+2
=6
Complexité 3 /12 M.GUEROIHI
b. Le coût des instructions composées
On appelle opération composée, toute instruction contenant:
L'exécution d'une instruction conditionnelle :
Si (test) alors
Q1
Sinon
Q2
Finsi
Le nombre d'opérations est: Coût(P) = Coût (test)+ max (Coût(Q1),Coût(Q2))
L'exécution d'une boucle : est égal à la multiplication de nombre de répétitions par la somme du coût
de chaque instruction xi du corps de la boucle;
Coût (bouclefor)= ∑ Coût (xi)
Coût(boucle while)= ∑ (Co ût (comparaison )+Coû t (xi) ¿ )¿
L'appel d'une fonction : Lorsqu'une fonction ou une procédure est appelée, le coût de cette fonction
ou procédure est le nombre total d'opérations élémentaires engendrées par l'appel de cette fonction.
Exemple2: Que vaut le coût de l'algorithme B
if i%2==0:
n=i//2
else:
i=i+1
n=i//2
Coût (B)=Coût(i%2==0)+max(Coût(n = i//2),Coût(i = i +1 , n = i//2))
= 2 + max(2,4) = 6
Exemple3: Que vaut le coût de l'algorithme C
i=1
while i <= n :
somme=somme+i
i=i+1
Coût(C)=1+ ∑ (Coû t (i<¿ n)+ Coû t (somme =somme +i)+Co û t (i=i+ 1))
=1+n+2n+2n =1+5n
c. La notation O : motivation
Les calculs à effectuer pour évaluer le temps d'exécution d'un algorithme peuvent parfois être longs et
pénibles;
De plus, le degré de précision qu'ils requièrent est souvent inutile;
On aura donc recours à une approximation de ce temps de calcul, représenté par la notation O(.);
d. Règles de calcul : simplifications
On calcule le temps d'exécution comme avant, mais on effectue les simplifications suivantes:
o On oublie les constantes multiplicatives (elles valent 1);
o On annule les constantes additives;
o On ne retient que les termes dominants;
Exemple (simplifications)
Soit un algorithme effectuant T(n) = 4n3 - 5n2 +2n +3 opérations;
o On remplace les constantes multiplicatives par 1 : 1n3 - 1n2 + 1n +3
o On annule les constantes additives : n3 - n2 + n +0
o On garde le terme de plus haut degré : n3 + 0
Complexité 4 /12 M.GUEROIHI
3
Et on a donc : T(n)= O(n ).
7. EXEMPLES DE CALCUL DE COMPLEXITÉ
Exemple 1 Exemple 2
La fonction suivante permet de La fonction suivante permet de retourner la somme des éléments d’une
retourner le quotient et le reste de liste:
la division d’un nombre entier a def Somme(L):
par b.
s=0
def division (a ,b):
for i in rang(len(L)) :
q=a//b
s=s+L[i]
r=a%b
return s
return(q,r)
Exemple
Exemple >>> L =[1, 2, 8, 4]
>>>x,y=10,3 >>>Somme(L)
>>>division(x , y) 15
3 , 1
Le paramètre de complexité est la taille de la liste d'entrée L.
Le nombre d'opérations est:5 en fixant i on exécute 2 opérations: (addition et affectation)
Temps de calcul est constant Nombre de fois d'exécution de ces 2 opérations est: len(T)
Complexité: O(1) Le nombre total d'opérations est : 4 * len(L) + 3
Complexité: O(len(L))= O(n)
Exemple 3 : remplir un tableau Exemple 4 : remplir une matrice
def RemplirTab (T): def RemplirMat(M):
n=len(M)
for i in range(len(T)):
p=len(M[0])
print("T[",i, "]=",end=' ')
for i in range(n):
T[i]=int(input()) for j in range(p):
Le paramètre de complexité est la taille du print("T[",i,"][",j,"]=")
tableau d'entrée T. T[i][j]=int(input())
en fixant i on exécute 4 opérations: (print, Coût pour saisir une valeur est : 4
input,int et affectation) Le nombre d'itérations de la boucle sur j pour i
Nombre de fois d'exécution de ces 4 opérations fixé égal à : p
est: len(T) Le nombre total pour lire toutes les valeurs pour
Le nombre total d'opérations est : 4 * len(T) i fixé égal à 4p
Complexité: O(len(T))= O(n) Le nombre total d'opérations est : 2 +4p.n
Complexité : O(n.p)
Complexité 5 /12 M.GUEROIHI
Exemple 5 : Produit matriciel
def prodMatrice(A,B):
n=len(A) #nombre de lignes de A
m=len(A[0]) #nombre de colonne de A
p=len(B[0]) #nombre de colonnes de B
C=[p*[0] for i in range(n)]
for i in range(0,n):
for j in range (0,p):
s=0;
for k in range (0,m):
s = s + A[i][k]*B[k][j]
C[i][j]=s
return C
On suppose que A et B sont deux matrices carrées d'ordre n = m = p
Coût pour calculer une valeur de s est : 3(produit, addition et affectation)
Le nombre d'itérations de la boucle sur k pour j fixé égal à m=n
Le nombre d'itérations de la boucle sur j pour i fixé égal à p=n
Le nombre total d'opérations est : T(n)=4+ n + n(n(2+n(3))
Complexité: O(n3)
Exemple6 : Tri par sélection
def TriSelection(T):
n=len(T)
for i in range(n-1):
Posmin=i
for j in range(i+1,n):
if T[j]<T[Posmin]:
Posmin=j
#Permutation
T[i],T[Posmin]=T[Posmin],T[i]
Coût des échanges:
o Le tri par sélection sur n nombres fait n-1 échanges, ce qui fait : 3(n- 1) affectations
Coût des recherches de minimum:
o On recherche le minimum parmi n éléments : au plus 3(n -1) opérations. (c'est le nombre
d'itérations de la boucle sur j pour i fixé égal à n- 1)
o On recherche le minimum parmi n-1 éléments : auplus3 (n-2) opérations. (c'est le nombre
d'itérations de la boucle sur j pour i fixé égal à n-2)
o On recherche en suite le minimum parmi n-2 éléments : 3(n-3) tests.
o ...
Le nombre total de tests est : 3(1+2+3+ … +(n- 2)+(n -1))=3( (n- 1)n)/2
Le nombre total d'opérations est : 3(n- 1) + 3((n- 1)n)/2
Donc, la complexité est quadratique : O(n2)
Complexité 6 /12 M.GUEROIHI
Exemple 7 : recherches séquentielle
def Recherche_Seq(T,x):
i=0
n=len(T)
for i in range(n):
if T[i]==x :
return True
return False
Le nombre d'opérations avant for est : 3
La boucle for contient 2 opérations : (test et return True)
Le nombre de fois d'exécution de ces 2 instructions dans le pire de cas est n
Le nombre d'opérations après la boucle for est : 1(return False)
Le nombre d'opérations total est : 3+2n+1
Complexité : O(n)
Exemple 8 : Recherche dichotomique
def RechDichotomique(T,x):
g,d=0,len(T)-1
while g <= d:
m=(g+d)//2
ifT[m]==x: returnTrue
ifT[m]<x:
g=m+1
else:
d=m-1
return False
Soit k le nombre de passages dans la boucle while.
on divise le nombre d'éléments restants par 2 jusqu'à ce qu'il n'en reste qu'un (k divisions)
((((n/2)/2)/2)/…./2)=1
k
Donc n/2 =1 et ainsi k=log2n
Complexité : O(log2(n))
Complexité 7 /12 M.GUEROIHI
Exemple.9 : les tours de Hanoï
def hanoi (n , A='A' , B='B', C='C'):
if n > 0:
hanoi(n-1, A, C, B)
print("Déplacer le disque %d de la tige %s vers la tige %s."% (n, A, C))
hanoi(n-1, B, A, C)
L'évaluation de la complexité de cet algorithme est assez simple. Nous allons chercher à savoir combien de
déplacements de disques sont nécessaires pour arriver à nos fins. Très simplement, la fonction calculant ceci
peut être définie de la manière suivante (on peut se baser sur la fonction ci-dessus) :
T(0) = 0; T(1) = 1; T(n) = 2*T(n-1)+1. On peut démontrer par récurrence que ceci est égal à T(n) = 2n-1 :
On a T(n+1) = 2*T(n)+1. En supposant que T(n) = 2 n-1, on a T(n+1) = 2*(2n-1)+1. On développe ensuite en
T(n+1) = 2*2n -2+1 ce qui nous donne au final T(n+1) = 2 n+1-1. On a ainsi démontré que si c'est vrai pour n,
alors c'est vrai pour (n+1). Comme on a T(1) = 2*0+1 = 2 1-1 = 1, c'est vrai pour tous les n supérieurs à 1. La
complexité exacte de l'algorithme présenté ci-dessus est donc en 2n-1 pour n disques à déplacer de la tour
On trouve T(n)=2n - 1
Donc lacomplexité est éxponentielle O(2n)
8. DIFFÉRENTESNUANCESDE COMPLEXITÉ
On distingue 3 types de complexités: Complexité au pire des cas, dans le meilleur des cas et en moyenne
des cas.
8-1 : Complexité au pire des cas
La complexité au pire est le plus grand nombre d'opérations qu'aura à exécuter l'algorithme sur un jeu de
données de taille fixée à n.
Exemple: Recherche d'un élément dans un tableau
def find(T,x): On note n la longueur du tableau T(n=len(T)).
for e in T:
Dans le pire des cas : quand l'élément recherché x est le dernier (dans la case
if e==x:
n-1) ou est absent.
return True
return False Donc la complexité dans le pire est Cmax (n)= n
8-2 : Complexité dans le meilleur des cas
La complexité au meilleur est le plus petit nombre d'opérations qu'aura à exécuter l'algorithme sur un jeu de
données de taille fixée. C'est une borne inférieure de la complexité de l'algorithme sur un jeu de données de
taille n.
Exemple: Recherche d'un élément dans un tableau
On considère le même exemple précédent.
Dans le meilleur des cas : quand l'élément recherché x se trouve en 1ere position.
Donc la complexité dans le meilleur des cas est : Cmin(n)=1
Complexité 8 /12 M.GUEROIHI
8-3 : Complexité en moyenne des cas
La complexité en moyenne est la moyenne des complexités de l'algorithme sur des jeux de données de taille n.
Cette complexité en moyenne reflète le comportement "général" de l'algorithme si les cas extrêmes sont rares ou
si la complexité varie peu en fonction des données.
Tmoy (n)={∑ Pr ( d ) .C ( d ) ,dϵ Dn}
Où Pr(d) est la probabilité d'avoir la donnée d en entrée de l'algorithme.
Exemple : Recherche d'un élément dans un tableau
En moyenne, dans l'hypothèse où la probabilité de trouver x dans le tableau est q et si x est présent, il peut se
trouver dans n'importe quelle case avec la même probabilité, à savoir 1/n
Alors la probabilité que x se trouve dans la ième case est :q * 1/n, d'où:
Cmoy (n)= q *(1/n + 2/n + 3/n + …+ n/n) + (1-q)*n=q(n+1)/2 + n(1-q)
Si q=1, la complexité en moyenne des cas sera Cmoy (n)= (n+1)/2
9. COMPLEXITÉ ET RÉCURSIVITÉ
Il existe diverses techniques pour la résolution des équations de récurrence.
méthode des fonctions génératrices et décomposition des fractions rationnelles
Transformée en Z…
Résolution des récurrences.
C(n) = C(n-1) + b
solution : C(n) = c(0) + b*n = O(n)
exemples : factorielle, recherche séquentielle récursive dans un tableau
C(n) = a*C(n-1) + b, a ≠ 1
solution : C(n) = an*(C(0) – b/(1-a)) + b/(1-a) = O(an)
exemples : répétition a fois d'un traitement sur le résultat de l'appel récursif
C(n) = C(n-1) + a*n + b
solution : C(n) = c(0) + a*n*(n+1)/2 + n*b = O(n2).
exemples : traitement en coût linéaire avant l'appel récursif, tri à bulle
C(n) = C(n/2) + b
solution : C(n) = C(1) + b*log2(n) = O(log(n))
exemples : élimination de la moitié des éléments en temps constant avant l'appel récursif, recherche
dichotomique récursive
C(n) = a*C(n/2) + b, a ≠ 1
solution : C(n) = nlog2(a) *(c(1) – b/(1-a)) + b/(1-a) = O(nlog2(a))
exemples : répétition a fois d'un traitement sur le résultat de l'appel récursif dichotomique
C(n) = C(n/2) + n
solution : C(n) = O(n)
exemples : traitement linéaire avant l'appel récursif dichotomique
C(n) = 2*C(n/2) + a*n + b
solution : C(n) = O(n*log(n))
exemples : traitement linéaire avant double appel récursif dichotomique, tri fusion
Complexité 9 /12 M.GUEROIHI
TD : Complexité
Exercice 1
On considère le programme suivant:
def Algo( n):
for i in range(0,n):
Algo1(n)
Algo2(n)
Algo3(n)
Sachant que Algo1(n) s'effectue en temps O(log(n)),Algo2(n) en temps O(n)etAlgo3(n) en temps O(n2), Quelle est
la complexité de Algo(n)?
Exercice 2 :
Quelle est la complexité du programme suivant :
def Algo(n):
res = 1;
for i in range(0,n):
res = res + i
for i in range(0,n):
for j in range(0,i):
res = res * (i+j)
return res
Exercice 3 :
Quelle est la complexité du programme suivant:
def f(x, n):
if n < 1:
return n
S = 0
for i in range(0,n):
S += x/(i+n)
return S + f(x,n/2)
Exercice 4
1- Que vaut la complexité de l'algorithme suivant:
i=1
while i <= n :
i=i*2
2- Le code ci-dessous consiste à programmer la fonction remove. Analyser sa complexité?
def SupprimerElement(L,a):
i=0
if a in L:
while L[i]!=a:
i=i+1
L[i:i+1]=[]
Complexité 10 /12 M.GUEROIHI
Exercice 5
Soient les 3 fonctions suivantes permettant de calculer la valeur d'un polynôme : p(x) = a0+a1x+a2x2+...+anxn
en un point x (c'est-à-dire pour une valeur x donnée).
Les coefficients du polynôme sont stockés dans une liste : A=[a0, a1, a2..., an-1 , an]
def f1(A,x) :#méthode naïve
n =len(A)
p=A[0]
for i in range(1,n) :
p = p + A[i]* x**i
return p
x**i : n’est pas une opération élémentaire
def f2(A,x) :# elle utilise le fait que xi = xi-1* x
n =len(A)
p=A[0] ;
q = 1;
for i in range(1,n) :
q = q * x ;
p = p +A[i]* q ;
return p
#algorithme de Horner ;
il calcule, DANS CET ORDRE, les valeurs de :an, anx+an-1, puis (anx+an-1) x + an-2, etc.
def f3(A,x) :# utilisant la méthode de Horner
n =len(A)
p = A[n-1] ;
for i in range(n-1,0,-1) :
p = p*x + A[i-1]
return p
Comparer leur complexité au pire cas.
Exercice 6
Écrire une fonction qui prend un tableau d’entiers T en argument et renvoie le tableau des sommes cumulées
croissantes correspondantes, autrement dit un tableau S de même taille dont la k-ième composante vaut :
k
S [ k ] =∑ t [i]
i=0
Evaluer la complexité de cette fonction.
Est-il possible d’en écrire une version plus efficace ? Donner alors sa complexité
Exercice 7
Soit u la suite définie par u0 = 1 et pour n ∈ N, un+1 = sin(un).
1. Écrire une fonction nommée suite prenant n ∈ N en argument et renvoyant un.
2. Que calcule la fonction suivante : de fmystere(n):
s = 0
for k in range(n+1):
s = s + suite(k)
return s
3. Déterminer en fonction de n le nombre d’additions et de calculs de sinus nécessaires pour calculer
mystere(n).
Complexité 11 /12 M.GUEROIHI
4. Écrire une autre fonction qui renvoie les mêmes valeurs que mystere mais avec une meilleure complexité.
Complexité 12 /12 M.GUEROIHI