Je préférerais le moins possible de définitions formelles et de mathématiques simples.
Remarque rapide, cela est presque certainement source de confusion notation Big O (qui est une limite supérieure) avec la notation Thêta "Θ" (qui est une borne à deux faces). D'après mon expérience, cela est en fait typique des discussions dans des contextes non académiques. Toutes mes excuses pour toute confusion causée.
La complexité de Big O peut être visualisée avec ce graphique:
La définition la plus simple que je puisse donner pour la notation Big-O est la suivante:
La notation Big-O est une représentation relative de la complexité d'un algorithme.
Il y a quelques mots importants et délibérément choisis dans cette phrase:
- relatif: vous ne pouvez comparer des pommes avec des pommes. Vous ne pouvez pas comparer un algorithme de multiplication arithmétique à un algorithme qui trie une liste d'entiers. Mais une comparaison de deux algorithmes pour effectuer des opérations arithmétiques (une multiplication, une addition) vous dira quelque chose de significatif;
- representation: Big-O (dans sa forme la plus simple) réduit la comparaison entre algorithmes à une seule variable. Cette variable est choisie en fonction d'observations ou d'hypothèses. Par exemple, les algorithmes de tri sont généralement comparés en fonction d'opérations de comparaison (comparaison de deux nœuds pour déterminer leur ordre relatif). Cela suppose que la comparaison coûte cher. Mais que se passe-t-il si la comparaison est bon marché mais que l’échange coûte cher? Cela change la comparaison; et
- complexité: s'il me faut une seconde pour trier 10 000 éléments, combien de temps me faudra-t-il pour trier un million? La complexité dans ce cas est une mesure relative par rapport à autre chose.
Revenez et relisez ce qui précède lorsque vous avez lu le reste.
Le meilleur exemple de Big-O auquel je puisse penser est celui de l’arithmétique. Prenez deux chiffres (123456 et 789012). Les opérations arithmétiques de base que nous avons apprises à l'école sont les suivantes:
- une addition;
- soustraction;
- multiplication; et
- division.
Chacun de ceux-ci est une opération ou un problème. Une méthode pour les résoudre s'appelle un algorithme.
L'addition est la plus simple. Vous alignez les nombres (à droite) et ajoutez les chiffres dans une colonne en écrivant le dernier numéro de cet ajout dans le résultat. La partie "dizaines" de ce nombre est reportée à la colonne suivante.
Supposons que l'ajout de ces nombres est l'opération la plus chère de cet algorithme. Il va de soi que pour additionner ces deux nombres, nous devons additionner 6 chiffres (et éventuellement porter un 7). Si nous additionnons deux nombres de 100 chiffres, nous devrons faire 100 additions. Si nous ajoutons deux 10 000 chiffres, nous devons faire 10 000 ajouts.
Voir le motif? Le complexité (étant le nombre d'opérations) est directement proportionnel au nombre de chiffres n dans le nombre le plus élevé. Nous appelons cela O (n) ou complexité linéaire.
La soustraction est similaire (sauf que vous devrez peut-être emprunter au lieu d’emporter).
La multiplication est différente. Vous alignez les chiffres, prenez le premier chiffre du nombre le plus bas et multipliez-le tour à tour contre chaque chiffre du nombre le plus élevé, et ainsi de suite. Donc, pour multiplier nos deux nombres à 6 chiffres, nous devons faire 36 multiplications. Nous aurons peut-être besoin d'ajouter jusqu'à 10 ou 11 colonnes pour obtenir également le résultat final.
Si nous avons deux nombres de 100 chiffres, nous devons faire 10 000 multiplications et 200 additions. Pour deux numéros à un million de chiffres, nous devons faire un billion de dollars (1012) multiplications et deux millions ajoute.
Comme l'algorithme évolue avec n - au carré , c'est O (n2) ou complexité quadratique. C'est un bon moment pour introduire un autre concept important:
Nous ne nous intéressons qu'à la portion de complexité la plus significative.
Les astucieux ont peut-être compris que nous pouvions exprimer le nombre d'opérations par: n2 + 2n. Mais comme vous l'avez vu dans notre exemple avec deux chiffres d'un million de chiffres chacun, le second terme (2n) devient insignifiant (représentant 0,0002% du total des opérations à ce stade).
On peut remarquer que nous avons supposé ici le pire scénario. En multipliant les nombres à 6 chiffres si l’un est à 4 chiffres et l’autre à 6 chiffres, nous n’avons que 24 multiplications. Néanmoins, nous calculons le pire scénario pour ce "n", c’est-à-dire lorsque les deux sont des nombres à 6 chiffres. La notation Big-O concerne donc le pire scénario d’un algorithme
Le prochain meilleur exemple auquel je puisse penser est l’annuaire téléphonique, normalement appelé les pages blanches ou similaire, mais il varie d’un pays à l’autre. Mais je parle de celui qui répertorie les gens par nom de famille, puis par leurs initiales ou par leur prénom, éventuellement leur adresse et leurs numéros de téléphone.
Maintenant, si vous demandiez à un ordinateur de rechercher le numéro de téléphone de "John Smith" dans un annuaire contenant 1 000 000 de noms, que feriez-vous? En ignorant le fait que vous pouviez deviner jusqu'où les S commençaient (supposons que vous ne le pouvez pas), que feriez-vous?
Une implémentation typique pourrait être d’ouvrir au milieu, prendre le 500.000th et comparez-le à "Smith". S'il s'agit de "Smith, John", nous avons vraiment de la chance. Beaucoup plus probable est que "John Smith" sera avant ou après ce nom. Si c'est après, nous divisons ensuite la dernière moitié de l'annuaire et répétons l'opération. Si c'est avant, nous divisons la première moitié de l'annuaire téléphonique et répétons l'opération. Etc.
Ceci s'appelle une recherche binaire et est utilisé tous les jours dans la programmation, que vous le réalisiez ou non.
Donc, si vous voulez trouver un nom dans un annuaire téléphonique comptant un million de noms, vous pouvez trouver n'importe quel nom en procédant ainsi au maximum 20 fois. En comparant les algorithmes de recherche, nous décidons que cette comparaison est notre "n".
- Pour un répertoire de 3 noms, il faut 2 comparaisons (au maximum).
- Pour 7 cela prend au plus 3.
- Pour 15 cela prend 4.
- …
- Pour 1 000 000, il en faut 20.
C'est étonnamment bon, n'est-ce pas?
En termes Big-O, il s'agit de O (log n) ou complexité logarithmique. Le logarithme en question pourrait être ln (base e), logdix, connectez-vous2 ou une autre base. Peu importe que ce soit toujours O (log n), tout comme O (2n2) et O (100n2) sont toujours tous les deux O (n2).
Cela vaut la peine d'expliquer que Big O peut être utilisé pour déterminer trois cas avec un algorithme:
- Meilleur scénario: Dans la recherche dans l'annuaire téléphonique, le meilleur des cas est que nous trouvons le nom dans une comparaison. C'est O (1) ou complexité constante;
- Cas attendu: Comme indiqué ci-dessus, il s'agit de O (log n); et
- Pire cas: C'est aussi O (log n).
Normalement, nous ne nous soucions pas du meilleur des cas. Nous nous intéressons au cas attendu et au pire des cas. Parfois l'un ou l'autre sera plus important.
Retour à l'annuaire téléphonique.
Que faire si vous avez un numéro de téléphone et que vous voulez trouver un nom? La police a un annuaire téléphonique inversé, mais ces recherches sont refusées au grand public. Ou sont-ils? Techniquement, vous pouvez inverser la recherche d'un numéro dans un annuaire ordinaire. Comment?
Vous commencez par le prénom et comparez le numéro. Si c'est un match, tant mieux, sinon, vous passez au suivant. Vous devez le faire de cette façon car le répertoire téléphonique est non numéroté (par numéro de téléphone de toute façon).
Donc, pour trouver un nom donné le numéro de téléphone (recherche inversée):
- Meilleur cas: O (1);
- Cas attendu: O(n) (pour 500 000); et
- Pire cas: O(n) (pour 1 000 000).
C’est un problème assez connu en informatique et qui mérite une mention. Dans ce problème, vous avez N villes. Chacune de ces villes est reliée à une ou plusieurs autres villes par une route d'une certaine distance. Le problème du voyageur de commerce est de trouver le circuit le plus court qui visite toutes les villes.
Cela semble simple? Pensez encore.
Si vous avez 3 villes A, B et C avec des routes entre toutes les paires, alors vous pouvez aller:
- A → B → C
- A → C → B
- B → C → A
- B → A → C
- C → A → B
- C → B → A
En fait, il y en a moins que cela parce que certaines sont équivalentes (A → B → C et C → B → A sont équivalentes, par exemple, parce qu'elles utilisent les mêmes routes, mais en sens inverse).
En réalité, il y a 3 possibilités.
- Emmenez-le dans 4 villes et vous avez (iirc) 12 possibilités.
- Avec 5 c'est 60.
- 6 devient 360.
Ceci est une fonction d'une opération mathématique appelée factorial. Fondamentalement:
- 5! = 5 × 4 × 3 × 2 × 1 = 120
- 6! = 6 × 5 × 4 × 3 × 2 × 1 = 720
- 7! = 7 × 6 × 5 × 4 × 3 × 2 × 1 = 5040
- …
- 25! = 25 × 24 ×… × 2 × 1 = 15 511 210 043 330 985 984 000 000
- …
- 50! = 50 × 49 ×… × 2 × 1 = 3,04140932 × 1064
Ainsi, le problème majeur du voyageur de commerce est O (n!) ou complexité factorielle ou combinatoire.
Au moment où vous arrivez dans 200 villes, il ne reste plus assez de temps dans l'univers pour résoudre le problème avec les ordinateurs traditionnels.
Quelque chose à quoi penser.
Un autre point que je voulais mentionner rapidement est que tout algorithme ayant une complexité de O (nune) est dit avoir complexité polynomiale ou peut être résolu en temps polynomial.
O (n), O (n2) etc. sont tous le temps polynomial. Certains problèmes ne peuvent pas être résolus en temps polynomial. Certaines choses sont utilisées dans le monde pour cette raison. Cryptographie à clé publique est un excellent exemple. Il est difficile de calculer deux facteurs premiers d’un très grand nombre. Sinon, nous ne pourrions pas utiliser les systèmes de clé publique que nous utilisons.
Quoi qu'il en soit, c'est tout pour mon explication (espérons-le en anglais clair) de Big O (révisée).
Il montre comment un algorithme évolue.
Sur2) : dit Complexe quadratique
Notez que le nombre d'items augmente d'un facteur 10, mais que le temps augmente d'un facteur 10.2. Fondamentalement, n = 10 et donc O (n2) nous donne le facteur d'échelle n2 qui est 102.
O(n) : dit Complexité linéaire
Cette fois, le nombre d'éléments augmente d'un facteur 10, de même que le temps. n = 10 et le facteur d'échelle de O (n) est donc 10.
O(1) : dit Complexité constante
Le nombre d'éléments augmente toujours d'un facteur 10, mais le facteur d'échelle de O(1) est toujours 1.
O (log n) : dit Complexité logarithmique
Le nombre de calculs n’est augmenté que par un journal de la valeur en entrée. Donc, dans ce cas, en supposant que chaque calcul prend 1 seconde, le journal de l’entrée n
est le temps requis, d’où log n
.
C'est l'essentiel. Ils réduisent les calculs pour qu'il ne soit peut-être pas exactement n2 ou peu importe ce qu'ils disent, mais ce sera le facteur dominant dans la mise à l'échelle.
La notation Big-O (également appelée notation "croissance asymptotique") est ce que les fonctions "ressemblent" lorsque vous ignorez les facteurs constants et les éléments proches de l'origine . Nous l’utilisons pour parler de l’échelle de chose .
Bases
pour des entrées "suffisamment" grandes ...
f(x) ∈ O(upperbound)
signifie f
"ne croît pas plus vite que" upperbound
f(x) ∈ Ɵ(justlikethis)
signifie f
"grandit exactement comme" justlikethis
f(x) ∈ Ω(lowerbound)
signifie f
"ne croît pas plus lentement que" lowerbound
la notation big-O ne tient pas compte des facteurs constants: la fonction 9x²
est dite "grandir exactement comme" 10x²
. Big-O asymptotique ne s’intéresse pas non plus à des éléments non asymptotiques ("éléments proches de l’origine" ou "que se passe-t-il lorsque le la taille du problème est petite "): on dit que la fonction 10x²
" grandit exactement comme "10x² - x + 2
.
Pourquoi voudriez-vous ignorer les plus petites parties de l'équation? Parce qu’elles sont complètement réduites à néant par les grandes parties de l’équation lorsque vous considérez des échelles de plus en plus grandes; leur contribution devient négligeable et sans importance. (Voir la section exemple.)
En d'autres termes, il s'agit du rapport à l'infini. Si vous divisez la durée réelle par O(...)
, vous obtiendrez un facteur constant dans la limite d'entrées volumineuses. Intuitivement, cela a du sens: les fonctions "s'échelonnent comme si" vous pouvez multiplier l'un pour obtenir l'autre. C'est-à-dire, quand on dit ...
actualAlgorithmTime(N) ∈ O(bound(N))
e.g. "time to mergesort N elements
is O(N log(N))"
... cela signifie que pour un problème "assez grand" de taille N (si nous ignorons les choses à proximité l'Origine), il existe des constantes (par exemple 2,5, entièrement composées) telles que:
actualAlgorithmTime(N) e.g. "mergesort_duration(N) "
────────────────────── < constant ───────────────────── < 2.5
bound(N) N log(N)
Il y a beaucoup de choix de constante; souvent, le "meilleur" choix est appelé "facteur constant" de l'algorithme ... mais nous l'ignorons souvent, tout comme nous ignorons les termes non plus grands (voir la section sur les facteurs constants pour savoir pourquoi ils ne comptent généralement pas). Vous pouvez également considérer l'équation ci-dessus comme une borne et dire ". Dans le pire des cas, le temps qu'il faudra ne sera jamais pire que approximativement N*log(N)
, dans un facteur de 2,5 (un facteur constant). ne vous souciez pas beaucoup de) ".
En général, O(...)
est le plus utile, car nous nous soucions souvent du comportement dans le pire des cas. Si f(x)
représente quelque chose de "mauvais" comme une utilisation du processeur ou de la mémoire, alors "f(x) ∈ O(upperbound)
" signifie "upperbound
est le pire des cas d'utilisation du processeur/de la mémoire".
Applications
En tant que construction purement mathématique, la notation big-O ne se limite pas à parler de temps de traitement et de mémoire. Vous pouvez l’utiliser pour discuter des aspects asymptotiques de tout élément où la mise à l’échelle est significative, tels que:
N
personnes lors d'une fête (Ɵ(N²)
, en particulier N(N-1)/2
, mais ce qui compte, c'est qu'elle "évolue comme" N²
)Exemple
Pour l'exemple de poignée de main ci-dessus, tout le monde dans une pièce serre la main de tous les autres. Dans cet exemple, #handshakes ∈ Ɵ(N²)
. Pourquoi?
Sauvegardez un peu: le nombre de poignées de main est exactement n-choisissez-2 ou N*(N-1)/2
(chacune des N personnes serre la main de N-1 autres personnes, mais ces doubles poignées de main sont divisées par 2):
Cependant, pour un très grand nombre de personnes, le terme linéaire N
est réduit au minimum et contribue effectivement à 0 dans le rapport (dans le graphique: la fraction des cases vides sur la diagonale par rapport au nombre total de cases diminue à mesure que le nombre de participants diminue). plus grand). Par conséquent, le comportement de la mise à l'échelle est order N²
ou le nombre de poignées de main "croît comme N²".
#handshakes(N)
────────────── ≈ 1/2
N²
C'est comme si les cases vides sur la diagonale de la carte (N * (N-1)/2 coches) n'étaient même pas là (N2 coches asymptotiquement).
(digression temporaire de "plain anglais" :) Si vous vouliez vous le prouver, vous pouvez effectuer une algèbre simple sur le rapport pour le scinder en plusieurs termes (lim
signifie "considéré dans la limite de", Ignorez-le si vous ne l'avez pas vu, c'est juste une notation pour "et N est vraiment grand"):
N²/2 - N/2 (N²)/2 N/2 1/2
lim ────────── = lim ( ────── - ─── ) = lim ─── = 1/2
N→∞ N² N→∞ N² N² N→∞ 1
┕━━━┙
this is 0 in the limit of N→∞:
graph it, or plug in a really large number for N
tl; dr: Le nombre de poignées de main 'ressemble à' x² tellement pour les grandes valeurs, que si nous devions écrire le rapport # handshakes/x², le fait de ne pas avoir besoin exactement x² poignées de main n'apparaîtraient même pas dans la décimale pendant un temps arbitrairement grand.
par exemple. pour x = 1 million, rapport # poignées de main/x²: 0.499999 ...
Intuition de construction
Cela nous permet de faire des déclarations comme ...
"Pour une taille d'entrée suffisamment grande = N, peu importe le facteur constant, si I double la taille d'entrée ...
N → (2N) = 2 (N)
N² → (2N) ² = 4 ( N² )
cN³ → c (2N) ³ = 8 ( cN³ )
c log (N) → c log (2N) = (c log (2)) + ( c log (N) ) = (montant fixe) + ( c log (N) )
c * 1 → c * 1
c'est moins que O (N1,000001), que vous voudrez peut-être appeler essentiellement linéaire
2N → 22N = (4N) ............ autrement dit ...... 2N → 2N + 1 = 2N21 = 2 2N
[pour les mathématiquement inclinés, vous pouvez passer la souris sur les spoilers pour les notes mineures]
(avec crédit pour https://stackoverflow.com/a/487292/711085 )
(techniquement, le facteur constant pourrait peut-être avoir une importance dans certains exemples plus ésotériques, mais j’ai énoncé les choses ci-dessus (par exemple dans log (N)) de telle sorte que ce n’est pas le cas)
Ce sont les ordres de croissance que les programmeurs et les informaticiens appliqués utilisent comme points de référence. Ils les voient tout le temps. (Alors, bien que vous puissiez techniquement penser "doubler l'entrée rend un algorithme O (√N) 1,414 fois plus lent," il est préférable de penser que "c'est pire que logarithmique mais meilleur que linéaire".)
Facteurs constants
Habituellement, nous ne nous soucions pas des facteurs constants spécifiques, car ils n’affectent pas la façon dont la fonction se développe. Par exemple, deux algorithmes peuvent prendre O(N)
fois, mais l’un peut être deux fois plus lent que l’autre. Nous ne nous soucions généralement pas trop, sauf si le facteur est très grand, car l'optimisation est une tâche délicate ( Quand l'optimisation est-elle prématurée? ); de plus, le simple fait de choisir un algorithme avec un meilleur big-O améliorera souvent les performances par ordres de grandeur.
Certains algorithmes asymptotiquement supérieurs (par exemple, un type O(N log(log(N)))
sans comparaison) peuvent avoir un facteur constant si important (par exemple, 100000*N log(log(N))
) ou un temps système relativement important, comme O(N log(log(N)))
avec un + 100*N
caché, qu'ils valent rarement la peine d'être utilisés, même sur "Big Data".
Pourquoi O(N) est parfois ce que vous pouvez faire de mieux, c'est-à-dire pourquoi nous avons besoin d'une infrastructure de données
Les algorithmes O(N)
sont en quelque sorte les "meilleurs" algorithmes si vous devez lire toutes vos données. L'acte de lire un groupe de données est une opération O(N)
. Le chargement en mémoire est généralement O(N)
(ou plus rapide si vous avez une prise en charge matérielle, ou pas du tout si vous avez déjà lu les données). Cependant, si vous touchez ou même regardez toutes les données (ou même toutes les autres données), votre algorithme prendra O(N)
pour effectuer cette recherche. Peu importe le temps que prend votre algorithme, il sera au moins O(N)
car il a passé tout ce temps à examiner toutes les données.
La même chose peut être dite pour le même acte d'écriture . Tous les algorithmes imprimant N éléments prendront N temps, car la sortie est au moins aussi longue (par exemple, l’impression de toutes les permutations (façons de réarranger) un jeu de N cartes à jouer est factorielle: O(N!)
).
Ceci motive l'utilisation de structures de données : une structure de données nécessite de lire les données une seule fois (généralement O(N)
fois), plus une quantité arbitraire de prétraitement (par exemple O(N)
ou O(N log(N))
ou O(N²)
) que nous essayons de garder petits. Ensuite, la modification de la structure de données (insertions/suppressions/etc.) et les requêtes sur les données prennent très peu de temps, comme O(1)
ou O(log(N))
. Vous procédez ensuite à un grand nombre de requêtes! En général, plus vous êtes prêt à faire du travail à l'avance, moins vous devrez le faire plus tard.
Par exemple, supposons que vous ayez les coordonnées de latitude et de longitude de millions de segments de route et que vous souhaitiez trouver toutes les intersections de rues.
O(N)
une seule fois, mais si vous voulez le faire plusieurs fois (dans ce cas, N
fois, une fois pour chaque segment), il faudrait effectuer O(N²)
travail ou 1000000² = 1000000000000 opérations. Pas bon (un ordinateur moderne peut effectuer environ un milliard d'opérations par seconde).O(N)
. Par la suite, il suffit en moyenne de temps constant pour rechercher un élément à l’aide de sa clé (dans ce cas, notre clé est constituée des coordonnées de latitude et de longitude, arrondies dans une grille; nous recherchons les espaces de grille adjacents, qui sont au nombre de 9, constant).O(N²)
infaisable à une O(N)
gérable, et tout ce que nous devions faire était de payer un coût minime pour créer une table de hachage.La morale de l'histoire: une structure de données nous permet d'accélérer les opérations. Des structures de données encore plus avancées peuvent vous permettre de combiner, de retarder, voire d’ignorer des opérations de manière extrêmement intelligente. Différents problèmes auraient des analogies différentes, mais ils impliqueraient tous d'organiser les données de manière à exploiter une structure qui nous tient à coeur ou que nous leur avons imposée artificiellement pour la comptabilité. Nous travaillons à l'avance (essentiellement en planifiant et en organisant), et maintenant, les tâches répétitives sont beaucoup plus faciles!
Exemple pratique: visualisation des ordres de croissance lors du codage
La notation asymptotique est fondamentalement distincte de la programmation. La notation asymptotique est un cadre mathématique permettant de réfléchir à la façon dont les choses évoluent et peut être utilisée dans de nombreux domaines différents. Cela dit ... c’est ainsi que vous appliquez la notation asymptotique au codage.
Les bases: chaque fois que nous interagissons avec chaque élément d’une collection de taille A (telle qu’un tableau, un ensemble, toutes les clés d’une carte, etc.), ou effectuons A des itérations d’une boucle, c’est un facteur multiplicatif de taille A Pourquoi est-ce que je dis "un facteur multiplicatif"? Parce que les boucles et les fonctions (presque par définition) ont un temps d'exécution multiplicatif: le nombre d'itérations, le temps de travail effectué dans la boucle (ou pour les fonctions: le nombre de fois que vous appelez le fonction, fois le travail effectué dans la fonction). (Ceci est valable si nous ne faisons rien d'extraordinaire, comme sauter des boucles ou quitter la boucle plus tôt, ou modifier le flux de contrôle de la fonction en fonction d'arguments, ce qui est très courant.) Voici quelques exemples de techniques de visualisation, accompagnées d'un pseudocode.
(ici, les x
s représentent des unités de travail à temps constant, des instructions de processeur, des opcodes d'interpréteur, peu importe)
for(i=0; i<A; i++) // A * ...
some O(1) operation // 1
--> A*1 --> O(A) time
visualization:
|<------ A ------->|
1 2 3 4 5 x x ... x
other languages, multiplying orders of growth:
javascript, O(A) time and space
someListOfSizeA.map((x,i) => [x,i])
python, O(rows*cols) time and space
[[r*c for c in range(cols)] for r in range(rows)]
Exemple 2:
for every x in listOfSizeA: // A * (...
some O(1) operation // 1
some O(B) operation // B
for every y in listOfSizeC: // C * (...
some O(1) operation // 1))
--> O(A*(1 + B + C))
O(A*(B+C)) (1 is dwarfed)
visualization:
|<------ A ------->|
1 x x x x x x ... x
2 x x x x x x ... x ^
3 x x x x x x ... x |
4 x x x x x x ... x |
5 x x x x x x ... x B <-- A*B
x x x x x x x ... x |
................... |
x x x x x x x ... x v
x x x x x x x ... x ^
x x x x x x x ... x |
x x x x x x x ... x |
x x x x x x x ... x C <-- A*C
x x x x x x x ... x |
................... |
x x x x x x x ... x v
Exemple 3:
function nSquaredFunction(n) {
total = 0
for i in 1..n: // N *
for j in 1..n: // N *
total += i*k // 1
return total
}
// O(n^2)
function nCubedFunction(a) {
for i in 1..n: // A *
print(nSquaredFunction(a)) // A^2
}
// O(a^3)
Si nous faisons quelque chose de légèrement compliqué, vous pourrez peut-être toujours imaginer ce qui se passe:
for x in range(A):
for y in range(1..x):
simpleOperation(x*y)
x x x x x x x x x x |
x x x x x x x x x |
x x x x x x x x |
x x x x x x x |
x x x x x x |
x x x x x |
x x x x |
x x x |
x x |
x___________________|
Ici, le plus petit contour reconnaissable que vous puissiez dessiner est ce qui compte; un triangle est une forme bidimensionnelle (0.5 A ^ 2), tout comme un carré est une forme bidimensionnelle (A ^ 2); le facteur constant de deux reste ici dans le rapport asymptotique entre les deux, cependant nous l'ignorons comme tous les facteurs ... (Il y a quelques nuances malheureuses dans cette technique dans laquelle je n'entre pas ici; elle peut vous induire en erreur.)
Bien sûr, cela ne signifie pas que les boucles et les fonctions sont mauvaises; au contraire, ils sont les blocs de construction des langages de programmation modernes, et nous les aimons. Cependant, nous pouvons voir que la manière dont nous tissons les boucles, les fonctions et les conditions ainsi que nos données (flux de contrôle, etc.) reproduit l'utilisation de notre programme dans le temps! Si l'utilisation du temps et de l'espace devient un problème, c'est alors que nous recourons à l'intelligence et trouvons un algorithme simple ou une structure de données que nous n'avions pas envisagée pour réduire l'ordre de croissance d'une manière ou d'une autre. Néanmoins, ces techniques de visualisation (bien qu'elles ne fonctionnent pas toujours) peuvent vous donner une conjecture naïve au pire des cas.
Voici une autre chose que nous pouvons reconnaître visuellement:
<----------------------------- N ----------------------------->
x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x
x x x x x x x x x x x x x x x x
x x x x x x x x
x x x x
x x
x
Nous pouvons simplement réorganiser ceci et voir que c'est O (N):
<----------------------------- N ----------------------------->
x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x
x x x x x x x x x x x x x x x x|x x x x x x x x|x x x x|x x|x
Ou peut-être que vous enregistrez (N) passes de données, pour O (N * log (N)) temps total:
<----------------------------- N ----------------------------->
^ x x x x x x x x x x x x x x x x|x x x x x x x x x x x x x x x x
| x x x x x x x x|x x x x x x x x|x x x x x x x x|x x x x x x x x
lgN x x x x|x x x x|x x x x|x x x x|x x x x|x x x x|x x x x|x x x x
| x x|x x|x x|x x|x x|x x|x x|x x|x x|x x|x x|x x|x x|x x|x x|x x
v x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x|x
Indépendamment, mais il convient de mentionner à nouveau: Si nous effectuons un hachage (par exemple une recherche dans un dictionnaire/une table de hachage), le facteur est O (1). C'est assez rapide.
[myDictionary.has(x) for x in listOfSizeA]
\----- O(1) ------/
--> A*1 --> O(A)
Si nous faisons quelque chose de très compliqué, comme avec une fonction récursive ou un algorithme de division et conquête, vous pouvez utiliser le Théorème Maître (fonctionne habituellement), ou dans des cas ridicules, le Théorème Akra-Bazzi (fonctionne presque toujours) vous consultez le temps d'exécution de votre algorithme sur Wikipedia.
Mais les programmeurs ne pensent pas comme cela parce que finalement, l’intuition de l’algorithme devient simplement une seconde nature. Vous commencerez à coder quelque chose d’inefficace et vous vous direz immédiatement: "Est-ce que je fais quelque chose de manière flagrante inefficace? ". Si la réponse est "oui" et que vous pensez que cela compte vraiment, vous pouvez prendre du recul et penser à diverses astuces pour accélérer les choses (la réponse est presque toujours "utilisez un hashtable", rarement "utilisez un arbre", et très rarement quelque chose d'un peu plus compliqué).
Complexité amortie et moyenne des cas
Il y a aussi le concept de "amorti" et/ou "cas moyen" (notez que ceux-ci sont différents).
Cas moyen : il s’agit simplement d’utiliser la notation big-O pour la valeur attendue d’une fonction, plutôt que la fonction elle-même. Dans le cas habituel où vous considérez que toutes les entrées ont la même probabilité, le cas moyen est simplement la moyenne de la durée d'exécution. Par exemple, avec quicksort, même si le pire des cas est O(N^2)
pour certaines très mauvaises entrées, le cas moyen est le cas habituel O(N log(N))
(les très mauvaises entrées sont très peu nombreuses, si peu que nous ne les remarquions pas dans le cas moyen ).
Cas le plus défavorable après amortissement : certaines structures de données peuvent présenter une complexité dans le cas le plus défavorable, mais vous garantissez que si vous effectuez plusieurs de ces opérations, la moyenne la quantité de travail que vous ferez sera meilleure que le pire des cas. Par exemple, vous pouvez avoir une structure de données qui prend normalement une durée O(1)
constante. Cependant, de temps en temps, il "hoquet" et prendra O(N)
fois pour une opération aléatoire, car il doit peut-être faire de la comptabilité ou un ramassage des ordures ou quelque chose d'autre ... N plus d'opérations. Le coût dans le cas le plus défavorable reste toujours O(N)
par opération, mais le coût amorti sur plusieurs exécutions est de O(N)/N
= O(1)
par opération. Parce que les grandes opérations sont suffisamment rares, on peut considérer que l’énorme quantité de travail occasionnel se confond avec le reste du travail. Nous disons que le travail est "amorti" sur un nombre d'appels suffisamment grand pour qu'il disparaisse de manière asymptotique.
L'analogie pour l'analyse amortie:
Vous conduisez une voiture. De temps en temps, vous devez passer 10 minutes à la station d’essence, puis 1 minute pour faire le plein d’essence. Si vous le faisiez chaque fois que vous alliez avec votre voiture (passez 10 minutes en voiture à la station d’essence, quelques secondes pour remplir une fraction de gallon), ce serait très inefficace. Mais si vous faites le plein une fois tous les deux ou trois jours, les onze minutes de conduite jusqu’à la station-service sont "amorties" sur un nombre de voyages suffisamment grand pour que vous puissiez les ignorer et prétendre que tous vos voyages durent peut-être 5% plus longtemps.
Comparaison entre le pire des cas et le pire des cas amortis:
Bien que, si vous êtes raisonnablement inquiet à propos d'un attaquant, il existe de nombreux autres vecteurs d'attaque algorithmiques à craindre, outre l'amortissement et le cas moyen.)
La casse moyenne et l'amortissement sont des outils extrêmement utiles pour réfléchir et concevoir avec une mise à l'échelle en tête.
(Voir Différence entre le cas moyen et l'analyse amortie si intéressé par ce sujet secondaire.)
Big-O multidimensionnelle
La plupart du temps, les gens ne se rendent pas compte qu'il y a plus d'une variable au travail. Par exemple, dans un algorithme de recherche de chaîne, votre algorithme peut prendre du temps O([length of text] + [length of query])
, c’est-à-dire qu’il est linéaire dans deux variables telles que O(N+M)
. D'autres algorithmes plus naïfs peuvent être O([length of text]*[length of query])
ou O(N*M)
. Ignorer plusieurs variables est l’un des oublis les plus courants que je vois dans l’analyse algorithmique et peut vous handicaper lors de la conception d’un algorithme.
Toute l'histoire
Gardez à l'esprit que big-O n'est pas toute l'histoire. Vous pouvez accélérer considérablement certains algorithmes en utilisant la mise en cache, en les rendant oubliés du cache, en évitant les goulets d'étranglement en travaillant avec RAM, en utilisant la parallélisation ou en effectuant des travaux à l'avance indépendant de la notation "big-O" d'ordre de croissance, bien que vous voyiez souvent le nombre de cœurs dans la notation big-O des algorithmes parallèles.
Gardez également à l'esprit qu'en raison des contraintes cachées de votre programme, le comportement asymptotique pourrait ne pas vous intéresser. Vous travaillez peut-être avec un nombre limité de valeurs, par exemple:
O(N log(N))
quicksort; vous voulez utiliser le tri par insertion, ce qui se passe bien sur de petites entrées. Ces situations se présentent souvent dans des algorithmes de division et de conquête, dans lesquels vous divisez le problème en sous-problèmes de plus en plus petits, tels que le tri récursif, les transformations rapides de Fourier ou la multiplication de matrices.En pratique, même parmi les algorithmes ayant des performances asymptotiques identiques ou similaires, leur mérite relatif peut en réalité être influencé par d'autres facteurs, tels que: d'autres facteurs de performance (quicksort et mergesort sont tous deux O(N log(N))
, mais quicksort tire parti des caches de processeur); considérations de non-exécution, comme la facilité de mise en œuvre; si une bibliothèque est disponible, et quelle est la réputation et la maintenance de la bibliothèque.
Les programmes fonctionneront également plus lentement sur un ordinateur à 500 MHz par rapport à un ordinateur à 2 GHz. Nous ne considérons pas vraiment cela comme faisant partie des limites des ressources, car nous pensons à la mise à l'échelle en termes de ressources machine (par exemple, par cycle d'horloge), et non par seconde réelle. Cependant, certaines choses similaires peuvent affecter "secrètement" les performances, par exemple si vous utilisez l'émulation ou si le code du compilateur a été optimisé ou non. Cela pourrait rendre certaines opérations de base plus longues (même les unes par rapport aux autres), voire accélérer ou ralentir certaines opérations de manière asymptotique (même les unes par rapport aux autres). L'effet peut être faible ou important entre différentes implémentations et/ou environnements. Changez-vous de langue ou de machine pour faire ce petit travail supplémentaire? Cela dépend d'une centaine d'autres raisons (nécessité, compétences, collègues, productivité du programmeur, valeur monétaire de votre temps, familiarité, solutions de contournement, pourquoi pas Assembly ou GPU, etc.), qui peuvent être plus importantes que la performance.
Les problèmes ci-dessus, comme le langage de programmation, ne sont presque jamais considérés comme faisant partie du facteur constant (et ne devraient pas l'être); pourtant, il faut en être conscient, car parfois (bien que rarement), ils peuvent affecter des choses. Par exemple, dans cpython, l'implémentation de la file de priorité native est asymptotiquement non optimale (O(log(N))
plutôt que O(1)
pour votre choix d'insertion ou de recherche-min); utilisez-vous une autre implémentation? Probablement pas, car la mise en œuvre du C est probablement plus rapide et il existe probablement d'autres problèmes similaires ailleurs. Il y a des compromis; parfois ils comptent et parfois non.
( edit : l'explication "anglais simplifié" se termine ici.)
Addenda mathématique
Pour être complet, la définition précise de la notation big-O est la suivante: f(x) ∈ O(g(x))
signifie que "f est limité asymptotiquement par const * g": en ignorant tout ce qui est en dessous d'une valeur finie de x, il existe une constante telle que |f(x)| ≤ const * |g(x)|
. (Les autres symboles sont les suivants: tout comme O
signifie ≤, Ω
signifie ≥. Il existe des variantes minuscules: o
signifie <et ω
signifie>.) f(x) ∈ Ɵ(g(x))
signifie à la fois f(x) ∈ O(g(x))
et f(x) ∈ Ω(g(x))
(bornes supérieure et inférieure par g): il existe des constantes telles que f sera toujours dans la "bande" entre const1*g(x)
et const2*g(x)
. C'est la déclaration asymptotique la plus forte que vous puissiez faire et à peu près équivalente à ==
. (Désolé, j'ai choisi de repousser la mention des symboles de valeur absolue jusqu'à présent, par souci de clarté; en particulier parce que je n'ai jamais vu de valeurs négatives apparaître dans un contexte informatique.)
Les gens vont souvent utiliser = O(...)
, qui est peut-être la notation 'comp-sci' la plus correcte, et tout à fait légitime à utiliser; "f = O (...)" est lu "f est ordre .../f est borné par xxx par ..." et est considéré comme "f est une expression dont les asymptotiques sont ...". On m'a appris à utiliser la ∈ O(...)
plus rigoureuse. ∈
signifie "est un élément de" (toujours lu comme avant). O(N²)
est en fait une classe d'équivalence , c'est-à-dire un ensemble de choses que nous considérons comme identiques. Dans ce cas particulier, O(N²)
contient des éléments tels que {2 N²
, 3 N²
, 1/2 N²
, 2 N² + log(N)
, - N² + N^1.9
, ...} et est infiniment grand, mais reste un ensemble. . La notation =
pourrait être la plus courante, et est même utilisée dans les papiers par des informaticiens de renommée mondiale. De plus, il arrive souvent que, dans un cadre informel, les gens disent O(...)
alors qu'ils veulent dire Ɵ(...)
; c'est techniquement vrai puisque l'ensemble des éléments Ɵ(exactlyThis)
est un sous-ensemble de O(noGreaterThanThis)
... et qu'il est plus facile à taper. ;-)
EDIT: Remarque rapide, c'est presque certainement déroutant Big O notation (qui est une limite supérieure) avec la notation Thêta (qui est à la fois une limite supérieure et inférieure). D'après mon expérience, cela est en fait typique des discussions dans des contextes non académiques. Toutes mes excuses pour toute confusion causée.
En une phrase: À mesure que la taille de votre travail augmente, combien de temps faut-il pour le terminer?
Évidemment, vous n'utilisez que "taille" comme entrée et "temps pris" comme sortie - la même idée s'applique si vous voulez parler de l'utilisation de la mémoire, etc.
Voici un exemple où nous avons N T-shirts que nous voulons sécher. Nous allons supposer qu'il est extrêmement rapide de les mettre en position de séchage (c'est-à-dire que l'interaction humaine est négligeable). Ce n'est pas le cas dans la vraie vie, bien sûr ...
Utilisation d'une corde à linge à l'extérieur: en supposant que vous avez une cour arrière infiniment grande, le linge sèche en O(1). Quelle que soit la quantité dont vous disposez, le soleil et l'air frais seront identiques, de sorte que la taille n'affectera pas le temps de séchage.
À l'aide d'un sèche-linge: vous mettez 10 chemises dans chaque chargement, puis elles sont terminées une heure plus tard. (Ignorez les chiffres réels ici - ils ne sont pas pertinents.) Ainsi, le séchage de 50 chemises prend environ 5 fois plus longtemps que le séchage de 10 chemises.
Tout mettre dans une aération: Si nous mettons tout en un gros tas et laissons simplement la chaleur générale le faire, il faudra beaucoup de temps pour que les chemises du milieu se dessèchent. Je ne voudrais pas deviner les détails, mais je soupçonne qu’il s’agit au moins de O (N ^ 2). Plus vous augmentez la charge de lavage, plus le temps de séchage augmente.
Un aspect important de la notation "big O" est que ne dit pas quel algorithme sera le plus rapide pour une taille donnée. Prenez une table de hachage (clé de chaîne, valeur entière) contre un tableau de paires (chaîne, entier). Est-il plus rapide de trouver une clé dans la table de hachage ou un élément du tableau, basé sur une chaîne? (c.-à-d. pour le tableau, "trouver le premier élément où la partie de chaîne correspond à la clé donnée".) Les tables de hachage sont généralement amorties (~ = "en moyenne") O(1) - une fois qu'elles sont configurées, Il faut à peu près le même temps pour trouver une entrée dans une table à 100 entrées que dans une table à 1 000 000 entrées. La recherche d'un élément dans un tableau (basé sur le contenu plutôt que sur l'index) est linéaire, c'est-à-dire O(N) - en moyenne, vous devrez examiner la moitié des entrées.
Cela rend-il une table de hachage plus rapide qu'un tableau pour les recherches? Pas nécessairement. Si vous avez une très petite collection d'entrées, un tableau peut être plus rapide - vous pourrez peut-être vérifier toutes les chaînes le temps nécessaire pour calculer le hashcode de celui que vous regardez. Cependant, à mesure que le jeu de données grossit, la table de hachage finira par vaincre le tableau.
Big O décrit une limite supérieure du comportement de croissance d'une fonction, par exemple l'exécution d'un programme, lorsque les entrées deviennent grandes.
Exemples:
O (n): Si je double la taille de l'entrée, le temps d'exécution double
Sur2): Si la taille d'entrée double les quadruples d'exécution
O (log n): si la taille d'entrée double, la durée d'exécution augmente d'un
O (2n): Si la taille d'entrée augmente de un, le temps d'exécution double
La taille de l'entrée est généralement l'espace en bits nécessaire pour représenter l'entrée.
La notation Big O est le plus souvent utilisée par les programmeurs comme mesure approximative de la durée nécessaire à un calcul (algorithme), exprimée en fonction de la taille du jeu d'entrées.
Big O est utile pour comparer la mise à l’échelle de deux algorithmes lorsque le nombre d’entrées augmente.
Plus précisément, notation Big O est utilisé pour exprimer le comportement asymptotique d'une fonction. Cela signifie que la fonction se comporte à l'approche de l'infini.
Dans de nombreux cas, le "O" d'un algorithme tombera dans l'un des cas suivants:
Big O ignore les facteurs qui ne contribuent pas de manière significative à la courbe de croissance d'une fonction lorsque la taille de l'entrée augmente vers l'infini. Cela signifie que les constantes ajoutées ou multipliées par la fonction sont simplement ignorées.
Big O est simplement un moyen de "s'exprimer" de manière commune, "combien de temps/espace faut-il pour exécuter mon code?".
Vous pouvez souvent voir O (n), O (n2), O(nlogn) et ainsi de suite, tout cela ne sont que des façons de montrer; Comment un algorithme change-t-il?
O (n) signifie que Big O est n, et maintenant vous pourriez penser: "Qu'est-ce que n !?" Eh bien "n" est la quantité d'éléments. Imaginez que vous souhaitez rechercher un élément dans un tableau. Vous devriez regarder chaque élément et dire "Êtes-vous le bon élément/élément?" dans le pire des cas, l'élément est au dernier indice, ce qui signifie qu'il a fallu autant de temps qu'il y a d'éléments dans la liste, alors pour être générique, nous disons "oh hé, n est une quantité donnée de valeurs!" .
Alors vous pourriez comprendre quoi "n2"signifie, mais pour être encore plus spécifique, jouez avec la pensée que vous avez un algorithme de tri simple, le plus simple; Bubblesort. Cet algorithme doit parcourir toute la liste, pour chaque élément.
Ma liste
Le flux ici serait:
C'est O n2 parce que vous devez examiner tous les éléments de la liste, il y a "n" éléments. Pour chaque élément, vous examinez à nouveau tous les éléments. Pour la comparaison, il s'agit également de "n". Par conséquent, pour chaque élément, vous recherchez "n" fois, ce qui signifie n * n = n.2
J'espère que c'est aussi simple que vous le voulez.
Mais rappelez-vous, Big O est juste un moyen de vous exposer à la manière du temps et de l'espace.
Big O décrit la nature fondamentale de la mise à l'échelle d'un algorithme.
Il y a beaucoup d'informations que Big O ne vous dit pas à propos d'un algorithme donné. Il coupe jusque dans l'os et ne donne que des informations sur la nature dimensionnelle d'un algorithme, en particulier sur la manière dont l'utilisation des ressources (temps de réflexion ou mémoire) d'un algorithme est mise à l'échelle en réponse à la "taille d'entrée".
Considérez la différence entre un moteur à vapeur et une fusée. Ce ne sont pas simplement des variétés différentes de la même chose (comme, par exemple, un moteur Prius contre un moteur Lamborghini), mais ce sont des types de systèmes de propulsion radicalement différents, à la base. Un moteur à vapeur peut être plus rapide qu'une fusée-jouet, mais aucun moteur à piston à vapeur ne pourra atteindre les vitesses d'un lanceur orbital. En effet, ces systèmes ont des caractéristiques d’échelle différentes en ce qui concerne la relation entre le carburant requis ("utilisation des ressources") pour atteindre une vitesse donnée ("taille de l’entrée").
Pourquoi est-ce si important? Parce que les logiciels traitent des problèmes dont la taille peut varier d’un facteur à l’autre. Considérez cela un instant. Le rapport entre la vitesse nécessaire pour se rendre sur la Lune et la vitesse de marche d’un homme est inférieur à 10 000: 1, ce qui est tout à fait minuscule par rapport à la gamme de tailles d’entrée que le logiciel peut faire face. Et parce que les logiciels peuvent faire face à une gamme astronomique de tailles d’entrées, il est possible que la complexité de Big O d’un algorithme constitue un facteur fondamental en matière de mise à l’échelle, qui l'emporte sur les détails d'implémentation.
Prenons l'exemple de tri canonique. Bubble-Sort est O (n2) tandis que merge-sort est O (n log n). Supposons que vous ayez deux applications de tri, l’application A qui utilise le tri à bulle et l’application B qui utilise le tri par fusion, et supposons que pour des tailles d’entrée d’environ 30 éléments, l’application A soit 1 000 fois plus rapide que l’application B au tri. Si vous ne devez jamais trier plus de 30 éléments, il est évident que vous devriez préférer l'application A, car elle est beaucoup plus rapide pour ces tailles d'entrée. Cependant, si vous devez trier 10 millions d’articles, vous vous attendez à ce que l’application B finisse par des milliers de fois plus rapide que l’application A dans ce cas, entièrement à cause de la façon dont chaque algorithme évolue.
Voici le bestiaire anglais que j’ai tendance à utiliser pour expliquer les variétés courantes de Big-O.
Dans tous les cas, préférez les algorithmes situés en haut de la liste à ceux situés en bas de la liste. Toutefois, le coût du passage à une classe de complexité plus coûteuse varie considérablement.
O (1):
Pas de croissance. Peu importe l’ampleur du problème, vous pouvez le résoudre dans le même temps. Ceci est un peu analogue à la radiodiffusion où il faut la même quantité d'énergie pour diffuser sur une distance donnée, quel que soit le nombre de personnes se trouvant dans la portée de la diffusion.
O (log n):
Cette complexité est la même que O(1) sauf que c'est un peu pire. À toutes fins pratiques, vous pouvez considérer cela comme une très grande mise à l'échelle constante. La différence de travail entre 1 000 et 1 milliard d’articles transformés n’est que de six.
O (n):
Le coût de la résolution du problème est proportionnel à la taille du problème. Si la taille de votre problème double, le coût de la solution double. Étant donné que la plupart des problèmes doivent être numérisés dans l’ordinateur d’une manière ou d’une autre, qu’il s’agisse de la saisie de données, de la lecture de disques ou du trafic réseau, il s’agit généralement d’un facteur d’échelle abordable.
O (n log n):
Cette complexité est très similaire à O (n) . À toutes fins pratiques, les deux sont équivalents. Ce niveau de complexité serait généralement considéré comme évolutif. En modifiant les hypothèses, certains O (n log n) peuvent être transformés en O (n) algorithmes. Par exemple, limiter la taille des clés réduit le tri de O (n log n) à O (n) .
O (n2):
Grandit comme un carré, où n est la longueur du côté d’un carré. C'est le même taux de croissance que "l'effet réseau", où tout le monde sur un réseau peut connaître tout le monde sur le réseau. La croissance est chère. La plupart des solutions évolutives ne peuvent pas utiliser des algorithmes avec ce niveau de complexité sans faire une gymnastique significative. Ceci s’applique généralement à toutes les autres complexités polynomiales - O (nk) - ainsi que.
O (2n):
N'échelle pas. Vous n'avez aucun espoir de résoudre un problème de taille non triviale. Utile pour savoir ce qu’il faut éviter et pour que les experts trouvent des algorithmes approchés qui se trouvent dans O (nk) .
Big O est une mesure de la quantité de temps/espace qu'un algorithme utilise par rapport à la taille de son entrée.
Si un algorithme est O(n), le temps/espace augmentera au même rythme que son entrée.
Si un algorithme est O (n2) puis le temps/espace augmente au rythme de son entrée au carré.
etc.
Il est très difficile de mesurer la vitesse des logiciels et, lorsque nous essayons, les réponses peuvent être très complexes et comporter des exceptions et des cas particuliers. C'est un gros problème, car toutes ces exceptions et cas spéciaux sont gênants et inutiles lorsque nous voulons comparer deux programmes différents pour déterminer lequel est le plus rapide.
En raison de toute cette complexité inutile, les gens essaient de décrire la vitesse des logiciels utilisant les expressions les plus petites et les moins complexes (mathématiques) possibles. Ces expressions sont des approximations très très rudimentaires: bien qu’avec un peu de chance, elles capturent l’essence de savoir si un logiciel est rapide ou lent.
Parce qu’il s’agit d’approximations, nous utilisons la lettre "O" (Big Oh) dans l’expression, comme convention pour indiquer au lecteur que nous sommes en train de faire une simplification excessive. (Et pour s'assurer que personne ne pense à tort que l'expression est exacte).
Si vous lisez "Oh" comme signifiant "de l'ordre de" ou "approximativement", vous n'allez pas trop mal. (Je pense que le choix du Big-Oh aurait pu être une tentative d'humour).
La seule chose que ces expressions "Big-Oh" essaient de faire est de décrire le ralentissement du logiciel lorsque nous augmentons la quantité de données qu’il doit traiter. Si nous doublons la quantité de données à traiter, le logiciel a-t-il besoin de deux fois plus de temps pour terminer son travail? Dix fois plus longtemps? En pratique, vous rencontrerez un nombre très limité d'expressions big-oh et vous devez vous en préoccuper:
Le bon:
O(1)
Constant : Le programme prend le même temps pour s'exécuter quelle que soit la taille de l'entrée.O(log n)
Logarithmic : Le temps d'exécution du programme n'augmente que très lentement, même en cas de forte augmentation de la taille de l'entrée.Le mauvais:
O(n)
Linear : Le temps d'exécution du programme augmente proportionnellement à la taille de l'entrée.O(n^k)
Polynôme : - Le temps de traitement augmente de plus en plus rapidement - en tant que fonction polynomiale - à mesure que la taille de l'entrée augmente.... et le laid:
O(k^n)
Exponential Le temps d'exécution du programme augmente très rapidement même si la taille du problème augmente de façon même modérée. Le traitement de petits ensembles de données avec des algorithmes exponentiels est pratique.O(n!)
Factorial L'exécution du programme sera plus longue que ce que vous pouvez vous permettre d'attendre, sauf pour les jeux de données les plus petits et les plus triviaux.Qu'est-ce qu'une explication anglaise simple de Big O? Avec aussi peu de définitions formelles que possible et des mathématiques simples.
Explication en anglais simple du besoin pour la notation Big-O:
Lorsque nous programmons, nous essayons de résoudre un problème. Ce que nous codons s'appelle un algorithme. La notation Big O nous permet de comparer les performances dans le pire des cas de nos algorithmes de manière normalisée. Les spécifications matérielles varient dans le temps et les améliorations apportées au matériel peuvent réduire le temps nécessaire à l'exécution d'un algorithme. Mais remplacer le matériel ne signifie pas que notre algorithme est meilleur ou amélioré avec le temps, car notre algorithme est toujours le même. Ainsi, afin de nous permettre de comparer différents algorithmes, de déterminer si l’on est meilleur ou non, nous utilisons la notation Big O.
Explication anglaise simple de la Quoi La notation Big O est:
Tous les algorithmes ne fonctionnent pas dans le même temps et peuvent varier en fonction du nombre d'éléments de l'entrée, que nous appellerons n. Sur cette base, nous considérons que l'analyse des cas les plus défavorables, ou une limite supérieure du temps d'exécution, est telle que n s'agrandit. Nous devons être conscients de ce que (n) _ _ est, car la plupart des notations Big O y font référence.
Une réponse simple et directe peut être:
Big O représente le pire espace/temps possible pour cet algorithme. L'algorithme ne prendra jamais plus d'espace/temps au-dessus de cette limite. Big O représente la complexité spatio-temporelle dans le cas extrême.
Ok, mes 2cents.
Big-O, est le taux d'augmentation des ressources consommées par programme, w.r.t. taille d'instance de problème
Ressource: Il peut s'agir d'un temps CPU total ou maximal RAM. Par défaut se réfère au temps CPU.
Dis le problème c'est "Trouve la somme",
int Sum(int*arr,int size){
int sum=0;
while(size-->0)
sum+=arr[size];
return sum;
}
problem-instance = {5,10,15} ==> problem-instance-size = 3, itérations en boucle = 3
problem-instance = {5,10,15,20,25} ==> problem-instance-size = 5 itérations en boucle = 5
Pour une entrée de taille "n", le programme croît à la vitesse de "n" itérations dans un tableau. Donc Big-O est N exprimé en O (n)
Dites le problème est "trouver la combinaison",
void Combination(int*arr,int size)
{ int outer=size,inner=size;
while(outer -->0) {
inner=size;
while(inner -->0)
cout<<arr[outer]<<"-"<<arr[inner]<<endl;
}
}
problème-instance = {5,10,15} ==> problème-instance-size = 3, total-iterations = 3 * 3 = 9
problème-instance = {5,10,15,20,25} ==> problème-instance-taille = 5, total-iterations = 5 * 5 = 25
Pour une entrée de taille "n", le programme croît à la vitesse de "n * n" itérations dans un tableau. Donc Big-O est N2 exprimé par O (n2)
La notation Big O est une manière de décrire la limite supérieure d'un algorithme en termes d'espace ou de temps d'exécution. Le n est le nombre d'éléments dans le problème (c'est-à-dire la taille d'un tableau, le nombre de nœuds dans un arbre, etc.). Nous souhaitons décrire le temps d'exécution lorsque n devient grand.
Lorsque nous disons qu'un algorithme est O(f(n)), nous disons que le temps d'exécution (ou l'espace requis) par cet algorithme est toujours inférieur à certains temps constants f (n).
Dire que la recherche binaire a une durée d'exécution de O(logn), c'est dire qu'il existe une constante c avec laquelle vous pouvez multiplier log (n) par qui sera toujours supérieure à la durée d'exécution de la recherche binaire. Dans ce cas, vous aurez toujours un facteur constant de comparaisons log (n).
En d'autres termes, où g(n) est la durée d'exécution de votre algorithme, nous disons que g(n) = O(f(n)) lorsque g(n) <= c * f (n) lorsque n> k, où c et k sont des constantes.
"Qu'est-ce qu'une explication anglaise simple de Big O? Avec le moins de définitions possible et de mathématiques simples."
Une question aussi simple et aussi belle semble au moins mériter une réponse aussi courte que celle qu’un étudiant pourrait recevoir lors d’un tutorat.
La notation Big O indique simplement combien de temps * un algorithme peut s'exécuter, en termes de niquement la quantité de données d'entrée **.
(* dans un sens merveilleux, sans unité!)
(** Ce qui compte, car les gens vont toujours en veulent plus , qu'ils vivent aujourd'hui ou demain)
Eh bien, qu'est-ce qui est si merveilleux avec la notation Big O si c'est ce qu'elle fait?
Concrètement, l'analyse de Big O est si utile et si importante parce que Big O met l'accent sur la complexité de l'algorithme propre et complètement ignore tout ce qui est simplement une constante de proportionnalité - comme un moteur JavaScript, la vitesse d'un processeur, votre connexion Internet et toutes ces choses qui deviennent rapidement aussi obsolètes qu'un modèle T. Big O se concentre sur la performance uniquement d'une manière qui importe tout autant aux personnes vivant dans le présent ou à l'avenir.
La notation Big O met également directement en lumière le principe le plus important de la programmation/ingénierie informatique, le fait qui incite tous les bons programmeurs à continuer de penser et de rêver: le seul moyen d’obtenir des résultats au-delà de la lenteur de la technologie consiste à inventer un meilleur algorithme.
Exemple d'algorithme (Java):
// given a list of integers L, and an integer K
public boolean simple_search(List<Integer> L, Integer K)
{
// for each integer i in list L
for (Integer i : L)
{
// if i is equal to K
if (i == K)
{
return true;
}
}
return false;
}
Description de l'algorithme:
Cet algorithme recherche une liste, élément par élément, à la recherche d'une clé,
Itération sur chaque élément de la liste, si c'est la clé puis retourne True,
Si la boucle est terminée sans trouver la clé, retournez False.
La notation Big-O représente la limite supérieure de la complexité (temps, espace, ..)
Pour trouver le Big-O sur la complexité temporelle:
Calculez combien de temps (en termes de taille d’entrée) le pire des cas prend:
Pire cas: la clé n'existe pas dans la liste.
Temps (pire cas) = 4n + 1
Temps: O (4n + 1) = O(n) | dans Big-O, les constantes sont négligées
O (n) ~ linéaire
Il y a aussi Big Omega, qui représentent la complexité du Best-Case:
Meilleur cas: la clé est le premier élément.
Temps (meilleur cas) = 4
Heure: Ω (4) = O(1) ~ Instantanée\Constante
Big O
f (x) = O (g (x)) lorsque x passe à a (par exemple, a = + ∞) signifie qu'il existe une fonction k tel que:
f (x) = k (x) g (x)
k est borné dans un voisinage de a (si a = + ∞, cela signifie qu'il existe des nombres N et M tels que pour tout x> N, | k (x) | <M).
En d'autres termes, en clair: f (x) = O (g (x)), x → a, signifie que dans le voisinage de a, f se décompose en le produit de g et de certaines fonctions liées.
Petit o
À propos, voici pour la comparaison la définition de petit o.
f (x) = o (g (x)) lorsque x passe à un signifie qu'il existe une fonction k telle que:
f (x) = k (x) g (x)
k (x) passe à 0 lorsque x passe à a.
Exemples
sin x = O(x) lorsque x → 0.
sin x = O(1) lorsque x → + ∞,
x2 + x = O(x) lorsque x → 0,
x2 + x = O (x2) quand x → + ∞,
ln (x) = o(x) = O(x) lorsque x → + ∞.
Attention! La notation avec le signe égal "=" utilise une "fausse égalité": il est vrai que o(g(x)) = O (g (x)), mais faux que O(g(x)) = o (g (x)). De même, il est correct d'écrire "ln (x) = o(x) lorsque x → + ∞", mais la formule "o (x) = ln (x)" n'aurait aucun sens.
Plus d'exemples
O (1) = O(n) = O (n2) quand n → + ∞ (mais pas l'inverse, l'égalité est "fausse"),
O (n) + O (n2) = O (n2) quand n → +
O (O (n2)) = O (n2) quand n → +
Sur2)Sur3) = O (n5) quand n → +
Voici l'article de Wikipedia: https://en.wikipedia.org/wiki/Big_O_notation
La notation Big O est un moyen de décrire la rapidité d'exécution d'un algorithme en fonction d'un nombre arbitraire de paramètres d'entrée, que nous appellerons "n". C'est utile en informatique car différentes machines fonctionnent à différentes vitesses, et dire simplement qu'un algorithme prend 5 secondes ne vous en dit pas beaucoup car, même si vous utilisez un système avec un processeur octo-core à 4,5 Ghz, il se peut que je fonctionne un système à 800 MHz datant de 15 ans, ce qui pourrait prendre plus de temps, quel que soit l'algorithme. Ainsi, au lieu de spécifier la vitesse d'exécution d'un algorithme en termes de temps, nous définissons sa vitesse d'exécution en termes de nombre de paramètres d'entrée, ou "n". En décrivant les algorithmes de cette manière, nous sommes en mesure de comparer les vitesses des algorithmes sans avoir à prendre en compte la vitesse de l'ordinateur lui-même.
Je ne suis pas sûr que je contribue davantage au sujet, mais je pensais quand même partager: j'ai déjà trouvé cet article de blog pour avoir des explications et des exemples très utiles (bien que très basiques) sur Big O:
Grâce à des exemples, cela a permis d’obtenir les bases essentielles dans mon crâne en forme d’écaille de tortue. Je pense donc que c’est une jolie descente de 10 minutes de lecture pour vous mettre dans la bonne direction.
Vous voulez tout savoir sur le grand O? Moi aussi.
Donc, pour parler du grand O, je vais utiliser des mots qui n’ont qu’un temps. Un son par mot. Les petits mots sont rapides. Vous connaissez ces mots, et moi aussi. Nous allons utiliser des mots avec un seul son. Ils sont petits. Je suis sûr que vous saurez tous les mots que nous utiliserons!
Maintenant, parlons de travail. La plupart du temps, je n'aime pas le travail. Aimez-vous le travail? C'est peut-être le cas, mais je suis sûr que non.
Je n'aime pas aller au travail. Je n'aime pas passer du temps au travail. Si je pouvais me débrouiller, je voudrais juste jouer et faire des choses amusantes. Vous sentez-vous comme moi?
Maintenant, parfois, je dois aller au travail. C'est triste mais vrai. Donc, quand je suis au travail, j'ai une règle: j'essaie de faire moins de travail. Aussi près que possible d'aucun travail que je peux. Ensuite je vais jouer!
Voici donc la grande nouvelle: le grand O peut m'aider à ne pas travailler! Je peux jouer plus de temps, si je connais gros O. Moins de travail, plus de jeu! C'est ce que le grand O m'aide à faire.
Maintenant j'ai du travail. J'ai cette liste: un, deux, trois, quatre, cinq, six. Je dois ajouter toutes les choses dans cette liste.
Wow, je déteste le travail. Mais bon, je dois le faire. Alors voilà.
Un plus deux, c'est trois… plus trois, c'est six… et quatre c'est… je ne sais pas. Je me suis perdu. C'est trop difficile pour moi de le faire dans la tête. Je ne me soucie pas beaucoup de ce genre de travail.
Alors ne faisons pas le travail. Laissez-vous et moi penser à quel point c'est difficile. Combien de travail devrais-je faire pour ajouter six chiffres?
Voyons voir. Je dois ajouter un et deux, puis ajouter cela à trois, puis ajouter cela à quatre… Au total, je compte six ajouts. Je dois faire six ajouts pour résoudre ce problème.
Voici grand O, pour nous dire à quel point ces calculs sont difficiles.
Big O dit: nous devons faire six ajouts pour résoudre ce problème. Un ajout, pour chaque chose de un à six. Six petites tâches… chaque tâche est une addition.
Eh bien, je ne ferai pas le travail pour les ajouter maintenant. Mais je sais à quel point ce serait difficile. Ce serait six ajoute.
Oh non, maintenant j'ai plus de travail. Sheesh. Qui fait ce genre de choses?!
Maintenant, ils me demandent d’ajouter un à dix! Pourquoi devrais-je le faire? Je ne voulais pas en ajouter un à six. Pour ajouter de un à dix… eh bien… ce serait encore plus difficile!
Combien serait-ce plus difficile? Combien plus de travail devrais-je faire? Ai-je besoin de plus ou moins d'étapes?
Eh bien, je suppose que je devrais faire dix ajouts… un pour chaque chose de un à dix. Dix c'est plus que six. Il me faudrait travailler beaucoup plus pour ajouter de un à dix, de un à six!
Je ne veux pas ajouter pour le moment. Je veux juste réfléchir à la difficulté d’ajouter cela. Et j'espère jouer dès que possible.
Pour ajouter de un à six, c'est un travail. Mais voyez-vous, pour ajouter de un à dix, cela représente plus de travail?
Big O est votre ami et le mien. Big O nous aide à réfléchir sur tout le travail que nous devons faire pour pouvoir planifier. Et si nous sommes amis avec le grand O, il peut nous aider à choisir un travail moins difficile!
Maintenant, nous devons faire un nouveau travail. Oh non. Je n’aime pas du tout ce travail.
Le nouveau travail est: ajoutez toutes les choses de un à n.
Attendre! Qu'est ce que n? Est-ce que j'ai raté ça? Comment puis-je ajouter de un à n si vous ne me dites pas ce que n est?
Eh bien, je ne sais pas ce que n est. On ne m'a pas dit Étiez vous? Non? Tant pis. Nous ne pouvons donc pas faire le travail. Ouf.
Mais même si nous ne ferons pas le travail maintenant, nous pouvons deviner à quel point ce serait difficile, si nous savions que n. Il faudrait ajouter n choses, non? Bien sûr!
Maintenant, voici le grand O, et il nous dira combien ce travail est difficile. Il dit: ajouter toutes les choses de un à N, une par une, est O (n). Pour ajouter toutes ces choses, [je sais que je dois ajouter n fois.] [1] C'est grand O! Il nous dit combien il est difficile de faire un type de travail.
Pour moi, je pense au grand O comme à un grand chef lent. Il pense au travail, mais il ne le fait pas. Il pourrait dire: "Ce travail est rapide." Ou, il pourrait dire: "Ce travail est si lent et difficile!" Mais il ne fait pas le travail. Il ne fait que regarder le travail, puis il nous dit combien de temps cela pourrait prendre.
Je me soucie beaucoup pour le grand O. Pourquoi? Je n'aime pas travailler! Personne n'aime travailler. C'est pourquoi nous aimons tous le grand O! Il nous dit à quelle vitesse nous pouvons travailler. Il nous aide à réfléchir à la dureté du travail.
Euh oh, plus de travail. Maintenant, ne faisons pas le travail. Mais préparons-nous un plan pour le faire, étape par étape.
Ils nous ont donné un jeu de dix cartes. Ils sont tous mélangés: sept, quatre, deux, six… pas du tout. Et maintenant ... notre travail consiste à les trier.
Ergh. Cela a l'air de faire beaucoup de travail!
Comment pouvons-nous trier cette plate-forme? J'ai un plan.
Je regarderai chaque paire de cartes, paire par paire, à travers le jeu de cartes, du premier au dernier. Si la première carte d'une paire est grosse et que la carte suivante est petite, je les échange. Sinon, je vais à la paire suivante, et ainsi de suite ... et bientôt, le pont est terminé.
Lorsque le jeu est terminé, je demande: ai-je échangé des cartes dans ce laissez-passer? Si c'est le cas, je dois tout refaire une fois de plus, du haut.À un moment donné, à un moment donné, il n’y aura pas d’échange d’échanges, et notre genre de jeu sera terminé. Tant de travail!.
Eh bien, combien de travail faudrait-il pour trier les cartes avec ces règles?
J'ai dix cartes. Et, la plupart du temps, c’est-à-dire si je n’ai pas beaucoup de chance, je dois parcourir le paquet entier jusqu’à dix fois, avec jusqu’à dix échanges de cartes à chaque fois.
Big O, aidez-moi!.
Big O entre et dit: pour un jeu de n cartes, le trier de cette façon se fera en temps O (N carré).
Pourquoi dit-il n carré?.
Eh bien, vous savez que n carré est n fois n. Maintenant, je comprends: n cartes vérifiées, jusqu’à ce qu’il puisse être n fois dans le jeu de cartes. C'est deux boucles, chacune avec n étapes. C’est beaucoup de travail à faire. Beaucoup de travail, bien sûr!
Maintenant, quand le grand O dit que ça va prendre du travail O (n carré), il ne veut pas dire que n carré ajoute, sur le nez. Ce pourrait être un petit peu moins, dans certains cas. Mais dans le pire des cas, il faudra près de n étapes au carré pour trier le jeu.
Maintenant, voici où le grand O est notre ami.
Big O fait remarquer ceci: lorsque n grandit, lorsque nous trions les cartes, le travail devient BEAUCOUP PLUS DIFFICILE que le vieil emploi qui consiste simplement à ajouter des choses. Comment le savons nous?.
Eh bien, si n devient très gros, nous ne nous soucions pas de ce que nous pourrions ajouter à n ou n au carré.
Pour grand n, n carré est plus grand que n.
Big O nous dit que trier les choses est plus difficile que d’ajouter des choses. O (n carré) est plus que O(n) pour grand n. Cela signifie que si n devient très gros, trier un ensemble mixte de n éléments DOIT prendre plus de temps que d’ajouter n éléments mélangés.
Big O ne résout pas le travail pour nous. Big O nous dit combien le travail est dur.
J'ai un jeu de cartes. Je les ai triés. Vous avez aidé Merci.
Existe-t-il un moyen plus rapide de trier les cartes? Big O peut-il nous aider?.
Oui, il y a un moyen plus rapide! Il faut un certain temps pour apprendre, mais ça marche ... et ça marche assez vite. Vous pouvez l'essayer aussi, mais prenez votre temps à chaque étape et ne perdez pas votre place.
Dans cette nouvelle façon de trier un paquet, nous ne vérifions pas les paires de cartes comme nous le faisions il y a quelque temps. Voici vos nouvelles règles pour trier ce deck:.
Un: je choisis une carte dans la partie du jeu sur laquelle nous travaillons maintenant. Tu peux en choisir un pour moi si tu veux. (La première fois que nous faisons cela, “la partie de la plate-forme sur laquelle nous travaillons maintenant” est bien entendu la plate-forme entière.)
Deux: j'écarte le paquet sur la carte que vous avez choisie. Quel est cet évasement? Comment puis-je jouer? Eh bien, je vais de bas en haut, une à une, et je cherche une carte plus haute que la carte évasée.
Troisièmement: je vais à partir de la carte finale et je cherche une carte plus basse que la carte évasée.
Une fois que j'ai trouvé ces deux cartes, je les échange et cherche d'autres cartes à échanger. C’est-à-dire que je reviens à l’étape 2 et que la carte que vous avez choisie est plus évasée.
À un moment donné, cette boucle (de deux à trois) se terminera. Il se termine lorsque les deux moitiés de cette recherche se rencontrent à la carte évasée. Ensuite, nous venons d’évaser le pont avec la carte que vous avez choisie à la première étape. Maintenant, toutes les cartes au début sont plus basses que la carte évasée; et les cartes proches de la fin sont plus hautes que la carte évasée. Truc cool!.
Quatre (et c'est la partie amusante): J'ai deux petits ponts maintenant, un plus bas que la carte évasée et un plus haut. Maintenant, je vais à la première étape, sur chaque petit pont! C'est-à-dire que je commence à la première étape sur le premier petit pont et lorsque ce travail est terminé, je commence à la première étape sur le prochain petit pont.
Je divise le pont en plusieurs parties, je trie chaque partie, de plus en plus petite et, à un moment donné, je n'ai plus de travail à faire. Maintenant, cela peut sembler lent, avec toutes les règles. Mais croyez-moi, ce n'est pas lent du tout. C'est beaucoup moins de travail que la première façon de trier les choses!
Comment s'appelle cette sorte? C'est ce qu'on appelle le tri rapide! Cette sorte a été faite par un homme appelé C. A. R. Hoare et il a appelé Quick Sort. Maintenant, Quick Sort s'habitue tout le temps!
Quick Sort divise les grands ponts en petits. C’est-à-dire qu’il divise de grandes tâches en petites tâches.
Hmmm. Je pense qu’il peut y avoir une règle. Pour faire de petites tâches plus petites, séparez-les.
Ce genre est assez rapide. Comment rapide? Big O nous dit: ce type nécessite O (n log n) de travail, dans le cas moyen.
Est-ce plus ou moins rapide que le premier? Big O, aidez s'il vous plaît!
La première sorte était O (n carré). Mais le tri rapide est O (n log n). Vous savez que n log n est inférieur à n carré, pour big n, non? Eh bien, c'est comme ça que nous savons que le tri rapide est rapide!
Si vous devez trier un jeu de cartes, quel est le meilleur moyen? Eh bien, vous pouvez faire ce que vous voulez, mais je choisirais le tri rapide.
Pourquoi est-ce que je choisis le tri rapide? Je n'aime pas travailler, bien sûr! Je veux que le travail soit fait dès que je peux le faire.Comment savoir si le tri rapide représente moins de travail? Je sais que O (n log n) est inférieur à O (n carré). Les O sont plus petits, donc le tri rapide est moins de travail!.
Tu connais maintenant mon ami, Big O. Il nous aide à faire moins de travail. Et si vous connaissez le grand O, vous pouvez aussi faire moins de travail!.
Tu as appris tout ça avec moi! Tu es tellement intelligent! Merci beaucoup!
Maintenant que le travail est terminé, allons jouer!
[1]: Il y a un moyen de tricher et d'ajouter toutes les choses de un à n, tout à la fois. Un gamin nommé Gauss l’a découvert quand il avait huit ans. Je ne suis pas si malin que ça alors _
[1]: There is a way to cheat and add all the things from one to n, all at one time. Some kid named Gauss found this out when he was eight. I am not that smart though, so don't ask me how he did it .
Supposons que nous parlions d'un algorithmeA, qui devrait faire quelque chose avec un ensemble de données de taille n .
Alors O( <some expression X involving n> )
signifie, en anglais simple:
Si vous n’avez pas la chance d’exécuter A, il se peut que X(n) opérations soient exécutées sur Achevée.
En fait, certaines fonctions (p.ex. en tant que implémentations de X(n) ) ont tendance à se produire assez souvent. Celles-ci sont bien connues et faciles à comparer (exemples: 1
, Log N
, N
, N^2
, N!
, etc.)
En les comparant lorsque vous parlez deAet d’autres algorithmes, il est facile de classer les algorithmes en fonction du nombre d’opérations qu’ils peuvent (dans le cas le plus défavorable).
En général, notre objectif sera de trouver ou de structurer un algorithmeAde manière à ce qu'il ait une fonction X(n)
qui retourne le plus petit nombre possible.
J'ai un moyen plus simple de comprendre la complexité temporelle La métrique la plus courante pour calculer la complexité temporelle est la notation Big O. Cela supprime tous les facteurs constants de sorte que le temps d'exécution puisse être estimé par rapport à N lorsque N s'approche de l'infini. En général, vous pouvez penser comme ça:
statement;
Est constant. La durée d'exécution de l'instruction ne changera pas par rapport à N
for ( i = 0; i < N; i++ )
statement;
Est linéaire. La durée d'exécution de la boucle est directement proportionnelle à N. Lorsque N double, il en va de même pour la durée d'exécution.
for ( i = 0; i < N; i++ )
{
for ( j = 0; j < N; j++ )
statement;
}
Est quadratique. La durée d'exécution des deux boucles est proportionnelle au carré de N. Lorsque N double, la durée d'exécution augmente de N * N.
while ( low <= high )
{
mid = ( low + high ) / 2;
if ( target < list[mid] )
high = mid - 1;
else if ( target > list[mid] )
low = mid + 1;
else break;
}
Est logarithmique. Le temps d'exécution de l'algorithme est proportionnel au nombre de fois où N peut être divisé par 2. En effet, l'algorithme divise la zone de travail en deux à chaque itération.
void quicksort ( int list[], int left, int right )
{
int pivot = partition ( list, left, right );
quicksort ( list, left, pivot - 1 );
quicksort ( list, pivot + 1, right );
}
Est-ce que N * log (N). Le temps d'exécution est constitué de N boucles (itératives ou récursives) logarithmiques. L'algorithme est donc une combinaison de linéaire et de logarithmique.
En général, faire quelque chose avec chaque article dans une dimension est linéaire, faire quelque chose avec chaque article dans deux dimensions est quadratique et diviser la zone de travail en deux est logarithmique. Il existe d'autres mesures Big O telles que cubique, exponentielle et racine carrée, mais elles ne sont pas aussi communes. La notation Big O est décrite comme O () où est la mesure. L'algorithme quicksort serait décrit par O (N * log (N)).
Remarque: rien de tout cela n'a pris en compte les mesures optimales, moyennes et pires. Chacun aurait sa propre notation Big O. Notez également qu'il s'agit d'une explication TRÈS simpliste. Big O est le plus commun, mais il est aussi plus complexe que je l’ai montré. Il existe également d'autres notations telles que big oméga, little o et big theta. Vous ne les rencontrerez probablement pas en dehors d'un cours d'analyse d'algorithme.
Dites que vous commandez Harry Potter: Collection complète de 8 films [Blu-ray] d'Amazon et téléchargez la même collection de films en ligne au même moment. Vous voulez tester quelle méthode est la plus rapide. La livraison prend presque un jour et le téléchargement terminé environ 30 minutes plus tôt. Génial! C’est donc une course serrée.
Et si je commande plusieurs films Blu-ray comme Le Seigneur des Anneaux, Twilight, La Trilogie du Chevalier Noir, etc. et que je télécharge tous les films en ligne en même temps? Cette fois, la livraison prend encore un jour, mais le téléchargement en ligne prend 3 jours . Pour les achats en ligne, le nombre d’articles achetés (entrée) n’affecte pas le délai de livraison. La sortie est constante. Nous appelons cela O(1).
Pour le téléchargement en ligne, la durée de téléchargement est directement proportionnelle à la taille du fichier vidéo (entrée). Nous appelons cela O(n).
D'après les expériences, nous savons que les achats en ligne évoluent mieux que le téléchargement en ligne. Il est très important de comprendre la grande notation O car elle vous aide à analyser la scalabilité et la efficacité des algorithmes.
Remarque: La notation Big O représente le pire scénario d'un algorithme. Supposons que O(1) et O(n) sont les scénarios les plus défavorables de l'exemple ci-dessus.
Référence: http://carlcheo.com/compsci
Si vous avez une notion appropriée de l'infini dans votre tête, alors il y a une description très brève:
La notation Big O vous indique le coût de la résolution d'un problème infiniment grand.
Et en plus
Les facteurs constants sont négligeables
Si vous effectuez une mise à niveau vers un ordinateur pouvant exécuter votre algorithme deux fois plus rapidement, la grande notation O ne le remarquera pas. Les améliorations des facteurs constants sont trop petites pour même être remarquées dans l'échelle avec laquelle la grande notation O fonctionne. Notez que ceci est une partie intentionnelle de la conception de la grande notation O.
Bien que tout ce qui est "plus grand" qu'un facteur constant peut être détecté, cependant.
Si vous souhaitez effectuer des calculs dont la taille est "assez grande" pour être considérée comme approximativement à l'infini, la notation "Big O" correspond approximativement au coût de la résolution du problème.
Si ce qui précède n’a pas de sens, vous n’avez pas dans votre tête une notion intuitive compatible de l’infini, et vous devriez probablement ignorer tout ce qui précède; Le seul moyen que je connaisse pour rendre ces idées rigoureuses, ou pour les expliquer si elles ne sont pas intuitivement utiles, consiste à vous apprendre d'abord la grande notation O ou quelque chose de similaire. (Bien que, une fois que vous compreniez bien la grosse notation O à l'avenir, il serait peut-être utile de revenir sur ces idées)
Qu'est-ce qu'une explication anglaise simple de la notation «Big O»?
Note très rapide:
Le O dans "Big O" se réfère à "Ordre" (ou précisément "ordre de")
afin que vous puissiez avoir l’idée littérale qu’il sert à commander quelque chose pour les comparer.
"Big O" fait deux choses:
Notations
normalisée.Il y a sept notations les plus utilisées
1
step, c'est excellent, classé n ° 1logN
étapes, ce qui est bien, Ordonné n ° 2N
étapes, sa juste, Ordre n ° 3O(NlogN)
étapes, ce n'est pas bon, commande n ° 4N^2
étapes, c'est mauvais, numéro de commande 52^N
étapes, c'est horrible, commande n ° 6N!
étapes, c'est horrible, commande n ° 7Supposons que vous obteniez la notation O(N^2)
, non seulement vous êtes certain que la méthode prend N * N étapes pour accomplir une tâche, mais vous voyez également que son classement en O(NlogN)
n'est pas bon.
Veuillez noter la commande à la fin de la ligne, juste pour votre meilleure compréhension. Il y a plus de 7 notations si toutes les possibilités sont considérées.
Dans CS, l’ensemble des étapes permettant d’accomplir une tâche est appelé algorithme.
En terminologie, la notation Big O est utilisée pour décrire la performance ou la complexité d’un algorithme.
En outre, Big O établit le cas le plus défavorable ou mesure les étapes de la borne supérieure.
Vous pouvez vous référer à Big-Ω (Big-Omega) pour le meilleur des cas.
Notation Big-Ω (Big-Omega) (article) | Khan Academy
Résumé
"Big O" décrit la performance de l'algorithme et l'évalue.
ou l’adresser formellement, "Big O" classifie les algorithmes et standardise le processus de comparaison.
La manière la plus simple de la regarder (en anglais)
Nous essayons de voir comment le nombre de paramètres d'entrée affecte le temps d'exécution d'un algorithme. Si le temps d'exécution de votre application est proportionnel au nombre de paramètres d'entrée, on dit qu'il est dans Big O sur n.
La déclaration ci-dessus est un bon début mais pas tout à fait vrai.
Une explication plus précise (mathématique)
Supposer
n = nombre de paramètres d'entrée
T (n) = fonction réelle exprimant le temps d'exécution de l'algorithme en fonction de n
c = une constante
f (n) = fonction approximative exprimant le temps d'exécution de l'algorithme en fonction de n
Ensuite, en ce qui concerne Big O, l’approximation f(n) est considérée comme suffisamment bonne pour autant que la condition ci-dessous soit vraie.
lim T(n) ≤ c×f(n)
n→∞
L'équation est lue comme Lorsque n s'approche de l'infini, T sur n est inférieur ou égal à c fois f sur n.
En grosse notation O, ceci est écrit comme
T(n)∈O(n)
Ceci est lu comme T de n est dans le grand O de n.
Retour en anglais
D'après la définition mathématique ci-dessus, si vous dites que votre algorithme est un Big O sur n, cela signifie qu'il est fonction de n (nombre de paramètres d'entrée) ou plus rapide. Si votre algorithme est Big O sur n, il est aussi automatiquement le Big O sur n carré.
Big O of n signifie que mon algorithme fonctionne au moins aussi vite que cela. Vous ne pouvez pas regarder la notation Big O de votre algorithme et dire que c'est lent. Vous pouvez seulement dire que c'est rapide.
Vérifiez this out pour un tutoriel vidéo sur Big O de UC Berkley. C'est en fait un concept simple. Si vous entendez le professeur Shewchuck (alias professeur au niveau de Dieu) l'expliquer, vous direz "Oh, c'est tout ce que c'est!".
Ceci est une explication très simplifiée, mais j'espère qu'elle couvre les détails les plus importants.
Supposons que votre algorithme traitant du problème dépend de certains «facteurs», par exemple, fixons-le à N et à X.
Selon N et X, votre algorithme nécessitera certaines opérations, par exemple, dans le cas extrême, il s'agit d'opérations 3(N^2) + log(X)
.
Puisque Big-O ne se soucie pas trop du facteur constant (aka 3), le Big-O de votre algorithme est O(N^2 + log(X))
. En gros, il traduit "la quantité d'opérations dont votre algorithme a besoin dans le pire des cas évolue avec cela".
Définition: - la notation Big O est une notation qui indique le rendement d'un algorithme si les données saisies augmentent.
Lorsque nous parlons d'algorithmes, il y a 3 piliers importants Entrée, Sortie et Traitement de l'algorithme. Big O est une notation symbolique qui indique si la vitesse de traitement des algorithmes varie en fonction de la vitesse d'entrée des données.
Je vous encourage à voir cette vidéo sur youtube qui explique Big O Notation en détail avec des exemples de code.
Ainsi, par exemple, supposons qu'un algorithme prenne 5 enregistrements et que le temps nécessaire pour le traiter soit 27 secondes. Maintenant, si nous augmentons les enregistrements à 10, l'algorithme prend 105 secondes.
En termes simples, le temps pris est carré du nombre d'enregistrements. Nous pouvons le noter par O (n ^ 2). Cette représentation symbolique est appelée notation Big O.
Maintenant, veuillez noter que les unités peuvent être n'importe quoi dans les entrées, il peut s’agir d’octets, de bits, d’enregistrements, les performances peuvent être mesurées dans n’importe quelle unité telle que seconde, minutes, jours, etc. Donc, ce n'est pas l'unité exacte, mais plutôt la relation.
Par exemple, regardez ci-dessous la fonction "Fonction1" qui prend une collection et effectue le traitement sur le premier enregistrement. Maintenant, pour cette fonction, les performances seront les mêmes, que vous mettiez 1000, 10 000 ou 100 000 enregistrements. On peut donc le noter par O(1).
void Function1(List<string> data)
{
string str = data[0];
}
Voir maintenant la fonction ci-dessous "Function2 ()". Dans ce cas, le temps de traitement augmentera avec le nombre d'enregistrements. Nous pouvons noter cette performance d'algorithme en utilisant O(n).
void Function2(List<string> data)
{
foreach(string str in data)
{
if (str == "shiv")
{
return;
}
}
}
Lorsque nous voyons une notation Big O pour tout algorithme, nous pouvons les classer en trois catégories de performances: -
Donc, en regardant la notation Big O, nous classons les bonnes et les mauvaises zones pour les algorithmes.
Je vous recommande de regarder cette vidéo de 10 minutes qui traite de Big O avec un exemple de code.
Si je veux expliquer cela à un enfant de 6 ans, je vais commencer à dessiner des fonctions f(x) = x et f(x) = x ^ 2 par exemple et demander à un enfant quelle fonction sera être la fonction supérieure en haut de la page. Ensuite, nous allons procéder au dessin et voir que x ^ 2 gagne. "Qui gagne" est en fait la fonction qui se développe plus rapidement lorsque x tend vers l'infini. Ainsi, "la fonction x est dans Big O sur x ^ 2" signifie que x croît plus lentement que x ^ 2 lorsque x tend vers l'infini. La même chose peut être faite quand x tend à 0. Si nous dessinons ces deux fonctions pour x de 0 à 1 x sera une fonction supérieure, donc "la fonction x ^ 2 est dans Big O de x pour x tend vers 0 ". Quand l'enfant grandira, j'ajoute que vraiment Big O peut être une fonction qui ne grandit pas plus vite mais de la même manière qu'une fonction donnée. De plus, la constante est rejetée. Donc 2x est dans Big O de x.
J'ai trouvé une très bonne explication à propos de la grande notation en O, en particulier pour quelqu'un qui n'aime pas beaucoup les mathématiques.
https://rob-bell.net/2009/06/a-beginners-guide-to-big-o-notation/
La notation Big O est utilisée en informatique pour décrire la performance ou la complexité d'un algorithme. Big O décrit spécifiquement le dans le pire des cas, et peut être utilisé pour décrire le temps d’exécution requis ou l’espace utilisé (par exemple, en mémoire ou sur disque) par un algorithme.
Tous ceux qui ont lu Programming Pearls ou toute autre science informatique livres et qui n’a pas de connaissances de base en mathématiques auront heurté un mur quand ils ont atteint les chapitres qui mentionnent O (N log N) ou autre apparemment syntaxe folle. Espérons que cet article vous aidera à obtenir un compréhension des bases de Big O et Logarithms.
En tant que programmeur d’abord et deuxième mathématicien (ou peut-être troisième ou .__ quatrième), j’ai trouvé que la meilleure façon de comprendre complètement Big O était de produire des exemples en code. Donc, ci-dessous sont quelques ordres courants de croissance avec des descriptions et des exemples si possible.
O(1)
O (1) décrit un algorithme qui s'exécutera toujours dans le même temps (ou espace) quelle que soit la taille du jeu de données en entrée.
bool IsFirstElementNull(IList<string> elements) { return elements[0] == null; } O(N)
O(N)
O (N) décrit un algorithme dont les performances vont croître linéairement et en proportion directe avec la taille de l'ensemble de données d'entrée. L'exemple ci-dessous montre également comment Big O favorise la performance dans le pire des cas scénario; une chaîne correspondante peut être trouvée lors de toute itération du fichier La boucle for et la fonction reviendraient tôt, mais la notation Big O le sera supposons toujours la limite supérieure à laquelle l’algorithme effectuera le nombre maximum d'itérations.
bool ContainsValue(IList<string> elements, string value) { foreach (var element in elements) { if (element == value) return true; } return false; }
SUR2)
SUR2) représente un algorithme dont la performance est directement proportionnelle au carré de la taille de l'ensemble de données en entrée. C'est commun avec les algorithmes qui impliquent des itérations imbriquées sur les données ensemble. Des itérations imbriquées plus profondes donneront O (N3), SUR4) etc.
bool ContainsDuplicates(IList<string> elements) { for (var outer = 0; outer < elements.Count; outer++) { for (var inner = 0; inner < elements.Count; inner++) { // Don't compare with self if (outer == inner) continue; if (elements[outer] == elements[inner]) return true; } } return false; }
O (2N)
O (2N) désigne un algorithme dont la croissance double avec chaque ajout à l'ensemble de données d'entrée. La courbe de croissance d’un O (2N) fonction est exponentielle - commençant très peu profonde, puis remontant de façon météorique. Un exemple d'un O (2N) est le calcul récursif de Fibonacci Nombres:
int Fibonacci(int number) { if (number <= 1) return number; return Fibonacci(number - 2) + Fibonacci(number - 1); }
Logarithmes
Les logarithmes sont un peu plus difficiles à expliquer, alors je vais utiliser un point commun Exemple:
La recherche binaire est une technique utilisée pour rechercher des ensembles de données triés. Ça marche en sélectionnant l'élément central de l'ensemble de données, essentiellement le médiane et la compare à une valeur cible. Si les valeurs y correspondent retournera le succès. Si la valeur cible est supérieure à la valeur de l'élément de sonde, il prendra la moitié supérieure de l'ensemble de données et effectuer la même opération contre elle. De même, si la valeur cible est inférieur à la valeur de l'élément de sonde, il effectuera le opération contre la moitié inférieure. Il continuera à réduire de moitié les données définir à chaque itération jusqu'à ce que la valeur ait été trouvée ou jusqu'à ce qu'elle le puisse ne divise plus le jeu de données.
Ce type d'algorithme est décrit par O (log N). La réduction de moitié itérative des ensembles de données décrits dans l'exemple de recherche binaire produit une croissance courbe qui culmine au début et s’aplatit lentement avec la taille des ensembles de données augmentent, par exemple. un ensemble de données d'entrée contenant 10 éléments prend une seconde, un ensemble de données contenant 100 éléments prend deux secondes et un ensemble de données contenant 1000 éléments en prendra trois secondes. Doubler la taille du jeu de données en entrée a peu d’effet sur sa croissance après une seule itération de l’algorithme l’ensemble de données sera divisé par deux et donc sur un pied d'égalité avec un ensemble de données d'entrée moitié de la Taille. Cela rend les algorithmes comme la recherche binaire extrêmement efficaces lorsqu'il s'agit de grands ensembles de données.
Big O décrit une classe de fonctions.
Il décrit la rapidité avec laquelle les fonctions grandissent pour les grandes valeurs d’entrée.
Pour une fonction donnée f, O(f) décrit toutes les fonctions g(n) pour lesquelles vous pouvez trouver un n0 et une constante c afin que toutes les valeurs de g(n) avec n> = n0 sont inférieurs ou égaux à c * f (n)
En termes moins mathématiques, O(f) est un ensemble de fonctions . À savoir, toutes les fonctions qui, à partir de la valeur n0, augmentent plus ou moins vite que f.
Si f(n) = n alors
g (n) = 3n est dans O (f) .Parce que les facteurs constants importent peu h (n) = n + 1000 est dans O(f) car il pourrait être plus grand que toutes les valeurs 1000 mais pour le grand O, seules les entrées importantes importent.
Cependant, i(n) = n ^ 2 n'est pas dans O(f) car une fonction quadratique se développe plus rapidement qu'une fonction linéaire.
Big O est un moyen de représenter les limites supérieures de toute fonction. Nous l'utilisons généralement pour exprimer les limites supérieures d'une fonction qui indique le temps d'exécution d'un algorithme.
Ex: f(n) = 2 (n ^ 2) + 3n soit une fonction représentant le temps d'exécution d'un algorithme hypothétique, la notation Big-O donne essentiellement la limite supérieure de cette fonction qui est O (n ^ 2)
Cette notation nous indique essentiellement que, pour toute entrée 'n', le temps d'exécution ne sera pas supérieur à la valeur exprimée par la notation Big-O.
En outre, d'accord avec toutes les réponses détaillées ci-dessus. J'espère que cela t'aides !!
Big O en anglais ordinaire est comme <= (inférieur ou égal). Lorsque nous disons pour deux fonctions f et g, f = O(g), cela signifie que f <= g.
Cependant, cela ne signifie pas que pour n f(n) <= g (n). En réalité, cela signifie que f est inférieur ou égal à g en termes de croissance . Cela signifie que après un point f(n) <= c * g (n) si c est une constante . Et après un point signifie que pour tout n> = n0 où n0 est une autre constante .
Il représente la vitesse d'un algorithme dans the long run.
Pour prendre une analogie littérale, vous ne vous souciez pas de la vitesse à laquelle un coureur peut sprinter à un tiret de 100 m, voire à une course de 5 km. Vous vous souciez plus des marathoniens, et de préférence des ultra marathoniens (au-delà desquels l'analogie avec la course à pied s'effondre et vous devez revenir à la signification métaphorique de "long terme").
Vous pouvez arrêter de lire en toute sécurité ici.
J'ajoute cette réponse parce que je suis surpris de la mathématique et de la technicité du reste des réponses. La notion de "long terme" dans la première phrase est liée aux tâches de calcul fastidieuses et fastidieuses. Contrairement à l'exécution, qui est limitée par les capacités humaines, la réalisation de certains algorithmes peut prendre encore plus de plusieurs millions d'années.
Qu'en est-il de tous ces logarithmes mathématiques et polynômes? Il s'avère que les algorithmes sont intrinsèquement liés à ces termes mathématiques. Si vous mesurez la taille de tous les enfants du quartier, cela vous prendra autant de temps qu'il y a d'enfants. Ceci est intrinsèquement lié à la notion de n ^ 1 ou simplement n où n n'est rien de plus que le nombre d'enfants sur le bloc. Dans le cas de l'ultra-marathon, vous mesurez les hauteurs de tous les enfants de votre ville, mais vous devez alors ignorer les temps de déplacement et présumer qu'ils sont tous disponibles en ligne (sinon nous sautons d'avance sur l'explication actuelle).
Supposons alors que vous essayez d’arranger la liste des hauteurs d’enfants que vous avez dressée par ordre de taille, de la plus petite à la plus longue. S'il ne s'agit que des enfants de votre quartier, vous pouvez le regarder et en dresser la liste ordonnée. C'est l'analogie du «sprint», et nous ne nous intéressons vraiment pas aux sprints en informatique, car pourquoi utiliser un ordinateur quand on peut observer quelque chose?
Mais si vous organisiez la liste des hauteurs de tous les enfants de votre ville ou, mieux encore, de votre pays, vous constaterez que la façon dont vous le faites est intrinsèquement liée à la/mathématique log et n ^ 2 . Parcourez votre liste pour trouver le plus petit des enfants, écrivez son nom dans un cahier séparé et effacez-le du cahier original par intrinsèquement lié au mathématique n ^ 2. Si vous pensez organiser la moitié de votre cahier, puis l'autre moitié, puis combiner les résultats, vous obtiendrez une méthode qui est intrinsèquement liée au logarithme.
Enfin, supposons que vous deviez d’abord aller au magasin pour acheter un ruban à mesurer. Ceci est un exemple d'effort important pour les sprints brefs, comme mesurer les enfants du bloc, mais lorsque vous mesurez tous les enfants de la ville, vous pouvez ignorer ce coût en toute sécurité. C’est le lien intrinsèque avec la chute mathématique de termes polynomiaux d’ordre inférieur.
J'espère avoir expliqué que la notation big-O ne concerne que le long terme, que les mathématiques sont intrinsèquement liées aux méthodes de calcul et que l'abandon de termes mathématiques et d'autres simplifications sont liés au long terme dans un sens plutôt commun façon.
Une fois que vous vous en rendrez compte, vous constaterez que le big-O est vraiment très facile, car toutes les mathématiques difficiles du lycée abandonnent facilement. La seule partie difficile est d'analyser un algorithme pour identifier les termes mathématiques, mais avec une certaine pratique, vous pouvez commencer à supprimer des termes pendant l'analyse et ignorer en toute sécurité des fragments de l'algorithme pour se concentrer uniquement sur la partie pertinente pour le big-O. C'est à dire. vous devriez pouvoir observer la plupart des situations.
Heureux big-O-ing, c’était ce que je préférais en informatique - trouver quelque chose de bien plus simple que je ne le pensais, puis pouvoir me montrer lors des interviews de Google lorsque les non-initiés seraient intimidés, lol.
Qu'est-ce qu'une explication anglaise simple de la notation «Big O»?
Je voudrais souligner que le motif principal de la notation «Big O» est une chose, quand une taille d'entrée d'algorithme devient trop grande certaines parties (c.-à-d. Constantes, coefficients, termes) de l'équation décrivant la mesure de l'algorithme devient si insignifiante que nous ignorons eux. Les parties de l'équation qui survivent après avoir ignoré certaines de ses parties sont appelées la notation «Big O» de l'algorithme.
Donc, si la taille de l'entrée n'est PAS trop grande, l'idée de la notation «Big O» (limite supérieure) n'aura aucune importance.
Les disent que vous voulez quantifier la performance de l'algorithme suivant
int sumArray (int[] nums){
int sum=0; // taking initialization and assignments n=1
for(int i=0;nums.length;i++){
sum += nums[i]; // taking initialization and assignments n=1
}
return sum;
}
Dans l'algorithme ci-dessus, supposons que vous trouviez T(n)
comme suit (complexité temporelle):
T(n) = 2*n + 2
Pour trouver sa notation «Big O», nous devons considérer une très grande taille d’entrée:
n= 1,000,000 -> T(1,000,000) = 2,000,002
n=1,000,000,000 -> T(1,000,000,000) = 2,000,000,002
n=10,000,000,000 -> T(10,000,000,000) = 20,000,000,002
Permet de donner à cette entrée des entrées similaires pour une autre fonction F(n) = n
n= 1,000,000 -> F(1,000,000) = 1,000,000
n=1,000,000,000 -> F(1,000,000,000) = 1,000,000,000
n=10,000,000,000 -> F(10,000,000,000) = 10,000,000,000
Comme vous pouvez le constater avec la taille d'entrée get too big le T(n)
égal ou proche de F(n)
, la constante 2
et le coefficient 2
deviennent trop insignifiants.
O(T(n)) = F(n)
O(T(n)) = n
Nous disons que le grand O de T(n)
est n
et que la notation est O(T(n)) = n
, c'est la limite supérieure de T(n)
, car n
obtient too big . la même étape s'applique pour d'autres algorithmes.
TLDR: Big O explique les performances d'un algorithme en termes mathématiques.
Les algorithmes les plus lents tendent à fonctionner de n à la puissance de x ou plus, en fonction de la profondeur de celle-ci, alors que les algorithmes plus rapides tels que la recherche binaire sont exécutés à O (log n), ce qui permet de l'exécuter plus rapidement à mesure que le jeu de données augmente. Big O pourrait être expliqué avec d'autres termes utilisant n, ou même pas avec n (ie: O(1)).
On peut calculer Big O En regardant les lignes les plus complexes de l'algorithme.
Big O peut être surprenant avec des ensembles de données petits ou non triés, car n algorithmes de complexité tels que la recherche binaire peuvent être lents pour des ensembles plus petits ou non triés. Pour un exemple simple d'utilisation courante de la recherche linéaire par rapport à la recherche binaire, regardez mon exemple JavaScript:
https://codepen.io/serdarsenay/pen/XELWqN?editors=1011 (algorithmes écrits ci-dessous)
function lineerSearch() {
init();
var t = timer('lineerSearch benchmark');
var input = this.event.target.value;
for(var i = 0;i<unsortedhaystack.length - 1;i++) {
if (unsortedhaystack[i] === input) {
document.getElementById('result').innerHTML = 'result is... "' + unsortedhaystack[i] + '", on index: ' + i + ' of the unsorted array. Found' + ' within ' + i + ' iterations';
console.log(document.getElementById('result').innerHTML);
t.stop();
return unsortedhaystack[i];
}
}
}
function binarySearch () {
init();
sortHaystack();
var t = timer('binarySearch benchmark');
var firstIndex = 0;
var lastIndex = haystack.length-1;
var input = this.event.target.value;
//currently point in the half of the array
var currentIndex = (haystack.length-1)/2 | 0;
var iterations = 0;
while (firstIndex <= lastIndex) {
currentIndex = (firstIndex + lastIndex)/2 | 0;
iterations++;
if (haystack[currentIndex] < input) {
firstIndex = currentIndex + 1;
//console.log(currentIndex + " added, fI:"+firstIndex+", lI: "+lastIndex);
} else if (haystack[currentIndex] > input) {
lastIndex = currentIndex - 1;
//console.log(currentIndex + " substracted, fI:"+firstIndex+", lI: "+lastIndex);
} else {
document.getElementById('result').innerHTML = 'result is... "' + haystack[currentIndex] + '", on index: ' + currentIndex + ' of the sorted array. Found' + ' within ' + iterations + ' iterations';
console.log(document.getElementById('result').innerHTML);
t.stop();
return true;
}
}
}
_ {Big O - Point de vue économique.
Mon mot anglais préféré pour décrire ce concept est le prix que vous payez pour une tâche à mesure qu’elle s’agrandit.
Considérez cela comme des coûts récurrents au lieu de coûts fixes que vous auriez à payer au début. Les coûts fixes deviennent négligeables dans l’ensemble, car les coûts ne font que croître et s’additionner. Nous voulons mesurer à quelle vitesse ils grandiraient et combien de temps ils ajouteraient par rapport à la matière première que nous donnons à la configuration - à la taille du problème.
Toutefois, si les coûts de configuration initiaux sont élevés et que vous ne produisez qu'une petite quantité du produit, vous voudrez peut-être examiner ces coûts initiaux - ils sont également appelés constantes.
Étant donné que ces constantes importent peu à la longue, ce langage nous permet de discuter de tâches au-delà du type d'infrastructure sur laquelle nous l'exécutons. Ainsi, les usines peuvent être n'importe où et les ouvriers peuvent être n'importe qui - tout est sauve. Mais la taille de l'usine et le nombre de travailleurs seraient les éléments que nous pourrions varier à long terme à mesure que vos intrants et vos produits augmentent.
Par conséquent, cela devient une approximation globale de combien vous devrez dépenser pour exécuter quelque chose. Puisque heure et espace sont les quantités économiques (c’est-à-dire qu’elles sont limitées), elles peuvent toutes deux être exprimées à l’aide de cette langue.
Notes techniques: Quelques exemples de complexité temporelle - O(n) signifie généralement que si un problème est de taille 'n', je dois au moins tout voir. O (log n) signifie généralement que je réduit de moitié la taille du problème et que je vérifie et répète jusqu'à ce que la tâche soit terminée. O (n ^ 2) signifie que je dois regarder des paires de choses (comme des poignées de main lors d'une fête entre n personnes).