La réponse de don.neufeld est très bonne, mais je l'expliquerais probablement en deux parties : tout d'abord, il existe une hiérarchie approximative de O() dans laquelle la plupart des algorithmes s'inscrivent. Ensuite, vous pouvez examiner chacun d'entre eux pour obtenir des esquisses de ce que vous pouvez faire. typique Les algorithmes de cette complexité temporelle le font.
Pour des raisons pratiques, les seuls O() qui semblent avoir de l'importance sont les suivants :
- O(1) "temps constant" - le temps requis est indépendant de la taille de l'entrée. En tant que catégorie approximative, j'inclurais ici des algorithmes tels que les recherches de hachage et les recherches d'union, même si aucun d'entre eux n'est réellement O(1).
- O(log(n)) "logarithmique" - il devient plus lent avec des entrées plus grandes, mais une fois que votre entrée est assez grande, il ne changera pas assez pour s'en inquiéter. Si votre temps d'exécution est correct avec des données de taille raisonnable, vous pouvez le submerger avec autant de données supplémentaires que vous le souhaitez et il sera toujours correct.
- O(n) "linéaire" - plus l'entrée est importante, plus le temps est long, dans un compromis équilibré. Trois fois la taille de l'entrée prendront environ trois fois plus de temps.
- O(n log(n)) "mieux que quadratique" - l'augmentation de la taille de l'entrée fait mal, mais c'est encore gérable. L'algorithme est probablement décent, c'est juste que le problème sous-jacent est plus difficile (les décisions sont moins localisées par rapport aux données d'entrée) que les problèmes qui peuvent être résolus en temps linéaire. Si la taille de vos données d'entrée augmente, ne supposez pas que vous pourriez nécessairement gérer une taille deux fois plus grande sans modifier votre architecture (par exemple en déplaçant les choses vers des calculs par lots la nuit, ou en ne faisant pas les choses par image). Il n'y a pas de problème si la taille de l'entrée augmente un peu, cependant ; faites juste attention aux multiples.
- O(n^2) "quadratique" - cela ne fonctionnera vraiment que jusqu'à une certaine taille de votre entrée, alors faites attention à la taille qu'elle pourrait atteindre. De même, votre algorithme peut être nul - réfléchissez bien pour voir s'il existe un algorithme O(n log(n)) qui vous donnerait ce dont vous avez besoin. Une fois que vous êtes ici, soyez très reconnaissant pour l'incroyable matériel dont nous avons été dotés. Il n'y a pas si longtemps, ce que vous essayez de faire aurait été impossible à toutes fins pratiques.
- O(n^3) "cubique" - pas qualitativement si différent de O(n^2). Les mêmes commentaires s'appliquent, mais en plus grand nombre. Il y a une bonne chance qu'un algorithme plus intelligent puisse réduire ce temps à quelque chose de plus petit, par exemple O(n^2 log(n)) ou O(n^2.8...), mais là encore, il y a une bonne chance que cela ne vaille pas la peine. (Vous êtes déjà limité dans votre taille d'entrée pratique, donc les facteurs constants qui peuvent être nécessaires pour les algorithmes plus intelligents vont probablement écraser leurs avantages pour les cas pratiques. De plus, la réflexion est lente ; laisser l'ordinateur la mastiquer peut vous faire gagner du temps dans l'ensemble).
- O(2^n) "exponentiel" - soit le problème est fondamentalement difficile à calculer, soit vous êtes un idiot. Ces problèmes ont une saveur reconnaissable. La taille de vos données d'entrée est plafonnée à une limite stricte assez spécifique. Vous saurez rapidement si vous entrez dans cette limite.
Et c'est tout. Il existe de nombreuses autres possibilités qui se situent entre celles-ci (ou qui sont supérieures à O(2^n)), mais elles ne se produisent pas souvent dans la pratique et elles ne sont pas qualitativement très différentes de l'une d'entre elles. Les algorithmes cubiques sont déjà un peu exagérés ; je ne les ai inclus que parce que je les ai rencontrés assez souvent pour qu'ils méritent d'être mentionnés (par exemple, la multiplication de la matrice).
Que se passe-t-il réellement pour ces classes d'algorithmes ? Je pense que vous avez bien commencé, bien qu'il existe de nombreux exemples qui ne correspondent pas à ces caractérisations. Mais pour ce qui est de ce qui précède, je dirais que cela se passe généralement comme suit :
- O(1) - vous ne regardez au maximum qu'un morceau de taille fixe de vos données d'entrée, et peut-être aucun. Exemple : le maximum d'une liste triée.
- Ou la taille de votre entrée est limitée. Exemple : addition de deux nombres. (Notez que l'addition de N nombres est un temps linéaire).
- O(log n) - chaque élément de votre entrée vous en dit suffisamment pour ignorer une grande partie du reste de l'entrée. Exemple : lorsque vous regardez un élément de tableau dans une recherche binaire, sa valeur vous indique que vous pouvez ignorer la "moitié" de votre tableau sans en regarder aucun. De même, l'élément que vous regardez vous donne un résumé suffisant d'une fraction de l'entrée restante pour que vous n'ayez pas besoin de le regarder.
- Les moitiés n'ont rien de spécial, cependant - si vous ne pouvez ignorer que 10 % de votre entrée à chaque étape, c'est toujours logarithmique.
- O(n) - vous effectuez une quantité fixe de travail par élément d'entrée. (Mais voir ci-dessous).
- O(n log(n)) - il existe quelques variantes.
- Vous pouvez diviser l'entrée en deux piles (en un temps linéaire maximum), résoudre le problème indépendamment sur chaque pile, puis combiner les deux piles pour former la solution finale. L'indépendance des deux piles est essentielle. Exemple : mergesort récursif classique.
- Chaque passage en temps linéaire sur les données vous amène à mi-chemin de votre solution. Exemple : quicksort si vous pensez en termes de distance maximale de chaque élément à sa position finale triée à chaque étape de partitionnement (et oui, je sais que c'est en fait O(n^2) à cause des choix de pivot dégénérés. Mais pratiquement parlant, cela tombe dans ma catégorie O(n log(n))).
- O(n^2) - vous devez examiner chaque paire d'éléments d'entrée.
- Ou vous ne le faites pas, mais vous pensez le faire, et vous utilisez le mauvais algorithme.
- O(n^3) - hum... Je n'ai pas une caractérisation rapide de ceux-ci. C'est probablement l'un de :
- Vous multipliez des matrices
- Vous regardez chaque paire d'entrées mais l'opération que vous faites nécessite de regarder à nouveau toutes les entrées.
- la structure graphique entière de votre entrée est pertinente
- O(2^n) - vous devez considérer chaque sous-ensemble possible de vos entrées.
Aucune d'entre elles n'est rigoureuse. Surtout pas les algorithmes en temps linéaire (O(n)) : Je pourrais trouver un certain nombre d'exemples où il faut examiner toutes les entrées, puis la moitié d'entre elles, puis la moitié de celles-ci, etc. Ou l'inverse : on réunit des paires d'entrées, puis on récure sur la sortie. Cela ne correspond pas à la description ci-dessus, puisque vous ne regardez pas chaque entrée une seule fois, mais le temps de calcul reste linéaire. Pourtant, dans 99,2% des cas, le temps linéaire signifie que l'on ne regarde chaque entrée qu'une seule fois.
12 votes
Le titre ne serait-il pas mieux formulé comme suit : "Quelle est l'explication simple du Big-O ?", etc.
62 votes
La réponse à cette question a été très bien faite, je ne vais donc pas m'attarder sur ce point. Je voulais juste dire que j'adore le titre de votre question ! Utiliser le concept selon lequel on ne comprend pas vraiment quelque chose avant de pouvoir l'expliquer à un enfant de 8 ans est une excellente façon de formuler la question.
5 votes
Quand j'ai lu ça, j'ai cru que tu parlais de.. : fr.wikipedia.org/wiki/The_Big_O
8 votes
@TMarshall Il peut s'agir d'un titre intéressant, mais cela ne signifie pas qu'il est nécessairement consultable.
6 votes
@bradtgmurray : ou classé PG...
3 votes
@RobertSCiaccio Pouvez-vous préciser ? :P
2 votes
@muntoo : Je ne pense pas que je veuille y aller :P
6 votes
Faut-il fumer du crack pour écrire un O(x !)? Légendaire !
0 votes
@Alex En retard à la fête : Au nom de Dieu, vous n'êtes pas coupable.
0 votes
Belle feuille de triche ici aussi : bigocheatsheet.com
0 votes
Je vais demander à Doug Stamper de s'en occuper.
0 votes
Pensez à laisser vos enfants de huit ans jouer avec des jouets et peut-être même avec des jeux d'ordinateur.