Vous êtes sur la page 1sur 32

Introduction à

la Complexté Algorithmique

Cours et exercices
Filière SMI
2016-2017

Mustapha kchikech

Département de Mathématiques
et Informatique
Faculté Polydsciplinaire-Safi
Université Cadi Ayyad
Table des matières

Introduction iii

1 Complexité algorithmique 1
1.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.2 Complexité des algorithmes . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.2.1 Motivation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.2.2 Complexité . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.2.3 Analyse des algorithmes . . . . . . . . . . . . . . . . . . . . . . . . 3
1.2.3.1 Taille des données . . . . . . . . . . . . . . . . . . . . . . 4
1.2.3.2 Le temps d’exécution . . . . . . . . . . . . . . . . . . . . 5
1.2.3.3 Évaluation des coûts . . . . . . . . . . . . . . . . . . . . . 5
1.2.3.4 Objectif . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
1.2.3.5 Évaluation du temps de calcul . . . . . . . . . . . . . . . 6
1.2.3.6 Évaluation de T(n) . . . . . . . . . . . . . . . . . . . . . 6
1.2.4 Notation asymptotique . . . . . . . . . . . . . . . . . . . . . . . . . 7
1.2.4.1 Pourquoi une analyse asymptotique ? . . . . . . . . . . . 7
1.2.4.2 Définition Θ-Notation . . . . . . . . . . . . . . . . . . . 8
1.2.4.3 Définition, O-Notation . . . . . . . . . . . . . . . . . . . 9
1.2.4.4 Complexité d’un algorithme . . . . . . . . . . . . . . . . 9
1.2.4.5 Classes de complexité . . . . . . . . . . . . . . . . . . . . 9
1.2.4.6 Hiérarchie entre les classes de complexité . . . . . . . . . 10
ii TABLE DES MATIÈRES

1.2.5 Exemples et exercices . . . . . . . . . . . . . . . . . . . . . . . . . 11

2 Réurrence 13
2.1 Présentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
2.2 Résolution des récurrences . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
2.2.1 Méthode itérative . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
2.2.2 Méthode de substitution . . . . . . . . . . . . . . . . . . . . . . . . 18
2.2.3 Méthode générale . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
2.2.4 DIVISER-POUR-RÉGNER . . . . . . . . . . . . . . . . . . . . . . 20
2.3 Résolution des récurrences . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
2.3.1 Théorème général : . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
2.3.1.1 Signification intuitive du théorème . . . . . . . . . . . . . 22
2.4 Exercices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
Introduction

Ce polycopié présente une introduction sur la complexité algorithmique et des notions


sur la récurrence en algorithmique. L’objectif est de présenter des concepts fondamentaux
de la théorie de la complexité algorithmique qui offrent un ensemble d’outils permettant
d’analyser, de comparer et d’optimiser des algorithmes par la suite des programmes
informatique.
Par conséquent, ce document met également à la disposition des étudiants un sup-
port de cours qui pourrait leur permettre d’acquérir et d’améliorer leurs compétences en
programmation.
Notons que ce document est un support pédagogique qui s’adresse principalement aux
étudiants de la filière SMI semestre 4 de la Faculté Poly-disciplinaire de Safi, université
cadi Ayyad. Il pourrait également servir comme support de cours aux étudiants d’autres
filières.
iv Introduction
Chapitre 1

Complexité algorithmique

1.1 Introduction

— Le mot algorithme provient du nom du mathématicien arabe du IXe siècle, Mo-


hammed Ibn-Moussa Al-Khuwarizmi (livre d’arithmétique).
— Autant dire que les algorithmes sont connus et utilisés bien avant les débuts de
l’informatique.
— Premier algorithme est l’Algorithme d’EUCLIDE (300 avant JC) pour calculer le
plus grand diviseur commun de deux entiers.
— La notion actuelle d’algorithme a été élaborée par les logiciens des années 1930
(Herbrand, Gödel, Church et Turing).
— En 1968, Knuth publia le premier ouvrage The Art of Computer Programming
sur l’étude moderne des algorithmes informatiques.
— Il existe différentes façons de définir un algorithme. C’est pourquoi, nous présentons
ici quelques définitions qui peuvent donner une idée sur la notion d’algorithme.
— Un algorithme est une procédure de calcul bien définie, qui prend en entrée une
valeur, ou un ensemble de valeurs, et qui produit, en sortie, une valeur ou un
ensemble de valeurs. Un algorithme est donc une séquence d’étapes de calcul per-
mettant de passer de la valeur d’entrée à la valeur de sortie.
2 Complexité algorithmique

— Un algorithme est une procédure de calcul spécifique permettant d’obtenir une


relation désirée entre l’entrée et la sortie d’un problème.
— L’algorithmique est la branche de l’informatique qui traite des algorithmes.

Exemple 1.

— Algorithmes d’Euclide :
Entrée : deux entiers naturels a,b
Sortie : pgcd(a,b)
— Calcul de puissance :
Entrée : deux entiers naturels a,n
Sortie : an
— Algorithmes de tri :
Entrée : un tableau de n entiers tab = [a0 , a1 , · · · , an−1 ]
Sortie : un tableau de n entiers tab = [b0 , b1 , · · · , bn−1 ] t.q. b0 ≤ b1 ≤ · · · ≤ bn−1
— Algorithme de recherche dans un arbre binaire de recherche :
Entrée : un arbre binaire de recherche, une valeur v
Sortie : une réponse : la valuer figure oui ou non dans l’arbre
— Test de primalité :
Entrée : un entier naturel n
Sortie : une réponse : n est premier oui ou non
— ···

1.2 Complexité des algorithmes

1.2.1 Motivation

— Un algorithme est dit correct si, pour chaque donnée en entrée, il doit fournir la
bonne sortie.
— Dans ce cas, on dit que l’algorithme (correct) résout le problème donné.
— Pour résoudre informatiquement un problème donné, on implante donc un al-
gorithme sur un ordinateur. C-à-d, l’algorithme sera réécrit avec un langage de
programmation.
1.2 Complexité des algorithmes 3

— Mais, pour un problème donné, il existe bien souvent plusieurs algorithmes qui le
résout.
— Ces algorithmes diffèrent entre eux en termes d’efficacité. Ces différences peuvent
être bien plus importantes que celles dues au matériel et au logiciel.
— Y a-t-il un intérêt à choisir des algorithmes ? et si oui comment choisir ?

1.2.2 Complexité

En informatique, la notion de complexité signifie deux concepts :


— la complexité des algorithmes : C’est l’étude de l’efficacité comparée des algo-
rithmes.
— En effet, la complexité introduit la notion de coût et montre qu’il ne suffit pas
de trouver une méthode correcte pour résoudre un problème, il faut aussi que
cette méthode soit efficace.
— L’efficacité est mesurée par le temps et l’espace nécessaire à un algorithme
pour résoudre un problème.
— La complexité des problèmes : C’est l’étude qui permet classification des problèmes
en fonction des performances des meilleurs algorithmes connus qui les résolvent.

Remarque 1.

Pour notre cours, on parlera de la complexité c’est pour designer la complexité


des algorithmes.

1.2.3 Analyse des algorithmes

— La résolution informatique d’un problème donné passe par un algorithme qui est
implémenté par un langage de programmation.
— L’exécution d’un programme a un coût. Il existe deux paramètres essentiels pour
évaluer ce coût :
— le temps d’exécution : la complexité temporelle
4 Complexité algorithmique

— l’espace mémoire requis : la complexité spatiale


— Ainsi, analyser un algorithme est une opération qui consiste à prévoir les res-
sources nécessaires (mémoire, temps de calcul) au programme qui implémente cet
algorithme.
— Pour pouvoir analyser un algorithme, il faut avoir un modèle de la technologie
qui sera employée.
— En algorithmique, les algorithmes sont analysés selon les calculs dans un modèle
générique de machine à accès aléatoires (RAM=Random Access Machine), à pro-
cesseur unique.
— Dans le modèle RAM, les instructions sont exécutées l’une après l’autre, sans
opérations simultanées.
— En résumé,
Analyse des algorithmes ⇐⇒ étude de la complexité des algorithmes

1.2.3.1 Taille des données

Si l’on prend en compte tous les paramètres : fréquence d’horloge, nombre de proces-
seurs, temps d’accès disque,... l’estimation de ces ressources peut :
— être assez compliquée, voire irréalisable,
— devenir irréaliste dès que l’on change d’architecture.
— Pour cela on se contente souvent d’estimer l’influence de la taille des données sur
la taille des ressources nécessaires. Ainsi,

La taille des données dépend du problème étudié


— On appelle les entrées ou les instances la taille des données nécessaires à un algo-
rithme pour résoudre un problème donné.
— La taille n va dépendre du codage de ces entrées
Exemple : en binaire, il faut blog2 (n)c + 1 bits pour coder l’entier n.
— En pratique, on choisit comme taille la ou les dimensions les plus significatives.
— Exemple :
1.2 Complexité des algorithmes 5

— des matrices m × n : max(m, n), m.n, m + n


— des listes, tableaux, fichiers : nombre de cases, d’éléments
— des chaı̂nes de caractères : leur longueur

1.2.3.2 Le temps d’exécution

— Le temps d’exécution d’un algorithme sur une entrée particulière est le nombre
d’opérations élémentaire (affectations, comparaisons, opérations arithmétiques),
exécutées.
— Le temps d’exécution du programme dépend :
— des données du problème pour cette exécution
— de la qualité du code engendré par le compilateur
— de la nature et de la rapidité des instructions offertes par l’ordinateur
— de l’efficacité de l’algorithme
— de l’encodage des données
— ... et aussi de la qualité de la programmation
— En général, on ne peut pas mesurer le temps de calcul sur toutes les entrées
possibles. Il faut trouver une autre méthode d’évaluation.
— L’idée est d’évaluer le temps de calcul en fonction d’une grandeur n représentant
la taille des données.

1.2.3.3 Évaluation des coûts

— P : un problème et M : une méthode pour résoudre le problème P


— Algorithme A : description de M dans un langage donné (algorithmique, C/C++,
Java, ...)
— Structures de contrôle :
— séquence (suite d’instructions)
— embranchement (ou sélection) : if() ou switch()
— boucle (ou itération) : for(); do...while(); while()
6 Complexité algorithmique

1.2.3.4 Objectif

— Évaluer le temps de calcul de A pour P


— Comparer A avec un autre algorithme A0
— L’évaluation :
— dépends de la taille du problème,
— doit être indépendante de la machine utilisée.

1.2.3.5 Évaluation du temps de calcul

— La complexité d’un algorithme ou l’évaluation du temps de calcul d’un algorithme


est une mesure exprimée en fonction de la taille n des données :
T(n)=nombre d’opérations élémentaires
— On distingue trois sortes de complexités :
— la complexité dans le pire des cas : calcul du coût dans le pire des cas,
Tpire (n) =max{T (n)}
n
— la complexité en moyenne : on calcule le coût pour chaque donnée possible
puis on divise la somme de ces coûts par le nombre de données différentes,
Tmoyenne (n) = n1 {T (n)}
P
n
— la complexité dans le meilleur des cas : on calcule le coût en se plaçant dans
le meilleur des cas.
Tmeilleur (n) =min{T (n)}
n
— Remarque : En analyse de complexité, on étudie souvent le pire cas ce qui donne
une borne supérieure de la complexité de l’algorithme.

1.2.3.6 Évaluation de T(n)

— Séquence (sommes des coûts) :


Exemple 2.
1.2 Complexité des algorithmes 7




 Traitement T1 (n)

··· ⇒T (n) = T1 (n) + T2 (n) + · · · + Tp (n)



 Traitement T (n)
p

— Embranchement (Max des coûts) :


Exemple 3.




 si (condition logique) alors


 Traitement T1 (n)

⇒T (n) = max(T1 (n), T2 (n))
 sinon





 Traitement T (n)
2

— Boucle (Somme des coûts de chaque passage) :


Exemple
 4.
 Tant que (condition logique) faire


 k
P
Traitement Ti (n) ⇒T (n) = Ti (n)


 i
 Fin faire
k =nombre de répétition du traitement Ti (n).

1.2.4 Notation asymptotique

1.2.4.1 Pourquoi une analyse asymptotique ?

— En pratique une analyse précise d’un algorithme même dans le pire des cas est
presque impossible (sauf pour un algorithme simple).
— La solution est d’établir une approximation asymptotique du temps de calcul de
l’algorithme. Autrement, on fait une analyse asymptotique.
— On veut comparer des algorithmes différents sans les implémenter, sans développer
des programmes. Pour faire ça on compare l’efficacité asymptotique des algo-
rithmes.
8 Complexité algorithmique

1.2.4.2 Définition Θ-Notation

Soient f et g deux fonctions de N dans R.


— On dit que g(n) est une borne approchée asymptotique pour f (n) et l’on écrit
f (n) ∈ Θ(g(n)) s’il existe deux constantes strictement positives c1 et c2 telles que,
pour n assez grand, on ait

0 ≤ c1 g(n) ≤ f (n) ≤ c2 g(n)

— Ceci revient à dire que f (n) est égale à g(n) à un facteur constant près.
— Pour indiquer que f (n) ∈ Θ(g(n)), on écrit

f (n) = Θ(g(n))

Exemple 5.

— Montrons que f (n) = 12 n2 − 3n = Θ(n2 ).


— On doit trouver c1 , c2 , n0 > 0 telles que
1.2 Complexité des algorithmes 9

pour tout n ≥ n0
1 2
c1 n2 ≤ 2n − 3n ≤ c2 n2
1 3
c1 ≤ 2 − n ≤ c2

Prenons c2 = 12 , on a
1 3
∀n ≥ 1, − ≤ c2
2 n
1
Prenons c1 = 14 , on a
1 3
∀n ≥ 7, c1 ≤ −
2 n
1
On prend donc n0 = 7, c1 = 14 et c2 = 12 .

1.2.4.3 Définition, O-Notation

Soient f et g deux fonctions de N dans R.


— On dit que g(n) est une borne supérieure asymptotique pour f (n) et l’on écrit
f (n) ∈ O(g(n)) s’il existe une constante strictement positive c telle que pour n
assez grand on ait
0 ≤ f (n) ≤ cg(n)
— Ceci revient à dire que f (n) est inférieure à g(n) à une constante près et pour n
assez grand.
— Pour indiquer que f (n) ∈ O(g(n)), on écrit
f (n) = O(g(n))

1.2.4.4 Complexité d’un algorithme

— Un algorithme A résout un problème P en temps O(f (n)) si pour toute instance


de taille n, l’algorithme retourne une solution correcte avec un coût O(f (n)).
— La nature de la fonction f (n) définie la classe de complexité de l’algorithme A.

1.2.4.5 Classes de complexité

Lors de l’analyse de complexité, on essaie de se ramener aux classes suivantes :


10 Complexité algorithmique

— temps constant :Coût : O(1) Exemple : addition, affectation...


— Complexité logarithmique : Coût : O(log(n))
Exemple : recherche dichotomique dans un tableau trié A[1...n]
— Complexité linéaire : Coût : O(n) Exemple : calcul du produit scalaire de deux
vecteurs de Rn
— Complexité quasi-linéaire : Coût : O(nlog(n))
Exemple : Tri par fusion
— Complexité polynômial : Coût : O(nk ), k > 1, pour k = 2, on parle de la Com-
plexité quadratique Exemple : Multiplication de deux matrices carrées d’ordre n :
O(n3 )
— Complexité exponentielle : Coût : O(an ) avec a > 1
Exemple : Tours de Hanoı̈

1.2.4.6 Hiérarchie entre les classes de complexité

On peut établir une hiérarchie entre les classes de complexité du plus petit au plus
grand :
— O(1)→O(log(n))→O(n)→O(nlog(n))→O(n2 )→O(nk )→O(an )
1.2 Complexité des algorithmes 11

k > 2, a > 1.
— Comparaison :

1.2.5 Exemples et exercices

1. Quelle est la complexité de ces parties de programme ?

somme=0;
for(i=1; i<=n; i++)
somme += n;
——————————————-
somme=0;
for (j=1; j<=n; j++)
a for (i=1; i<=n; i++)
a somme++;
for (k=0; k<n; k++)
a A[k] = k;
——————————————-
somme = 0;
for (i=1; i<=n; i++)
a for (j=1; j<=i; j++)
somme++;
12 Complexité algorithmique

——————————————-
somme = 0;
for (i=1; i<=n; i*=2)
a for (j=1; j<=n; j++)
a somme++;

2. Pour chacun des problèmes suivants, écrire en langage C une fonction itérative
qui permet de le résoudre et donner sa complexité.

(a) Puissance n-ème d’un entier.

(b) Suite de Fibonacci.

(c) Tri des tableaux.

(d) Test de Primalité d’un entier.


Chapitre 2

Réurrence

2.1 Présentation

— Une récurrence est une équation ou une inégalité qui décrit une fonction à partir
de sa valeur sur des entrées plus petites.
— une fonction est récursive si elle fait appel à elle-même d’une manière directe ou
indirecte.
— Un algorithme est dit récursif si son temps de calcul peut être décrit par une
récurrence.
14 Réurrence

Remarque 2.

— L’utilisation des algorithmes récursifs est une technique de programma-


tion qui permet de trouver des solutions d’une grande élégance à un
certain nombre de problèmes.
— Dans un algorithme récursif, la récursivité doit s’arrêter à un moment
donné (test d’arrêt). Autrement, l’exécution va continuer indéfiniment.
— Ce processus est connu sous le nom du processus de réduction qui à
chaque appel, on doit se rapprocher de la condition d’arrêt.
— Attention ! : lorsqu’elle mal utilisée, on peut créer un code de programme
totalement inefficace.

Suite de Fibonacci
— La suite de Fibonacci est définie par :

 u0 = u1 = 1

n+2 = un+1 + un si n ≥ 2
 u

— Un algorithme récursif qui calcule le nième terme de cette suite :


int fib(int n)
{
a if(n<=1)
a return 1;
a else
a return fib(n-1)+fib(n-2);
}
— Complexité :
— Soit T (n) =nombre d’additions effectuées par cet algorithme lors du calcul.
— T (n) vérifie les équations suivantes :
2.2 Résolution des récurrences 15


 T (0) = T (1) = 1
 T (n) = T (n − 2) + T (n − 1) si n ≥ 2

— C’est une équation de récurrence où chaque terme d’ordre ≥ 2 dépend unique-
ment des 2 termes qui le précèdent.
— La solution de l’équation de récurrence est

√ √ √ √
5 + 5 1 + 5 n 5 − 5 1 − 5 n
T (n) = +
10 2 10 2

√  
1+ 5 n
— T (n) =O 2 .
— La complexité de cet algorithme est donc exponentielle !

2.2 Résolution des récurrences

Dans ce chapitre, nous allons proposer trois méthodes de résolution des récurrences,
c-à-d pour obtenir des bornes asymptotiques Θ ou O pour la solution.

— Méthode itérative
— Méthode de substitution
— Méthode générale
16 Réurrence

Remarque 3.

— En pratique, quand on définit et résout des récurrences, on omet souvent


les parties entières et les conditions aux limites.
— En fait, ces détails n’affectent pas les bornes asymptotiques des
récurrences rencontrées dans l’analyse des algorithmes. En effet :
— On passe souvent sur le fait que les arguments des fonctions sont
des entiers. Normalement, le temps d’exécution T (n) d’un algorithme
n’est définit que pour n entier puisque, dans la plupart des algo-
rithmes, la taille de l’entrée a toujours une valeur entière.
— Le plus souvent on ignore les conditions aux limites. Puisque le
temps d’exécution d’un algorithme sur une entrée de taille constante
est une constante, les récurrences sous-jacentes au calcul du temps
d’exécution des algorithmes ont généralement T (n) = Θ(1) pour n
suffisamment petit. Ainsi, pour simplifier, on suppose généralement
que T (n) est constant pour n petit.

2.2.1 Méthode itérative

— L’idée de la méthode consiste à développer la récurrence en sommation.


— On utilise les techniques d’évaluation des sommations pour trouver les bornes à
la solution.
— Cette méthode nécessite plus de manipulations algébriques.

Exemple 6.

n
T (n) = 2T ( ) + 2n + 1
2
2.2 Résolution des récurrences 17

On développe comme suit :

n
T (n) = 2n + 1 + 2T ( )
2
n
= 2n + 1 + 2(n + 1 + 2T ( ))
4
n n
= 2n + 1 + 2(n + 1 + 2( + 1 + 2T ( )))
2 4
n
= 3(2n) + 1 + 2 + 4 + 23 T ( )
4
= ···
i−1
P j n
= 2in + 2 + 2i T ( i )
j=0 2

n
On arrive à T (1) lorsque = 1, c-à-d lorsque i = log(n). Ainsi,
2i

log(n)−1
2j + nT (1)
P
T (n) = 2n log(n) +
j=0

= 2n log(n) + n − 1 + n Θ(1)
= 2n log(n) + n − 1 + Θ(n)

Par conséquent T (n) = O(n log(n))

Exemple 7.
n
T (n) = 3T ( ) + n
4

On développe comme suit :

n
T (n) = n + 3T ( )
4
n n
= n + 3( + 3T ( 2 ))
4 4
n n n
= n + 3( + 3( 2 + 3T ( 3 )))
4 4 4
3 3 2 3 n
= n + n + ( ) n + 3 T( 3)
4 4 4
= ···
i−1
P 3 j n
= ( ) n + 3i T ( i )
j=0 4 4
18 Réurrence

n
On arrive à T (1) lorsque = 1, c-à-d lorsque i = log4 (n). Ainsi,
4i
3
T (n) = 4n(1 − ( )log4 (n) ) + 3log4 (n) T (1)
4
3 log4 (n)
= 4n(1 − ( ) ) + nlog4 (3) Θ(1) car 3log4 (n) = nlog4 (3)
4
3
= 4n + Θ(nlog4 (3) ) pour n assez grand ( )log4 (n) ≈ 0
4
Par conséquent T (n) = O(n)

2.2.2 Méthode de substitution

Cette méthode recouvre deux phases :


— On conjecture la forme de la solution : c-à-d on devine (par intuition) une idée
de la solution.
— On utilise une récurrence mathématique pour trouver les constantes et pour mon-
trer que la solution est correcte.

Remarque 4.

— Le nom de cette méthode vient du fait que l’on substitue la réponse


supposée à la fonction quand on applique l’hypothèse de récurrence aux
valeurs plus petites.
— Elle ne s’applique que lorsque la forme de la réponse est facile à deviner.

Exemple 8.
n
T (n) = 2T ( ) + n
2

On conjecture (par intuition) que la solution est T (n) = O(n log(n)).


La méthode consiste à démontrer que T (n) ≤ cn log(n) pour un choix approprié de la

constante c > 0.
n n n
On suppose que T ( ) ≤ c log( ).
2 2 2
La substitution donne
2.2 Résolution des récurrences 19

n n
T (n) ≤ 2(c log( )) + n
2 2
n
≤ cn log( ) + n
2
= cn log(n) − cn log(2) + n
= cn log(n) − cn + n
≤ cn log(n) si c ≥ 1

Remarque 5.

Comment conjecturer les solutions des récurrences ?


— Malheureusement, il n’existe pas de règle générale.
— Deviner une bonne solution ressort de l’expérience, c-à-d se servir des
récurrences déjà rencontrées. Par exemple si T (n) = 2T ( n2 + 100) + n.
Pour n assez grand, T ( n2 ) et T ( n2 + 100) sont quasiment les mêmes. On
peut donc supposer que T (n) = O(n log(n)).
— Parfois, il suffit d’une petite manipulation algébrique pour faire resembler
une récurrence inconnue à une autre déjà vue. Par exemple, on utilise un
changement de variables.

Exemple : Prenons T (n) = 2T ( n) + log(n).
m
Posons m = log(n) ⇒ n = 2m . On aura donc T (2m ) = 2T (2 2 ) + m.
On pose S(m) = T (2m ), on obtient une nouvelle récurrence
m
S(m) = 2S( ) + m ⇒ S(m) = O(m log(m)).
2
D’où T (n) = T (2m ) = S(m) = O(m log(m)) = O(log(n) log(log(n))).
— Et parfois, de l’intuition pure ! ! !

Exemple & exercice : La recherche dichotomique


— Vérifier que le nombre d’itération de l’algorithme de recherche dichotomique d’un
tableau de taille n est
n
T (n) = T ( ) + 1
2
20 Réurrence

— Montrer que

T (n) = O(log(n))

2.2.3 Méthode générale

La méthode générale est une méthode qui s’appuie sur le théorème maı̂tre de la
complexité, il est connu comme Master theorem en anglais et on l’appelle Théorème
général en français.
— Elle est considérée comme une ”recette” pour résoudre les récurrences de la forme

n
T (n) = aT ( ) + f (n),
b

où a ≥ 1 et b > 1 sont des constantes, et f (n) une fonction asymptotiquement


positive.
— Elle permet d’analyser le temps d’exécution T (n) (au pire des cas) d’un algorithme
récursif obtenu par l’approche ”DIVISER-POUR-RÉGNER”.

2.2.4 DIVISER-POUR-RÉGNER

— L’approche ”diviser-pour-régner ” est une technique pour la conception des algo-


rithmes.
— En effet, un algorithme basé sur l’approche ”diviser-pour-régner ”

1. sépare le problème en plusieurs sous-problèmes semblables au problème initial


mais de taille inférieure,

2. résolve les sous-problèmes de façon récursive,

3. combine toutes les solutions pour produire la solution du problème original.

— Le principe de l’approche ”diviser-pour-régner ” se base sur trois étapes à chaque


niveau de récursivité :

1. Diviser le problème en un certain nombre de sous-problèmes.


2.3 Résolution des récurrences 21

2. Régner sur les sous-problèmes en les résolvant récursivement.


Si la taille d’un sous-problème est assez réduite, on peut le résoudre directe-
ment.

3. Combiner les solutions aux sous-problèmes en une solution complète pour le


problème initial.

sous-problème 1 solution 1

sous-problème 2 solution 1

Problème sous-problème j solution 1 Solution

sous-problème p solution 1
diviser régner combiner

2.3 Résolution des récurrences

— La récurrence de la forme T (n) = aT ( nb ) + f (n), décrit le temps d’exécution d’un


algorithme qui divise un problème de taille n en a sous-problèmes, chacun de taille
n
.
b
— Les a sous-problèmes sont résolus récursivement, chacun dans un temps T ( nb ).
— f (n) = D(n) + C(n) représente le coût induit par la décomposition du problème
(D(n)) et la combinaison des résultats des sous-problèmes (C(n)).
22 Réurrence

2.3.1 Théorème général :


Théorème 1.

Soient a ≥ 1 et b > 1 deux constantes, soit f (n) une fonction et soit T (n) définie
pour les entiers n ≥ 0 par la récurrence

n
T (n) = aT ( ) + f (n),
b

T (n) peut alors être bornée asymptotiquement de la façon suivante.

1. Si ∃ε > 0 t.q. f (n) = O(nlogb (a)−ε ) alors T (n) = Θ(nlogb (a) ).

2. Si f (n) = Θ(nlogb (a) ) alors T (n) = Θ(nlogb (a) log(n)).

3. Si ∃ε > 0 t.q. f (n) = Ω(nlogb (a)+ε ) et si ∃c < 1 t.q. af ( nb ) ≤ cf (n), pour


n suffisamment grand, alors T (n) = Θ(f (n)).
f (n) = Ω(g(n)) si ∃c > 0 t.q. pour n assez grand on ait 0 ≤ cg(n) ≤ f (n)

2.3.1.1 Signification intuitive du théorème

Dans chaque cas, on compare la fonction f (n) à la fonction nlogb (a) . La solution de la
récurrence est déterminée par la plus grande des deux.
— Cas 1 : nlogb (a) est la plus grande T (n) = Θ(nlogb (a) ).
— Cas 2 : f (n) est la plus grande T (n) = Θ(f (n)).
— Cas 3 : f (n) = nlogb (a) , T (n) = Θ(nlogb (a) log(n)).

Remarque 6.

Le théorème général ne couvre pas toutes les possibilités pour f (n).

Exemple 9.
n
T (n) = 9T ( ) + n
3
2.4 Exercices 23

Pour cette récurrence, on a a = 9, b = 3 et f (n) = n.


On a donc nlogb (a) = nlog3 (9) = Θ(n2 ) et f (n) = n = O(nlog3 (9)−ε ), avec ε = 1.
D’après le cas 1 du théorème général, on peut dire que T (n) = Θ(n2 ).

Exemple 10.
2n
T (n) = T ( )+1
3
3
Pour cette récurrence, on a a = 1, b = 2 et f (n) = 1.
(log 3 (1))
On a donc nlogb (a) = n 2 = n0 = 1. On est dans le cas 2 puisque f (n) =
Θ(nlogb (a) ) = Θ(1).
La solution de la récurrence est donc T (n) = Θ(log(n)).

Exemple 11.
n
T (n) = 3T ( ) + n log(n)
4
Pour cette récurrence, on a a = 3, b = 4 et f (n) = n log(n).
On a donc nlogb (a) = nlog4 (3) = O(n0.793 ). Puisque f (n) = Ω(nlog4 (3)+ε ), avec ε ' 0.2, on
est dans le cas 3.
Pour n suffisamment grand, a f ( n4 ) = 3 n4 log( n4 ) ≤ 3 n4 log(n) = c f (n) avec c = 3
4 < 1.
Par conséquent, D’après le cas 3, on peut dire que T (n) = Θ(n log(n)).

Exemple 12.
n
T (n) = 2T ( ) + n log(n)
2
On a a = 2, b = 2 et f (n) = n log(n). On a donc nlogb (a) = nlog2 (2) = n. Dans ce cas, le
théorème général ne s’applique pas à la récurrence. En effet,
On pourrais penser que le cas 3 s’applique, puisque f (n) est plus grande asymptotique-
f (n)
ment que nlogb (a) = n. Le problème est que nlogb (a)
= log(n) est asymptotiquement plus
petit que nε , ∀ε > 0.

2.4 Exercices

Exercice 1 :
24 Réurrence

Pour chacune des fonctions suivantes, déterminer le temps de calcul dans le pire des
cas et en O() en justifiant votre réponse :

1. int fonct1(int n) 2. int fonct2(int n)


{ {
int i,j,s=0; int i,j,s=n;
for(j=30;j>10;j--) for(i=200;i>100;i-=20)
for(i=n/2;i>0;i-=2) for(j=n;j>=n-4;j-=2)
s*=2; s/=2;
return s; return s;
} }

3. int fonct3(int n) 4. int fonct4(int n)


{ {
int i,j=1,s=0; int i=0,j,s=0;
while(j<n) do
{ {j=i++;
for(i=1;i<n*n;i*=2) while(j<2*i)
s++; {
j++; s++;
} j+=3;
return s; }
} }while(i<n);
return s;
}

5. int fonct5(int n) 6. int fonct6(int n)


{ {
if(n==0) if(n==0)
return 1; return 1;
return 2*fonct5(n/4); return fonct6(n-1)+fonct6(n-1);
} }

Exercice 2 :

1. Donner la complexité des algorithmes itératifs suivants :


2.4 Exercices 25

(a) Ajouter un élément en tête, fin et quelconque, d’une liste chaı̂née.

(b) Supprimer un élément en tête, fin et quelconque, liste chaı̂née.

(c) Rechercher un élément quelconque dans une liste chaı̂née.

2. Évaluer la complexité des méthodes itératives proposées pour

(a) calculer la puissance nième d’un entier,

(b) trier (selection, bulle, insertion) un tableau (ou une liste),

(c) calculer le nième terme de la suite de Fibonacci,

(d) effectuer la recherche dichotomique dans une table,

(e) parcourir un arbre binaire et rechercher un élément dans un arbre binaire AVL.

Exercice 3 :

1. Donnez des bornes asymptotiques approchées pour T (n) dans chacun des récurrences
suivantes :

(a) T (n) = 4T ( n2 ) + n.

(b) T (n) = 4T ( n2 ) + n3 .

(c) T (n) = 2T ( n2 ) + n.
√ √
(d) T (n) = nT ( n) + n.

2. Le temps d’exécution d’un algorithme A1 résolvant un problème P est décrit par


la récurrence T1 (n) = 7T1 ( n2 ) + n2 . Un autre algorithme A2 résolvant le même
problème P a un temps d’exécution décrit par T2 (n) = αT2 ( n4 ) + n2 . Quelle est la
plus grande valeur de α telle que A2 soit asymptotiquement plus rapide que A1 ?

3. Soit une fonction définie par :

int fonct(int n)
{
if(n<2) return 1;
else return 4*fonct(n-1)-2*fonct(n-2);
}
26 Réurrence

(a) Soit T (n) le temps d’exécution de la fonction fonct(). Exprimer T (n) en


fonction de T (n − 1) et T (n − 2).

(b) Calculer la complexité dans le pire des cas et en O() de cette fonction. Ce
programme est-il efficace ?

(c) Proposer une autre fonction en O(n) pour effectuer le même traitement.

Exercice 5 :

1. Donner la complexité des algorithmes récursifs suivants :

(a) Supprimer un élément quelconque dans une liste chaı̂née.

(b) Rechercher un élément quelconque dans une liste chaı̂née.

(c) calculer la puissance nième d’un entier,

(d) trier (fusion, rapide, tas) un tableau (ou une liste),

(e) calculer le nième terme de la suite de Fibonacci,

(f) effectuer la recherche dichotomique dans une table.

2. Évaluer la complexité des différents algorithmes récursifs, vus au cours et en TD,


traitant les arbres binaires.

Exercice 4 :
Le tri fusion est une méthode algorithmique basée sur la technique ”diviser pour
régner”. Le principe de cette méthode peut être décrit par l’algorithme récursif suivant :
— On divise la liste en deux parties presque de même tailles,
— On trie les deux parties,
— On fusionne les deux parties.
Ce processus de la récursivité s’arrête une fois la décomposition des sous-listes donnent
des sous-listes composées d’un seul élément et le tri est alors trivial.

1. Écrire une fonction de prototype liste diviseListe(liste l) qui permet de


diviser une liste chaı̂née en deux sous-liste. Calculer sa complexité en fonction de
la taille de la liste d’entrée.
2.4 Exercices 27

2. Écrire une fonction de prototype liste FusionListe(liste lG,liste lD) qui


permet de fusionner deux listes. Calculer sa complexité.

3. Écrire une fonction de prototype void triFusionListe(liste *l) qui permet


de trier une liste chaı̂née d’entiers. Calculer sa complexité en fonction de la taille
de la liste d’entrée.

Vous aimerez peut-être aussi