IV. Complexité d'un algorithme¶
Pour une entrée quelconque,
Combien d'opérations mon algorithme va-t-il réaliser ? Complexité temporelle
Combien d'espace mémoire mon algorithme va-t-il utiliser ? Complexité spatiale
NB : ceci dépend :
de l'entrée elle-même,
de la machine sur laquelle s'exécute l'algorithme : représentation des données manipulées, implémentation des opérations, vitesse de calcul ...
Il y a beaucoup trop de paramètres à prendre en compte ...
on doit faire un certain nombre d'approximations ...
Approximations¶
- On ne tient pas compte de toutes les entrées possibles, mais souvent de leur "taille"
On ne tient pas compte de la machine sur laquelle s'exécute le programme
- Pour doubler la vitesse d'exécution, il suffit d'utiliser un machine deux fois plus rapide ... ce qui est sans lien avec l'efficacité intrinsèque de l'algorithme (ce qui nous intéresse ici).
On considère que toutes les opérations de base ont un coût unitaire :
addition, multiplication ... de deux entiers ou flottants
comparaison de deux entiers ou deux flottants
affectation, accès dans une liste ou chaîne de caractères
Attention : un appel à une fonction n'a pas forcément un coût unitaire :
min(L)
: valeur minimale dans une liste : Quel coût ?
min(L)
: valeur minimale dans une liste :- Il faut bien parcourir tous les éléments de la liste pour trouver la plus petite valeur donc ...
- coût proportionnel à la longueur de la liste
L.copy()
copie de liste : Quel coût ?
L.copy()
copie de liste :- Il faut obtenir une nouvelle zone mémoire, et y copier les éléments de la liste donc ...
- coût proportionnel à la longueur de la liste
L1 == L2
égalité de deux listes : Quel coût ?
L1 == L2
égalité de deux listes :coût constant si les deux listes sont de longueur différentes
coût constant si les deux listes diffèrent sur leur premier élément
coût proportionnel à la longueur des deux listes si les listes deux listes sont égales (cas le plus coûteux : pire des cas)
L1+L2
: concaténation de deux listes : Quel coût ?
L1+L2
: concaténation de deux listes :- on crée une nouvelle liste, et on y copie les éléments des deux listes (cf PythonTutor), donc ...
- coût proportionnel à la longueur des deux listes
Cours - définitions¶
Exemple : Recherche séquentielle dans une liste de longueur n
¶
def recherche(L:[int],v:int) -> bool:
""" renvoie True si v est dans la liste L, False sinon """
for i in range(len(L)):
if L[i] == v:
return True
return False
Dans le meilleur des cas ? (en fonction de $n$) :
- v est le premier élément de la liste : 1 comparaison (la liste vide est un cas particulier : n=0)
Dans le pire des cas ? (en fonction de $n$) :
- v n'est pas dans la liste : n comparaisons
En moyenne ? (en fonction de $n$ ?):
- n/2 comparaisons ??? Pas si simple ... cf exercices
Cours - complexité asymptotique¶
On privilégie bien sûr les algorithmes de faible complexité lorsque c'est possible.
Passage à l'échelle : lorsqu'on double la valeur de $n$, pour un algorithme en :
- $\mathcal{O}(1)$ : le coût ...
- $\mathcal{O}(\log_2(n))$ : le coût ...
- $\mathcal{O}(\sqrt{n})$ : le coût ...
- $\mathcal{O}(n)$ : le coût ...
- $\mathcal{O}(n^2)$ : le coût ...
- $\mathcal{O}(n^k)$ : le coût ...
- $\mathcal{O}(2^n)$ : le coût ...
Passage à l'échelle : lorsqu'on double la valeur de $n$, pour un algorithme en :
- $\mathcal{O}(1)$ : le coût ne varie pas (car indépendant de $n$)
- $\mathcal{O}(\log_2(n))$ : le coût est augmenté d'une constante (car $\log_2(2n)$ = $1+\log_2(n)$)
- $\mathcal{O}(\sqrt{n})$ : le coût est multiplié par $\sqrt{2}$ (car $\sqrt{2n}$ = $\sqrt{2}\times \sqrt{n}$)
- $\mathcal{O}(n)$ : le coût est multiplié par 2 (car $2n = 2 \times n$)
- $\mathcal{O}(n^2)$ : le coût est multiplié par 4 (car $(2n)^2 = 4 \times n^2$)
- $\mathcal{O}(n^k)$ : le coût est multiplié par $2^k$ (car $(2n)^k = 2^k \times n^k$)
- $\mathcal{O}(2^n)$ : le coût est mis au carré (car $2^{2n} = (2^n)^2$)
Complexité : quelle utilité ?¶
Estimation du temps d'exécution
Supposons que l'on dispose d'une machine qui réalise $10^8$ opérations par seconde.
Durée d'exécution en fonction de $n$ et de la complexité de l'algorithme
Complexité | n=10 | n=20 | n=50 | n=1000 | n=1000000 |
---|---|---|---|---|---|
$\mathcal{O}(1)$ | 10ns | 10ns | 10ns | 10ns | 10ns |
$\mathcal{O}(\log(n))$ | 30ns | 40ns | 60ns | 100ns | 200ns |
$\mathcal{O}(n)$ | 100ns | 200ns | 500ns | 10µs | 10ms |
$\mathcal{O}(n\log n)$ | 300ns | 1µs | 3µs | 1ms | 200ms |
$\mathcal{O}(n^2)$ | 1µs | 4µs | 25µs | 10ms | 3h |
$\mathcal{O}(2^n)$ | 10µs | 10ms | 130j | $10^{14}$ ans | ... |
$\mathcal{O}(n!)$ | 36ms | 761ans | $10^{49}$ ans | ... | .. |
NB : âge de l'univers : $10^{10}$ ans
Exemple : problème du "voyageur de commerce"¶
Un étudiant souhaite faire le tour des $n=27$ capitales européennes. Soucieux de minimiser son bilan carbone, il veut que son trajet soit le plus court possible.
Algorithme "naïf" :¶
"on énumère les 27! permutations possibles, en conservant celle qui donne le plus court chemin" (complexité en $\mathcal{O}(n!)$)
En traitant 10⁸ permutations par seconde (machine actuelle) ...
il faudra 3000 milliards d'années pour obtenir le résultats...
Pour n
grand, seuls les algorithmes de complexité polynomiale (ou inférieure) sont utilisables en pratique¶
Cours - fin¶
Quelques algorithmes classiques et complexité associées¶
Algorithmes en $\mathcal{O}(1)$ : nombre borné d'opérations
- échange de contenu de deux variables
Algorithmes en $\mathcal{O}(n)$ : sur une séquence de longueur $n$
Somme des valeurs
Compter le nombre d'occurrences
Recherche séquentielle d'un élément
Recherche du minimum et toutes les variantes
- recherche du maximum, du minimum et du maximum, des positions des maximums, des 2 plus grandes valeurs ...
Autres algorithmes classiques :
Algorithmes de tri (cf cours à venir) : $\mathcal{O}(n\log_2(n))$ ou $\mathcal{O}(n^2)$
Recherche dichotomique dans une liste triée (cf cours à venir) : $\mathcal{O}(\log_2(n))$
La complexité spatiale¶
A priori ... on dispose de beaucoup de "mémoire"
Ordres de grandeur :
- 1 caractère : 1 octet (8bits)
- 1 page : 3000 caractères (3ko)
- 1 livre : 300 pages (900ko $\simeq$ 1Mo)
- Une belle bibliothèque personnelle : 1000 livres (1Go)
- la Bibliothèque Nationale : 10 millions de livres (10 To)
Attention toutefois ... :
allouer/libérer de la mémoire prend du temps
la mémoire est organisée de façon hiérarchique
- plus on doit aller chercher "profond" dans la mémoire, plus cela prend de temps ...