Big O: limite supérieure
"Big O" ( ) est de loin le plus commun. Lorsque vous analysez la complexité d'un algorithme, il est généralement essentiel de définir une limite supérieure pour la rapidité avec laquelle le temps d'exécution¹ augmente lorsque la taille de l'entrée augmente. En gros, nous voulons savoir que l’exécution de l’algorithme ne prendra pas «trop longtemps». Nous ne pouvons pas exprimer cela en unités de temps réelles (secondes), car cela dépend de l'implémentation précise (la manière dont le programme est écrit, la qualité du compilateur, la rapidité du processeur de la machine,…). Nous évaluons donc ce qui ne dépend pas de ces détails, à savoir le temps nécessaire pour exécuter l'algorithme lorsque nous lui fournissons une entrée plus importante. Et nous nous soucions principalement lorsque nous pouvons être sûrs que le programme est terminé, nous voulons donc généralement savoir que cela prendra tel ou tel temps ou moins.O
Dire qu’un algorithme a un temps d’exécution de pour une taille d’entrée signifie qu’il existe une constante telle que l’algorithme se termine en au plus étapes, c’est-à-dire le temps d’exécution de l’algorithme croît au plus vite que (jusqu’à un facteur de mise à l’échelle). Notant le temps d'exécution de l'algorithme pour la taille d'entrée , signifie informellement que atteint un facteur d'échelle.O(f(n))nKKf(n)fT(n)nO(n)T(n)≤f(n)
Borne inférieure
Parfois, il est utile d’avoir plus d’informations qu’une limite supérieure. est l'inverse de : il exprime qu'une fonction grandit au moins aussi vite qu'une autre. signifie que pour une constante , ou pour le dire de manière informelle, up à un facteur d'échelle.ΩOT(n)=Ω(g(n))T(N)≥K′g(n)K′T(n)≥g(n)
Lorsque le temps d'exécution de l'algorithme peut être déterminé avec précision, combine et : il indique que le taux de croissance d'une fonction est connu, jusqu'à un facteur de mise à l'échelle. signifie que pour certaines constantes et . De manière informelle, jusqu’à un facteur d’échelle.ΘOΩT(n)=Θ(h(n))Kh(n)≥T(n)≥K′h(n)KK′T(n)≈h(n)
Autres considérations
Le «petit» et le sont beaucoup moins utilisés dans l'analyse de la complexité. Le petit est plus fort que le grand ; où indique une croissance qui n'est pas plus rapide, indique que la croissance est strictement plus lente. Inversement, indique une croissance strictement plus rapide.oωoOOoω
J'ai été légèrement informel dans la discussion ci-dessus. Wikipedia a des définitions formelles et une approche plus mathématique.
N'oubliez pas que l'utilisation du signe égal dans et similaires est impropre. Strictement parlant, est un ensemble de fonctions de la variable , et nous devrions écrire .T(n)=O(f(n))O(f(n))nT∈O(f)
Exemple: des algorithmes de tri
Comme c'est plutôt sec, laissez-moi vous donner un exemple. La plupart des algorithmes de tri ont une durée d'exécution quadratique dans le pire des cas, c'est-à-dire que pour une entrée de taille , la durée d'exécution de l'algorithme est de . Par exemple, le tri par sélection a un temps d’exécution , car la sélection du élément nécessite comparaisons, pour un total de comparaisons. En fait, le nombre de comparaisons est toujours exactement , qui croît en tant que . Nous pouvons donc être plus précis sur la complexité temporelle du tri de la sélection: c’est .nO(n2)O(n2)kn−kn(n−1)/2n(n−1)/2n2Θ(n2)
Maintenant, prenez le tri par fusion . Le type de fusion est également quadratique ( ). C'est vrai, mais pas très précis. Le tri par fusion a en fait une durée d'exécution de dans le pire des cas. Comme le tri par sélection, le flux de travail du tri par fusion est essentiellement indépendant de la forme de l'entrée et son temps d'exécution est toujours jusqu'à un facteur multiplicatif constant, c'est-à-dire .O(n2)O(nlg(n))nlg(n)Θ(nlg(n))
Ensuite, considérez quicksort . Quicksort est plus complexe. C'est certainement . De plus, le pire cas de tri rapide est quadratique: le pire est . Cependant, le meilleur cas de tri rapide (lorsque l'entrée est déjà triée) est linéaire: le meilleur que l'on puisse dire pour une limite inférieure du tri rapide en général est . Je ne répéterai pas la preuve ici, mais la complexité moyenne de quicksort (la moyenne prise sur toutes les permutations possibles de l’entrée) est .O(n2)Θ(n2)Ω(n)Θ(nlg(n))
Il existe des résultats généraux sur la complexité des algorithmes de tri dans les paramètres courants. Supposons qu'un algorithme de tri ne puisse comparer que deux éléments à la fois, avec un résultat oui ou non ( ou ). Ensuite, il est évident que la durée d'exécution de tout algorithme de tri est toujours (où est le nombre d'éléments à trier), car l'algorithme doit comparer chaque élément au moins une fois pour savoir où il se placera. Cette limite inférieure peut être satisfaite, par exemple, si l'entrée est déjà triée et que l'algorithme compare simplement chaque élément au suivant et les maintient dans l'ordre (c'est-à comparaisons). Ce qui est moins évident, c’est que la durée maximale est nécessairementx≤yx>yΩ(n)nn−1Ω(nlg(n)) . Il est possible que l'algorithme fasse parfois moins de comparaisons, mais il doit exister une constante telle que pour toute taille d'entrée , il existe au moins une entrée pour laquelle l'algorithme fait plus que comparaisons. L'idée de la preuve est de construire l'arbre de décision de l'algorithme, c'est-à-dire de suivre les décisions que l'algorithme prend à partir du résultat de chaque comparaison. Comme chaque comparaison renvoie un résultat oui ou non, l’arbre de décision est un arbre binaire. Il y en apermutations possibles de l’entrée, et l’algorithme doit faire la distinction entre elles, la taille de l’arbre de décision est doncKnKnlg(n)n!n!. Comme l’arbre est un arbre binaire, il faut une profondeur de pour s’adapter à tous ces nœuds. La profondeur correspond au nombre maximal de décisions prises par l'algorithme. Son exécution implique donc au moins ce nombre de comparaisons: le temps d'exécution maximal est .Θ(lg(n!))=Θ(nlg(n))Ω(nlg(n))
¹ Ou d’autres ressources, telles que l’espace mémoire. Dans cette réponse, je ne considère que le temps en cours.