COMPLEXITÉ ALGORITHMIQUES
Introduction
Étant donné deux algorithmes, il est nécessaire d’évaluer
l’efficacité de chacune d’entre eux et de savoir le meilleur d’entre
eux, pour ce faire, nous allons établies une métrique qui fait
abstraction de tout ce qui est subjectif à savoir le langage de
programmation, la qualité du programmeur, la qualité du
programmeur, la puissance de calcul du matériel utilisé etc. ….
Cette grandeur est quantifiée en fonction de la taille de données
n en entrée.
Cette mesure est appelée complexité de l’algorithme.
Il existe deux types de complexités algorithmiques
- La complexité temporelle : la quantité nécessaire à son
exécution complète. Le temps d’exécution est proportionnel aux
nombres d’opérateurs fondamentaux effectués par l’algorithme
On entend les calculs arithmétiques, les comparaisons, les
affectations. Chaque opération fondamentale de l’algorithme
prend un temps unitaire
Explication : La complexité temporelle d'un algorithme
mesure le temps nécessaire à son exécution en fonction de la
taille de l'entrée. Autrement dit, elle évalue combien de temps
l'algorithme mettra pour traiter des données de plus en plus
grandes. Ce temps est proportionnel au nombre d'opérations
fondamentales effectuées pendant l'exécution de l'algorithme.
Ces opérations fondamentales incluent :
• Les calculs arithmétiques (comme les additions,
soustractions, multiplications, etc.),
• Les comparaisons (par exemple, comparer deux valeurs
pour voir laquelle est la plus grande),
• Les affectations (attribuer une valeur à une variable).
Chaque opération fondamentale est supposée prendre un temps
unitaire, ce qui signifie que, dans le cadre de l'analyse de la
complexité, on considère qu'elle prend toujours le même temps,
quelle que soit la machine ou les conditions d'exécution.
Exemple :
Imaginons un algorithme simple qui prend une liste de n
éléments et qui fait la somme de ces éléments. Si chaque somme
ou opération prend un temps unitaire, l'algorithme doit effectuer
n additions (car il y a n éléments). Donc, la complexité temporelle
est proportionnelle à n, ce qui se note O(n). Cela signifie que le
temps d'exécution de l'algorithme croît de manière linéaire avec
la taille de l'entrée.
Notation Big-O :
Pour décrire la complexité temporelle, on utilise souvent la
notation Big-O. Elle donne une estimation asymptotique du
nombre d'opérations en fonction de la taille de l'entrée, en ne
tenant compte que des termes qui dominent pour les grandes
valeurs de n. Voici quelques exemples courants :
• O(1) : temps constant, peu importe la taille de l'entrée.
• O(n) : temps linéaire, proportionnel à la taille de l'entrée.
• O(n2): temps quadratique, le temps croît
proportionnellement au carré de la taille de l'entrée.
• O(log(n): temps logarithmique, où le temps croît beaucoup
plus lentement que la taille de l'entrée.
En résumé, la complexité temporelle d'un algorithme représente
le nombre d'opérations que l'algorithme doit effectuer et, par
conséquent, comment le temps d'exécution augmente lorsque la
taille des données d'entrée augmente.
- La complexité spatiale : la quantité de mémoire nécessaire
à son exécution complètes.
Il existe 3 nuances de complexités :
- La complexité dans le meilleur des cas : représente le plus petit
nombre d’opérations à exécuter par l’algorithme sur un jeu de
données
-La complexité dans le pire des cas : est le plus grand nombre
d’opérations sur un jeu de données et du nombre d’opérations
effectuées par l’algorithme
-La complexité dans le moyen des cas
Toutes les opérations élémentaires ont un cout et de O(I)
le calcul de la complexité d’une F = Somme des complexités de
toutes les instructions qui composent une séquence constituée
d’opérations élémentaire de o(I)
A connaitre :
1. Algorithmes de tri (suite) :
• Tri par insertion (Insertion Sort) :
o Complexité : O(n2)dans le pire des cas, mais O(n) dans
le meilleur des cas (si la liste est déjà triée).
o Insère chaque élément à sa position correcte en
comparant les éléments précédents.
• Tri rapide (Quick Sort) :
o Complexité : O(n log n) en moyenne, O(n2) dans le pire
des cas.
o Utilise la stratégie "diviser pour régner" en choisissant
un pivot et en triant les sous-listes.
• Tri fusion (Merge Sort) :
o Complexité : O(n log n).
o Divise la liste en deux, trie chaque moitié, puis
fusionne les sous-listes triées.
• Tri à bulles (Bubble Sort) :
o Complexité : O(n2).
o Compare des paires d'éléments adjacents et les
échange s'ils sont dans le mauvais ordre.
2. Algorithmes de recherche :
• Recherche linéaire :
o Complexité : O(n).
o Parcourt tous les éléments d'une liste jusqu'à trouver
l'élément recherché.
• Recherche binaire :
o Complexité : O(log n).
o Fonctionne sur une liste triée. Divise la liste en deux à
chaque étape pour rechercher l'élément.
3. Algorithmes de graphes :
• Algorithme de Dijkstra (pour les plus courts chemins dans
un graphe pondéré) :
o Complexité : O((V+E) log V) où V est le nombre de
sommets et E le nombre d'arêtes.
• Algorithme de Bellman-Ford (pour les plus courts
chemins, avec des poids négatifs possibles) :
o Complexité: O(VE).
• Algorithme de Prim (pour les arbres couvrants minimaux) :
o Complexité : O((V+E)log V).
• Parcours en profondeur (DFS) :
o Complexité : O(V+E).
• Parcours en largeur (BFS) :
o Complexité : O(V+E).
4. Algorithmes de programmation dynamique :
• Problème du sac à dos (Knapsack Problème) :
o Complexité : O(nW)où n est le nombre d'objets et W la
capacité du sac.
o Utilise une approche récursive avec mémorisation.
• Problème de la plus longue sous-séquence commune
(LCS) :
o Complexité : O(n.m) où n et m sont les longueurs des
deux chaînes comparées.
• Problème de la plus courte distance de Levenshtein
(pour la comparaison de chaînes) :
o Complexité : O(n×m) .
5. Algorithmes de recherche d'éléments :
• Recherche dans une table de hachage (Hashting) :
o Complexité : O(1) pour les recherches dans le meilleur
des cas, O(n) dans le pire des cas avec collisions.
• Arbre binaire de recherche (Binary Search Tree) :
o Complexité : O(logn)O(\log n)O dans le meilleur cas
(arbre équilibré), O(n) dans le pire cas (arbre
dégénéré).
• Arbre rouge-noir :
o Complexité : O(logn)O(\log n)O(logn) pour les
opérations de recherche, insertion, et suppression. Il
s'agit d'un arbre binaire équilibré.
6. Algorithmes de base en géométrie :
• Convex Hull (Enveloppe convexe) :
o Complexité : O(nlogn)O(n \log n)O(nlogn), utilise des
méthodes comme l'algorithme de Graham ou de
Jarvis pour trouver l'enveloppe convexe d'un
ensemble de points.
• Triangulation de Delaunay :
o Complexité : O(nlogn)O(n \log n)O(nlogn).
Conclusion :
Ces algorithmes te donneront une solide compréhension des
différents types de complexités (linéaire, quadratique,
logarithmique, exponentielle, etc.). En comprenant comment ils
fonctionnent et en étudiant leur complexité temporelle et
spatiale, tu pourras développer une meilleure intuition sur
l'efficacité des algorithmes et leur comportement pour des
ensembles de données de différentes tailles.