Objectifs de cette leçon
- Expliquer ce que mesure la complexité
- Reconnaître O(1), O(log n), O(n), O(n²)
- Comparer deux algorithmes sans les exécuter
Le problème : « ça marche chez moi »
Un programme qui répond en une seconde avec 100 données peut mettre des heures avec un million. Mesurer le temps sur ta machine ne suffit pas : ce temps dépend de l’ordinateur. La complexité algorithmique mesure autre chose : comment le nombre d’opérations grandit quand la taille n des données grandit.
Les grandes familles
- O(1) — constant : lire
liste[5]. Le temps ne dépend pas de n. - O(log n) — logarithmique : chercher dans une liste triée en la coupant en deux à chaque étape. 1 million d’éléments ≈ 20 étapes.
- O(n) — linéaire : parcourir toute la liste une fois. Dix fois plus de données, dix fois plus de temps.
- O(n log n) — les bons tris (tri fusion, tri rapide en moyenne).
- O(n²) — quadratique : deux boucles imbriquées (comparer chaque élément avec tous les autres). Dix fois plus de données, cent fois plus de temps.
- O(2ⁿ) — exponentiel : inutilisable dès que n dépasse quelques dizaines.
Compter les opérations sur du vrai code
def contient(liste, cible): # O(n)
for x in liste:
if x == cible:
return True
return False
def a_un_doublon(liste): # O(n²)
for i in range(len(liste)):
for j in range(i + 1, len(liste)):
if liste[i] == liste[j]:
return True
return False
def a_un_doublon_rapide(liste): # O(n) grâce à un ensemble
vus = set()
for x in liste:
if x in vus:
return True
vus.add(x)
return False
import time
data = list(range(3000))
for f in (a_un_doublon, a_un_doublon_rapide):
t = time.time()
f(data)
print(f.__name__, round((time.time() - t) * 1000, 1), "ms")
Les deux fonctions font le même travail. La seconde échange un peu de mémoire (l’ensemble vus) contre un gain de temps énorme : c’est le compromis temps / mémoire, omniprésent en informatique.
Comment lire une complexité
- Une boucle sur n éléments → O(n).
- Deux boucles imbriquées sur n → O(n²).
- Deux boucles l’une après l’autre → O(n) + O(n) = O(n) : on ignore les constantes.
- Un problème coupé en deux à chaque étape → O(log n).
Erreurs courantes
- Croire que O(n) est toujours plus rapide que O(n²) : sur de très petites données, une approche « mauvaise » peut gagner. La complexité parle des grandes tailles.
- Oublier le coût caché d’une fonction :
x in listeest lui-même O(n) ! - Optimiser trop tôt : d’abord un code correct et lisible, puis on mesure, puis on optimise là où c’est nécessaire.