Loin de moi l’idée de faire un article complet sur la notion de complexité, mais en travaillant sur le nouveau programme de NSI (qui entre en vigueur à la rentrée 2019), je me suis aperçu que cette notion allait pointer le bout de son petit museau perfide… Je voudrais donc par cet article familiariser les élèves avec elle.
C’est quoi la complexité d’un algorithme ?
C’est le nombre d’opérations et d’affectations faites. On appelle cela des opérations élémentaires. Prenons le programme Python suivant:
def convert(n): h = n // 3600 m = (n - 3600*h) // 60 s = n % 60 return h,m,s n = 102152 print("{} secondes = {} h {} min {} sec.".format(n,convert(n)[0],convert(n)[1],convert(n)[2]))
Dans la fonction “convert”, il y a :
- 3 affectations (h, m et s)
- 5 opérations (n//3600, 3600*h, n-3600*h, (n-3600*h)//60 et n%60)
La complexité de cette fonction est donc égale à 8.
Exercice 1
Montrer que la complexité de la fonction suivante est égale à 5.
def myFunction(n): if n%3 == 0: p = n/3 + 2 else: p = n*2 + 1 return p
Solution
il y a d’abord un test (if) dans lequel il y a une opération (n%3), ce qui nous fait pour le moment une complexité de 2.
Ensuite, quelle que soit l’issue du test, il y a 1 affectation (p) et 2 opérations (pour calculer p), soit une complexité augmentée de 3.
Au final, on obtient bien une complexité de 5.
Structures itératives
Dans un programme, il y a souvent des boucles. Nous allons voir un exemple avec une boucle “for”:
def recherche(l,x): for i in range(len(l)): if l[i]==x: return i return -1 l = "Bonjour." x = "p" print(recherche(l,x))
La fonction recherche a pour but d’afficher le rang où se trouve le caractère “x” dans la chaîne de caractères “l”, et affiche “-1” si ce dernier n’est pas trouvé.
- Au niveau de la boucle for, il y a 1 addition (sur i), une affectation (i) et une comparaison (test si i<len(l));
- dans la boucle for, il y a un test (if l[i]==x).
Au final, si n est la longueur de la chaîne de caractères, il y a 4n opérations élémentaires, qui correspond à la complexité de la fonction recherche.
Exercice 2
Calculer la complexité de la fonction somme définie dans ce programme:
def somme(n): s = 0 for i in range(n+1): s += i return s print(somme(100))
Solution
Il y a :
- au début, 1 affectation (s = 0);
- au niveau de la boucle for, 3 opérations élémentaires (affectation sur i, opération d’incrémentation sur i, test sur i);
- dans la boucle for, il y a 2 opérations élémentaires (1 affectation sur s et une opération sur s).
Ainsi, il y a 5 opérations élémentaires dans la boucle, répétées n fois, plus la première (hors boucle). La complexité de la fonction somme est donc égale à 5n+1.
En général, on appelle T la complexité (initiale de Time).
Exercice 3
Calculer la complexité de la fonction mystere du programme suivant:
def mystere(n): m = 0 for i in range(n): for j in range(i): m += i+j return m print(mystere(100))
Solution
- Il y a avant tout 1 affectation dès le début (m=0);
- ensuite, au niveau de la boucle sur i, 3 opérations élémentaires;
- au niveau de la boucle sur j, il y a 3 opérations élémentaires répétées i fois;
- dans la boucle sur j, il y a 3 opérations élémentaires (1 affectation sur m, une somme sur m et une somme de i et j).
Ainsi, à part la première affectation, il y a 9 opérations élémentaires pour chaque valeur de j possible. Il y a:
- 1 valeur possible de j quand i=0 (j=0);
- 1 valeur possible de j quand i=1 (j=0);
- 2 valeurs possibles de j pour i=2 (j=0 et j=1):
- etc.
- n-1 valeurs possibles de j pour i=n-1.
La complexité est donc égale à:$$\begin{aligned} & 1+9\times(1+1+2+3+\cdots+(n-1))\\=\ & 10+\frac{9n(n-1)}{2}\\=\ & \frac{1}{2}(9n^2-9n+20).\end{aligned}$$
Ordre de grandeur de la complexité
Nous le voyons dans l’exemple précédent, la complexité peut s’exprimer par un polynôme. Dans un tel cas, si n est relativement grand, on pourra assimiler la complexité à son ordre de grandeur. Par exemple ici, la complexité de la dernière fonction est de l’ordre de \(n^2\). On écrira alors:$$T_n=O(n^2).$$On dira ici que la complexité est quadratique.
Pour l’exercice 2, \(T_n=5n+1=O(n)\). On dira que la complexité est linéaire.
Fonction récursive
Une des fonctions récursives la plus simple est celle qui permet de calculer n! (la factorielle de n), c’est-à-dire \(2\times3\times\cdots\times n\).
def factorielle(n): if n == 0: return 1 else: return n*factorielle(n-1)
Il y a un test pour commencer (sur n). Si le test est vrai, on s’arrête, sinon on effectue une opération (un produit) en faisant appel à la même fonction. Ainsi, si n est non nul, il y a 2 opérations élémentaires + le nombre d’opérations élémentaires de la fonction au rang n – 1.
Si \(T_n\) représente la complexité de la fonction factorielle(n) alors:$$T_n=T_{n-1}+2,\quad T_0=1.$$
La complexité est donc donnée par une suite arithmético-géométrique. C’est toujours le cas pour les fonctions récursives.
On peut alors montrer que \(T_n=2n+1=O(n).\)
Généralités
Si on considère une fonction toto(n) dans laquelle on fait appel à cette même fonction a fois et dans laquelle il y a b opérations élémentaires, si on note \(T_n\) sa complexité alors:$$T_n=aT_{n-1}+b.$$Ainsi, \(T_n=O(a^n)\). La complexité est alors exponentielle.
Exercice 4
Quelle est la complexité de la fonction suivante:
def fibo(n): if n<2: return n else: return fibo(n-1) + fibo(n-2)
Solution
Il y a 2 opérations élémentaires (un test sur n et une addition de fibo(n-1) et fibo(n-1)) donc:$$T_n=T_{n-1}+T_{n-2}+2,\quad T_0=T_1=1.$$ La complexité est donc une suite linéaire d’ordre 2 et d’équation caractéristique:$$r^2-r-1=0$$ de solutions \(r_1=\frac{1+\sqrt5}{2}\) et \(r_2=\frac{1-\sqrt5}{2}\). Comme \(|r_1|>|r_2|\), une propriété nous dit que la complexité de la fonction fibo(n) est:$$T_n=O(r_1^n).$$La complexité est alors exponentielle.
c dur frero
On ne va pas se mentir, les calculs de complexité ne sont pas simples en effet. Il faut avoir de solides bases en mathématiques :-). Et même si en théorie on parle de complexité en NSI au lycée, dans la pratique, il n’y a que très peu de calculs de complexité je pense. Avec du recul (car cela fait 2 ans que l’article a été écrit), je trouve que ces calculs sont presque impossibles à faire pour des élèves de 1ère (et même de Terminale).
Je prépare mon sujet de grand oral dessu c’est l’enfer :joy:
tro dur, j’arrive pas a expliquer a mes éleves
Le coût d’un programme (algorithme) est en effet au programme de NSI 1ère, mais c’est sans doute l’une des notions les plus compliquées car elle fait appel à des notions et une logique mathématique que n’ont pas les élèves. Il ne faut pas se prendre la tête. Il faut juste reprendre les propriétés importantes (du genre: les tris par insertion sont d’ordre O(n²), etc.).
Documentaire tres interessant. Tres utile pour mes cours de NSI !!
Continuez a nous fournir du bon contenu comme cela et vous allez percer !!
Bravo