web-dev-qa-db-fra.com

Big O Question sur un algorithme avec (n ^ 2 + n) / 2 taux de croissance

Je pose cette question parce que je suis confus au sujet d'un aspect concernant la notation du grand O.

J'utilise le livre, Structures de données et abstractions avec Java de Frank Carrano. Dans le chapitre sur "l'efficacité des algorithmes", il montre l'algorithme suivant:

int sum = 0, i = 1, j = 1
for (i = 1 to n) {
    for (j = 1 to i)
        sum = sum + 1
}

Il décrit initialement cet algorithme comme ayant un taux de croissance de (n2+ N)/2. Ce qui semble intuitif.

Cependant, il est alors précisé que (n2+ N)/2 se comporte comme n2 lorsque n est grand. Dans le même paragraphe, il déclare (n2+ N)/2 se comporte également comme n2/2. Il l'utilise pour classer l'algorithme ci-dessus comme O (n2).

Je comprends cela (n2+ N)/2 est similaire à n2/2 car en pourcentage, n fait peu de différence. Ce que je ne comprends pas, c'est pourquoi (n2+ N)/2 et n2 sont similaires lorsque n est grand.

Par exemple, si n = 1 000 0:

(n^2 + n) / 2 =  500000500000 (5.000005e+11)
(n^2) / 2     =  500000000000 (5e+11)
(n^2)         = 1000000000000 (1e+12)

Ce dernier n'est pas du tout similaire. En fait, bien évidemment, c'est deux fois autant que celui du milieu. Alors, comment Frank Carrano peut-il dire qu'ils sont similaires? De plus, comment l'algorithme est-il classé O (n2). En regardant cette boucle intérieure, je dirais que c'était n2 + n/2

16
Andrew S

Lors du calcul de la complexité Big-O d'un algorithme, l'élément affiché est le facteur qui contribue le plus à l'augmentation du temps d'exécution si le nombre d'éléments sur lesquels vous exécutez l'algorithme augmente.

Si vous avez un algorithme d'une complexité de (n^2 + n)/2 Et que vous doublez le nombre d'éléments, alors la constante 2 N'affecte pas l'augmentation du temps d'exécution, le terme n provoque un doublement du temps d'exécution et le terme n^2 entraîne une multiplication par quatre du temps d'exécution.
Comme le terme n^2 A la plus grande contribution, la complexité Big-O est O(n^2).

38

La définition est que

f(n) = O(g(n))

s'il existe une constante C> 0 telle que, pour tout n supérieur à certains n_0, on ait

|f(n)| <= C * |g(n)|

Cela est clairement vrai pour f(n) = n ^ 2 et g(n) = 1/2 n ^ 2, où la constante C doit être 2. Il est également facile de voir que c'est vrai pour f(n) = n ^ 2 et g(n) = 1/2 (n ^ 2 + n).

10
cfh

Lorsque vous parlez de complexité, vous ne vous intéressez qu'aux changements de facteurs temporels en fonction du nombre d'éléments (n).

En tant que tel, vous pouvez supprimer tout facteur constant (comme le 2 Ici).

Cela vous laisse avec O(n^2 + n).

Maintenant, pour une taille raisonnable n, le produit, c'est-à-dire n * n, Sera beaucoup plus gros que simplement n, c'est la raison pour laquelle vous êtes également autorisé à ignorer cette partie , ce qui vous laisse en effet avec une complexité finale de O(n^2).

C'est vrai, pour les petits nombres, il y aura une différence significative, mais cela devient plus marginalement plus votre n est grand.

6
Mario

Ce n'est pas que "(n² + n)/2 se comporte comme n² quand n est grand", c'est que (n² + n)/2 pousse comme lorsque n augmente.

Par exemple, lorsque n passe de 1 000 à 1 000 000

(n² + n) / 2  increases from  500500 to  500000500000
(n²) / 2      increases from  500000 to  500000000000
(n²)          increases from 1000000 to 1000000000000

De même, lorsque n passe de 1 000 000 à 1 000 000 000

(n² + n) / 2  increases from  500000500000 to  500000000500000000
(n²) / 2      increases from  500000000000 to  500000000000000000
(n²)          increases from 1000000000000 to 1000000000000000000

Ils se développent de la même manière, c'est ce dont parle Big O Notation.

Si vous tracé (n² + n)/2 et n²/2 sur Wolfram Alpha , ils sont tellement similaires qu'ils sont difficiles à distinguer par n = 100. Si vous tracez les trois sur Wolfram Alpha , vous voyez deux lignes séparées par un facteur constant de 2.

3
ShadSterling

Il semble que vous ayez besoin de travailler un peu plus sur la notation big O. Comme cette notation est pratique, elle est très trompeuse en raison de l'utilisation d'un signe égal, qui n'est pas utilisé ici pour désigner l'égalité des fonctions.

Comme vous le savez, cette notation exprime une comparaison asymptotique des fonctions, et écrire f = O (g) signifie que f (n) croît au plus vite as g (n) as n va à l'infini. Une façon simple de traduire cela est de dire que la fonction f/g est bornée. Mais bien sûr, nous devons prendre soin des endroits où g est nul et nous nous retrouvons avec la définition la plus robuste que vous pouvez lire presque partout .

Cette notation s'avère très pratique pour l'informatique - c'est pourquoi elle est si répandue - mais elle doit être manipulée avec précaution car le signe égal que nous y voyons ne dénote pas une égalité de fonctions. Cela revient à dire que 2 = 5 mod 3 n'implique pas que 2 = 5 et si vous aimez l'algèbre, vous pouvez réellement comprendre le grande notation O comme quelque chose modulo d'égalité.

Maintenant, pour revenir à votre question spécifique, il est totalement inutile de calculer quelques valeurs numériques et de les comparer: si grand soit un million, il ne tient pas compte du comportement asymptotique. Il serait plus utile de tracer le rapport des fonctions f (n) = n (n-1)/2 et g (n) = n² - mais dans ce cas particulier, nous pouvons facilement voir que f (n)/g (n) est plus petit que 1/2 if n> 0 ce qui implique que f = O (g).

Pour améliorer votre compréhension de la notation, vous devez

  • Travaillez avec une définition claire, pas une impression floue basée sur les choses étant similaires - comme vous venez de le ressentir, une telle impression floue ne fonctionne pas bien.

  • Prenez le temps de trouver des exemples en détail. Si vous travaillez aussi peu que cinq exemples en une semaine, cela suffira à améliorer votre confiance. C'est un effort qui en vaut vraiment la peine.


Note algébrique Si [~ # ~] a [~ # ~] est l'algèbre de toutes les fonctions Ν → Ν et [~ # ~] c [~ # ~] la sous-algèbre des fonctions bornées, étant donné une fonction f l'ensemble des fonctions appartenant à O (f) est a [~ # ~] c [~ # ~] - sous-module de [~ # ~] a [~ # ~], et les règles de calcul sur la grande notation O décrivent simplement comment [~ # ~] a [~ # ~] fonctionne sur ces sous-modules . Ainsi, l'égalité que nous voyons est une égalité de [~ # ~] c [~ # ~] - des sous-modules de [~ # ~] a [~ # ~], ce n'est qu'un autre type de module.

Je pense que vous comprenez mal ce que signifie la grande notation O.

Lorsque vous voyez O (N ^ 2), cela signifie essentiellement: lorsque le problème devient 10 fois plus important, le temps de le résoudre sera: 10 ^ 2 = 100 fois plus grand.

Frappons 1000 et 10000 dans votre équation: 1000: (1000 ^ 2 + 1000)/2 = 500500 10000: (10000 ^ 2 + 10000)/2 = 50005000

50005000/500500 = 99,91

Ainsi, alors que le N est devenu 10 fois plus gros, les solutions sont devenues 100 fois plus grandes. D'où il se comporte: O (N ^ 2)

1
Pieter B

si n était un 1,000,000 alors

(n^2 + n) / 2  =  500000500000  (5.00001E+11)
(n^2) / 2      =  500000000000  (5E+11)
(n^2)          = 1000000000000  (1E+12)

1000000000000.00 quoi?

Bien que la complexité nous donne un moyen de prédire un coût réel (secondes ou octets selon que nous parlons de complexité temporelle ou d'espace), elle ne nous donne pas un certain nombre de secondes, ni aucune autre unité particulière.

Cela nous donne une certaine proportion.

Si un algorithme doit faire quelque chose n² fois, alors il faudra n² × c pour une valeur de c qui est la durée de chaque itération.

Si un algorithme doit faire quelque chose n² ÷ 2 fois, alors il faudra n² × c pour une valeur de c qui est deux fois plus longue que chaque itération.

De toute façon, le temps pris est toujours proportionnel à n².

Maintenant, ces facteurs constants ne sont pas quelque chose que nous pouvons simplement ignorer; en effet, vous pouvez avoir le cas où un algorithme avec une complexité O (n²) fait mieux qu'un algorithme avec une complexité O(n), car si nous travaillons sur un petit nombre d'éléments, l'impact du les facteurs de consentement sont plus importants et peuvent submerger d'autres préoccupations. (En effet, même O (n!) est identique à O(1) pour des valeurs suffisamment faibles de n).

Mais ce n'est pas ce dont la complexité nous parle.

En pratique, il existe plusieurs façons d'améliorer les performances d'un algorithme:

  1. Améliorez l'efficacité de chaque itération: O (n²) s'exécute toujours en n² × c secondes, mais c est plus petit.
  2. Réduisez le nombre de cas vus: O (n²) s'exécute toujours en n² × c secondes, mais n est plus petit.
  3. Remplacez l'algorithme par un qui a les mêmes résultats, mais une complexité moindre: par exemple. si nous pouvions remplacer quelque chose O (n²) par quelque chose O (n log n) et donc passer de n² × c₀ secondes à (n log n) × c₁ secondes.

Ou pour voir les choses d'une autre manière, nous avons f(n)×c secondes prises et vous pouvez améliorer les performances en réduisant c, en réduisant n ou en réduisant ce que f renvoie pour un n donné.

Le premier que nous pouvons faire par quelques micro-opts à l'intérieur d'une boucle, ou en utilisant un meilleur matériel. Cela donnera toujours une amélioration.

La seconde, nous pouvons le faire en identifiant peut-être un cas où nous pouvons court-circuiter l'algorithme avant que tout soit examiné, ou filtrer certaines données qui ne seront pas significatives. Cela ne donnera pas d'amélioration si le coût de cette opération l'emporte sur le gain, mais ce sera généralement une amélioration plus importante que dans le premier cas, en particulier avec un grand n.

Le troisième, nous pouvons le faire en utilisant entièrement un algorithme différent. Un exemple classique serait de remplacer un tri à bulles par un tri rapide. Avec un petit nombre d'éléments, nous pouvons avoir aggravé les choses (si c₁ est supérieur à c₀), mais cela permet généralement les gains les plus importants, en particulier avec un n très grand.

Dans la pratique, les mesures de complexité nous permettent de raisonner sur les différences entre les algorithmes précisément parce qu'elles ignorent la question de savoir comment la réduction de n ou c aidera, de se concentrer sur l'examen de f()

1
Jon Hanna

Facteur constant

Le point de la grande notation O est que vous pouvez choisir un facteur constant arbitrairement grand de sorte que O(function(n)) est toujours plus grand que la fonction C * (n). Si l'algorithme A est un milliards de fois plus lentement que l'algorithme B, alors ils ont la même complexité O, tant que cette différence ne grandit pas lorsque n devient arbitrairement grand.

Supposons un facteur constant de 1000000 pour illustrer le concept - il est un million de fois plus grand que nécessaire, mais cela illustre le fait qu'ils sont considérés comme non pertinents.

(n ^ 2 + n)/2 "s'inscrit à l'intérieur" O (n ^ 2) car pour tout n, quelle que soit sa taille, (n ^ 2 + n)/2 <1000000 * n ^ 2.

(n ^ 2 + n)/2 "ne correspond pas" à un ensemble plus petit, par ex. O(n) car pour certaines valeurs (n ^ 2 + n)/2> 1000000 * n.

Les facteurs constants peuvent être arbitrairement grands - un algorithme avec un temps d'exécution de n années a une complexité O(n) qui est "meilleure" qu'un algorithme avec un temps d'exécution de n * log (n) microsecondes.

0
Peteris

Big-O est tout au sujet de la "complexité" d'un algorithme. Si vous avez deux algorithmes, et que l'un prend n^2*k Secondes pour s'exécuter, et l'autre prend n^2*j Secondes pour s'exécuter, alors vous pouvez discuter de celui qui est le mieux, et vous pourrez peut-être faire quelques optimisations intéressantes pour essayer d'affecter k ou j, mais ces deux algorithmes sont très lents comparés à un algorithme qui prend n*m pour s'exécuter. Peu importe la taille des constantes k ou j, pour une entrée suffisamment grande, l'algorithme n*m Gagnera toujours, même si m est assez grand.

Nous appelons donc les deux premiers algorithmes O(n^2), et nous appelons le second O(n). Il divise bien le monde en classes d'algorithmes. C'est à cela que sert le big-O. C'est comme diviser les véhicules en voitures, camions et bus, etc. Il y a beaucoup de variations entre les voitures, et vous pouvez passer toute la journée à vous demander si une Prius est meilleure qu'une Chevy Volt, mais à la fin de la journée si vous besoin de mettre 12 personnes en un, alors c'est un argument plutôt insensé. :)

0
Jason Walton