web-dev-qa-db-fra.com

Complexité O (N log N) - Similaire à linéaire?

Je pense donc que je vais être enterré pour avoir posé une question aussi banale, mais je suis un peu confus à propos de quelque chose.

J'ai implémenté quicksort dans Java et C et je faisais quelques comparissons de base. Le graphique se présente sous la forme de deux lignes droites, le C étant 4 ms plus rapide que l'homologue Java sur 100 000 entiers aléatoires.

Results

Le code de mes tests peut être trouvé ici;

Android-benchmarks

Je ne savais pas à quoi ressemblerait une ligne (n log n) mais je ne pensais pas qu'elle serait droite. Je voulais juste vérifier que c'est le résultat attendu et que je ne devrais pas essayer de trouver une erreur dans mon code.

J'ai collé la formule dans Excel et pour la base 10, il semble que ce soit une ligne droite avec un coude au début. Est-ce parce que la différence entre log (n) et log (n + 1) augmente linéairement?

Merci,

Gav

72
gav

Agrandir le graphique et vous verrez que O (n logn) n'est pas tout à fait une ligne droite. Mais oui, c'est assez proche d'un comportement linéaire. Pour voir pourquoi, il suffit de prendre le logarithme de quelques très grands nombres.

Par exemple (base 10):

log(1000000) = 6
log(1000000000) = 9
…

Ainsi, pour trier 1 000 000 de nombres, un tri O (n logn) ajoute un maigre facteur 6 (ou juste un peu plus car la plupart des algorithmes de tri dépendent des logarithmes en base 2). Pas beaucoup.

En fait, ce facteur de log est donc extraordinairement petit que pour la plupart des ordres de grandeur, les algorithmes O (n logn) établis surpassent les algorithmes de temps linéaire. Un exemple frappant est la création d'une structure de données de tableau de suffixes.

Un cas simple m'a récemment mordu quand j'ai essayé d'améliorer un tri rapide des chaînes courtes en utilisant le tri radix . Il s'avère que, pour les chaînes courtes, ce tri radix (temps linéaire) était plus rapide que le tri rapide, mais il y avait un point de basculement pour les chaînes encore relativement courtes, car le tri radix dépend de manière cruciale de la longueur des chaînes que vous triez.

76
Konrad Rudolph

Pour info, le tri rapide est en fait O (n ^ 2), mais avec un cas moyen d'O (nlogn)

Pour info, il y a une assez grande différence entre O(n) et O (nlogn). C'est pourquoi il n'est pas bornable par O(n) pour toute constante) .

Pour une démonstration graphique, voir:

O(n) vs O(nlogn)

11
groundhog

Pour encore plus de plaisir dans une veine similaire, essayez de tracer le temps pris par n opérations sur la norme structure de données d'ensemble disjoint . Il s'est avéré être asymptotiquement n α ( n ) où α ( n ) est l'inverse de fonction Ackermann (bien que votre manuel d'algorithmes habituel ne montre probablement qu'une limite de n log log n ou éventuellement n log * n ). Pour tout type de nombre que vous rencontrerez probablement comme taille d'entrée, α ( n ) ≤ 5 (et en effet log * n ≤ 5), bien qu'il se rapproche de l'infini asymptotiquement.

Ce que je suppose que vous pouvez apprendre de cela, c'est que si la complexité asymptotique est un outil très utile pour penser aux algorithmes, ce n'est pas tout à fait la même chose que l'efficacité pratique.

5
  1. Habituellement, les algorithmes O (n * log (n)) ont une implémentation logarithmique à 2 bases.
  2. Pour n = 1024, log (1024) = 10, donc n * log (n) = 1024 * 10 = 10240 calculs, une augmentation d'un ordre de grandeur.

Ainsi, O (n * log (n)) est similaire à linéaire uniquement pour une petite quantité de données.

Astuce: n'oubliez pas que quicksort se comporte très bien sur des données aléatoires et qu'il ne s'agit pas d'un algorithme O (n * log (n)).

3
Nick Dandoulakis

Toutes les données peuvent être tracées sur une ligne si les axes sont choisis correctement :-)

Wikipedia dit que Big-O est le pire des cas (c'est-à-dire f(x) est O(N) signifie f(x) est "délimité au-dessus" par N ) https://en.wikipedia.org/wiki/Big_O_notation

Voici un bel ensemble de graphiques décrivant les différences entre les différentes fonctions communes: http://science.slc.edu/~jmarshall/courses/2002/spring/cs50/BigO/

La dérivée de log (x) est 1/x. C'est à quelle vitesse log (x) augmente à mesure que x augmente. Il n'est pas linéaire, bien qu'il puisse ressembler à une ligne droite car il se plie si lentement. En pensant à O (log (n)), je le pense comme O (N ^ 0 +), c'est-à-dire la plus petite puissance de N qui n'est pas une constante, car toute puissance constante positive de N la dépassera finalement. Ce n'est pas précis à 100%, donc les professeurs se fâcheront contre vous si vous l'expliquez de cette façon.

La différence entre les journaux de deux bases différentes est un multiplicateur constant. Recherchez la formule de conversion des journaux entre deux bases: (sous "changement de base" ici: https://en.wikipedia.org/wiki/Logarithm ) L'astuce consiste à traiter k et b comme des constantes.

Dans la pratique, il y aura normalement des hoquets dans toutes les données que vous tracez. Il y aura des différences dans les choses en dehors de votre programme (quelque chose qui s'introduit dans le processeur avant votre programme, le cache manque, etc.). Il faut de nombreuses exécutions pour obtenir des données fiables. Les constantes sont le plus grand ennemi d'essayer d'appliquer la notation Big O à l'exécution réelle. Un algorithme O(N) avec une constante élevée peut être plus lent qu'un algorithme O (N ^ 2) pour un N. suffisamment petit.

2
nobodyImportant

log (N) est (très) grossièrement le nombre de chiffres de N. Donc, pour la plupart, il y a peu de différence entre log (n) et log (n + 1)

1
James Curran

Essayez de tracer une ligne linéaire réelle au-dessus et vous verrez la petite augmentation. Notez que la valeur Y à 50 000 est inférieure à la valeur 1/2 Y à 100 000.

C'est là, mais c'est petit. C'est pourquoi O(nlog(n)) est si bon!

0
Chris Harris