O (N log N) Komplexität - Ähnlich wie linear?

78

Ich glaube, ich werde begraben, weil ich so eine triviale Frage gestellt habe, aber ich bin etwas verwirrt über etwas.

Ich habe Quicksort in Java und C implementiert und einige grundlegende Vergleiche durchgeführt. Der Graph wurde als zwei gerade Linien dargestellt, wobei das C über 100.000 zufällige Ganzzahlen 4 ms schneller war als das Java-Gegenstück.

Ergebnisse

Den Code für meine Tests finden Sie hier;

Android-Benchmarks

Ich war mir nicht sicher, wie eine (n log n) Linie aussehen würde, aber ich dachte nicht, dass sie gerade sein würde. Ich wollte nur überprüfen, ob dies das erwartete Ergebnis ist und ob ich nicht versuchen sollte, einen Fehler in meinem Code zu finden.

Ich habe die Formel in Excel eingefügt und für Basis 10 scheint es eine gerade Linie mit einem Knick am Anfang zu sein. Liegt das daran, dass der Unterschied zwischen log (n) und log (n + 1) linear zunimmt?

Vielen Dank,

Gav

gav
quelle
1
Die Google - Bildsuche scheint bei Suchvorgängen wie "n log n" überraschend gut zu sein.
Tom Hawtin - Tackline
1
Die Java-Zeile oben sieht für mich nicht direkt aus.
Welpe
In der Tat ähnlich, weshalb es als "linearithmische Zeit" bezeichnet wird
msanford

Antworten:

81

Wenn Sie das Diagramm vergrößern, sehen Sie, dass O (n logn) keine gerade Linie ist. Aber ja, es kommt dem linearen Verhalten ziemlich nahe. Um zu sehen warum, nehmen Sie einfach den Logarithmus einiger sehr großer Zahlen.

Zum Beispiel (Basis 10):

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

Um 1.000.000 Zahlen zu sortieren, fügt eine O (n logn) -Sortierung einen dürftigen Faktor 6 hinzu (oder nur ein bisschen mehr, da die meisten Sortieralgorithmen von Logarithmen der Basis 2 abhängen). Nicht sehr viel.

Tatsächlich ist dieser Log-Faktor so außerordentlich klein, dass etablierte O (n logn) -Algorithmen für die meisten Größenordnungen lineare Zeitalgorithmen übertreffen. Ein prominentes Beispiel ist die Erstellung einer Suffix-Array-Datenstruktur.

Ein einfacher Fall hat mich kürzlich gebissen, als ich versuchte, eine schnelle Sortierung von kurzen Saiten durch Verwendung der Radix-Sortierung zu verbessern . Es stellte sich heraus, dass diese (lineare Zeit-) Radix-Sortierung für kurze Zeichenfolgen schneller war als die Quicksortierung, aber es gab einen Wendepunkt für noch relativ kurze Zeichenfolgen, da die Radix-Sortierung entscheidend von der Länge der von Ihnen sortierten Zeichenfolgen abhängt.

Konrad Rudolph
quelle
1
Gute Sorten tendieren dazu, einen linearen Algorithmus zu wählen, sobald sie sich in ausreichend kleine Stücke geteilt und erobert haben. Wie klein genau ist, ist eine Frage des Benchmarking (reale Daten).
Tom Hawtin - Tackline
2
Tom: Ich bin mir nicht sicher, was genau du mit linear meinst. Oft machen Sortieralgorithmen das Gegenteil, indem sie O (n ^ 2) -Sortierungen wie die Einfügungssortierung für kleine Teile verwenden, da ihr konstanter Faktor so klein ist, dass selbst die quadratische Laufzeit die nlogn-Sortierung übertrifft. Auf der anderen Seite verwendet Introsort eine Strategie, um aus zu tiefen Rekursionen auszubrechen - aber auch dies ist nirgendwo linear, sondern tauscht nur einen quadradischen Worst-Case gegen O (n logn) -Verhalten aus.
Konrad Rudolph
11

Zu Ihrer Information, Quicksort ist eigentlich O (n ^ 2), aber mit einem durchschnittlichen Fall von O (nlogn)

Zu Ihrer Information, es gibt einen ziemlich großen Unterschied zwischen O (n) und O (nlogn). Deshalb ist es für keine Konstante an O (n) gebunden.

Eine grafische Demonstration finden Sie unter:

O (n) gegen O (nlogn)

Murmeltier
quelle
2
a) Ohne Angabe wird O () normalerweise verwendet, um die erwartete (durchschnittliche) Komplexität zu bezeichnen. b) Die O () - Notation enthält keine konstanten Faktoren, daher sind O (n) und O (2n) gleich. Da log (n) nahezu konstant ist (für große Zahlen im Vergleich zu n), kann gesagt werden, dass O (n) und O (n log (n)) nahezu gleich sind. Sie sollten gezeichnet haben: wolframalpha.com/input/?i=plot+7+x%2C+x+log+x+from+1+to+1500
Timmmm
13
Dies ist im Allgemeinen nicht wahr. Die Big O-Notation bezeichnet typischerweise die asymptotische Komplexität im schlimmsten Fall und notiert eine Funktion, die über der Komplexität des Algorithmus liegt. O (n) nähert sich nicht O (nlogn) an, obwohl O (nlogn) aus praktischen Gründen relativ gut und nicht viel schlechter ist. Die schnellste Leistung im schlimmsten Fall ist sicherlich keine Seltenheit. Versuchen Sie, die Einträge im Wörterbuch schnell zu sortieren, wenn Sie mir nicht glauben.
Murmeltier
Ich weiß nicht, dass es einen großen Unterschied gibt. Besonders wenn man es mit der nächsten Bestellung vergleicht $ O (n ^ 2) $. i.sli.mg/9zXUQR.png
Isopycnal Oscillation
5

Um noch mehr Spaß in ähnlicher Weise zu haben, versuchen Sie, die von n Operationen benötigte Zeit in der Standarddatenstruktur für disjunkte Mengen zu zeichnen . Es wurde gezeigt, dass es asymptotisch n  α ( n ) ist, wobei α ( n ) die Umkehrung der Ackermann-Funktion ist (obwohl Ihr übliches Lehrbuch für Algorithmen wahrscheinlich nur eine Grenze von n log log n oder möglicherweise n  log *  n zeigt ). Für jede Art von Zahl, auf die Sie wahrscheinlich als Eingabegröße stoßen, gilt α ( n ) ≤ 5 (und tatsächlich log *  n  ≤ 5), obwohl sie sich asymptotisch der Unendlichkeit nähert.

Ich nehme an, Sie können daraus lernen, dass asymptotische Komplexität zwar ein sehr nützliches Werkzeug zum Nachdenken über Algorithmen ist, aber nicht ganz dasselbe ist wie praktische Effizienz.

Jouni K. Seppänen
quelle
3
  1. Normalerweise haben die O (n * log (n)) - Algorithmen eine logarithmische Implementierung mit 2 Basen.
  2. Für n = 1024 ist log (1024) = 10, also n * log (n) = 1024 * 10 = 10240 Berechnungen, eine Zunahme um eine Größenordnung.

Daher ist O (n * log (n)) nur für eine kleine Datenmenge linear ähnlich.

Tipp: Vergessen Sie nicht, dass sich Quicksort bei zufälligen Daten sehr gut verhält und dass es sich nicht um einen O (n * log (n)) - Algorithmus handelt.

Nick Dandoulakis
quelle
3
Alle Logarithmen sind gleich, sie unterscheiden sich nur im Maßstab. Ich sehe also nicht die Bedeutung Ihrer ersten Aussage. Ich stimme auch Ihrer Aussage nicht zu, dass O (n log n) nur für eine kleine Datenmenge linear ähnlich ist. Wieder einmal ist es eine Skalierungssache. Schauen Sie sich als Gegenbeispiel einfach die Grafiken in der ursprünglichen Frage an.
Waxwing
Ich meine nicht grafisch ähnlich (zu einer geraden Linie), sondern zeitlich komplex. Die O (n logn) -Zeit kann leicht eine Größenordnung größer sein als O (n). Wenn die Graphen die Algorithmen O (n logn) und O (n) vergleichen würden, würden Sie sehen, was ich meine. :) Wenn das N immer größer wird, bewegt sich das O (n logn) * zur nächsten logarithimischen Skala.
Nick Dandoulakis
1
Im Durchschnitt ist Quicksort ein O (n log n) -Algorithmus.
Manu
Der erste Punkt ist falsch, wie von @waxwing angegeben. Durch Teilen der Grenzen verschiedener Basen können Sie nachweisen, dass das Ändern der Protokollbasis die Komplexität nur durch einen konstanten Faktor beeinflusst - die Protokollbasis ist aus Komplexitätssicht irrelevant. Mit zunehmender Anzahl der Elemente werden die Formen der linearen und logarithmisch linearen Linien ähnlicher und nicht weniger.
Mateor
2

Alle Daten können auf einer Linie dargestellt werden, wenn die Achsen richtig gewählt sind :-)

Laut Wikipedia ist Big-O der schlechteste Fall (dh f (x) ist O (N) bedeutet, dass f (x) durch N "oben" begrenzt ist) https://en.wikipedia.org/wiki/Big_O_notation

Hier ist eine schöne Reihe von Grafiken, die die Unterschiede zwischen verschiedenen allgemeinen Funktionen darstellen: http://science.slc.edu/~jmarshall/courses/2002/spring/cs50/BigO/

Die Ableitung von log (x) ist 1 / x. So schnell steigt log (x) mit zunehmendem x. Es ist nicht linear, obwohl es wie eine gerade Linie aussehen kann, weil es sich so langsam biegt. Wenn ich an O (log (n)) denke, stelle ich es mir als O (N ^ 0 +) vor, dh die kleinste Potenz von N, die keine Konstante ist, da jede positive konstante Potenz von N sie schließlich überholen wird. Es ist nicht 100% genau, also werden Professoren sauer auf Sie, wenn Sie es so erklären.

Der Unterschied zwischen Protokollen zweier verschiedener Basen ist ein konstanter Multiplikator. Schlagen Sie die Formel zum Konvertieren von Protokollen zwischen zwei Basen nach: (hier unter "Basiswechsel": https://en.wikipedia.org/wiki/Logarithm ) Der Trick besteht darin, k und b als Konstanten zu behandeln.

In der Praxis treten bei den von Ihnen geplotteten Daten normalerweise Probleme auf. Es wird Unterschiede in Dingen außerhalb Ihres Programms geben (etwas, das vor Ihrem Programm in die CPU wechselt, Cache-Fehler usw.). Es dauert viele Läufe, um zuverlässige Daten zu erhalten. Konstanten sind der größte Feind beim Versuch, die Big O-Notation auf die tatsächliche Laufzeit anzuwenden. Ein O (N) -Algorithmus mit einer hohen Konstante kann langsamer sein als ein O (N ^ 2) -Algorithmus für ausreichend kleine N.

niemandWichtig
quelle
(Ich gehe davon aus, dass Sie eine gerade Linie gemeint haben und nicht "Linie" als allgemeinen Begriff für Kurve.) Ich würde kaufen, dass jede kontinuierlich differenzierbare, reelle Funktion einer reellen Variablen auf einer geraden Linie dargestellt werden kann, wenn die Achsen werden korrekt mit nur moderaten Spielereien wie doppelten Achsenwerten ausgewählt (erforderlich, es sei denn, es handelt sich um eine Eins-zu-Eins-Funktion), aber "irgendwelche Daten"? Ich denke, das ist eine Strecke. Wie wäre es mit der Funktion, die für alle rationalen Zahlen Null ist, aber für alle irrationalen Zahlen eins. (Es ist als Dirichlet-Funktion bekannt und eine echte mathematische Funktion.)
Sarah G
1

log (N) ist (sehr) ungefähr die Anzahl der Ziffern in N. Daher gibt es zum größten Teil kaum einen Unterschied zwischen log (n) und log (n + 1).

James Curran
quelle
3
log-base- 10 ist ungefähr die Anzahl der Stellen in N (vorausgesetzt, Sie verwenden die Dezimaldarstellung). Die meisten Sortier- / Suchalgorithmen würden log-base-2 verwenden, was zwar proportional zu log-base-10 ist (so dass das große O immer noch gilt), aber nicht dem entspricht, was Sie beschreiben :-)
paxdiablo
Eine andere Möglichkeit zu sagen ist, dass log-base-2 ungefähr die Anzahl der Stellen in N ist, wenn sie binär geschrieben sind, auch bekannt als die Anzahl der Bits, die zur Darstellung von N erforderlich sind.
Tyler McHenry
0

Versuchen Sie, eine tatsächliche lineare Linie darüber zu zeichnen, und Sie werden den kleinen Anstieg sehen. Beachten Sie, dass der Y-Wert bei 50.0000 kleiner ist als der 1/2 Y-Wert bei 100.000.

Es ist da, aber es ist klein. Deshalb ist O (nlog (n)) so gut!

Chris Harris
quelle
Es ist immer noch ein verdammter Anblick besser als O (n ^ 2).
Paxdiablo