Wie ist dieser Sortieralgorithmus Θ (n³) und nicht Θ (n²) im ungünstigsten Fall?


52

Ich habe gerade angefangen, einen Kurs über Datenstrukturen und Algorithmen zu belegen, und mein Lehrassistent hat uns den folgenden Pseudocode zum Sortieren einer Reihe von ganzen Zahlen gegeben:

void F3() {
    for (int i = 1; i < n; i++) {
        if (A[i-1] > A[i]) {
            swap(i-1, i)
            i = 0
        }
    }
}

Es mag nicht klar sein, aber hier ist n die Größe des Arrays A, das wir sortieren wollen.

In jedem Fall erklärte die Lehrassistentin der Klasse, dass sich dieser Algorithmus in Θ(n3) Zeit befindet (schlimmster Fall, glaube ich), aber egal, wie oft ich ihn mit einem umgekehrt sortierten Array durcharbeite, scheint es so für mich sollte es Θ(n2) und nicht Θ(n3) .

Kann mir jemand erklären, warum dies und nicht Θ (n ^ 2) ist ?Θ ( n 2 )Θ(n3)Θ(n2)


Möglicherweise interessieren Sie sich für einen strukturierten Analyseansatz . versuche selbst einen Beweis zu finden!
Raphael

Einfach umsetzen und messen, um sich selbst zu überzeugen. Ein Array mit 10.000 Elementen in umgekehrter Reihenfolge sollte viele Minuten dauern, und ein Array mit 20.000 Elementen in umgekehrter Reihenfolge sollte etwa achtmal länger dauern.
gnasher729

@ gnasher729 Du liegst nicht falsch, aber meine Lösung ist anders: Wenn du versuchst zu beweisen, dass dein gebunden ist, wirst du ausnahmslos scheitern, was dir sagt, dass etwas nicht stimmt. (Natürlich kann man beides tun. Plotten / Anpassen ist definitiv schneller, um Hypothesen abzulehnen, aber weniger zuverlässig . Solange Sie eine formale / strukturierte Analyse durchführen, kann dies keinen Schaden anrichten. Wenn Sie sich auf Plots stützen, beginnen Probleme.)O(n2)
Raphael

1
wegen der i = 0Aussage
njzk2

Antworten:


60

Dieser Algorithmus kann so umgeschrieben werden

  1. Scannen ASie, bis Sie eine Inversion finden .
  2. Wenn Sie eines finden, tauschen Sie es aus und beginnen Sie von vorne.
  3. Wenn es keine gibt, beenden Sie.

Jetzt kann es höchstens Inversionen geben, und Sie benötigen einen linearen Zeitscan, um jede zu finden - die Laufzeit im ungünstigsten Fall ist also . Ein wunderschönes Lehrbeispiel, da es den Pattern-Matching-Ansatz auslöst, dem viele erliegen!(n2)Θ(n2)Θ(n3)

Hinweis: Man muss ein wenig vorsichtig sein: Einige Inversionen erscheinen zu früh, andere zu spät, so dass es an sich nicht trivial ist, dass sich die Kosten wie angegeben summieren (für die Untergrenze). Sie müssen auch beachten, dass Swaps niemals neue Inversionen einführen . Eine detailliertere Analyse des Falls mit dem invers sortierten Array ergibt dann so etwas wie den quadratischen Fall der Gaußschen Formel.

Wie @ gnasher729 treffend bemerkt, ist es leicht zu erkennen, dass die Laufzeit im schlimmsten Fall indem die Laufzeit beim Sortieren der Eingabe analysiert wird. (obwohl diese Eingabe wahrscheinlich nicht der schlechteste Fall ist).[ 1 , 2 , , n , 2 n , 2 n - 1 , , n + 1 ]Ω(n3)[1,2,,n,2n,2n1,,n+1]

Achtung: Gehen Sie nicht davon aus, dass ein Array mit umgekehrter Sortierung für alle Sortieralgorithmen die schlechteste Eingabe ist. Das hängt vom Algorithmus ab. Es gibt einige Sortieralgorithmen, bei denen ein Array mit umgekehrter Sortierung nicht der schlechteste Fall ist und möglicherweise sogar dem besten nahe kommt.


14
Wenn Sie ein Array verwenden, bei dem die erste Hälfte aus den Zahlen 1 bis n / 2 in aufsteigender Reihenfolge besteht und die zweite Hälfte aus n bis n / 2 + 1 in umgekehrter Reihenfolge besteht, ist es offensichtlich, dass Sie mindestens n / 2 benötigen Schritte, um jede Inversion zu finden, und es wird ungefähr (n / 2) ^ 2/2 von ihnen geben. Und das ist höchstwahrscheinlich nicht der schlimmste Fall.
gnasher729

@AnthonyRossello Dies ist ein Standardergebnis (in der Kombinatorik von Permutationen). Kurz gesagt, zählen Sie die Anzahl der Inversionen in dem umgekehrt sortierten Array (ist es offensichtlich, dass dies der schlimmste Fall ist?); Es ist eine Gauß-Summe.
Raphael

Man muss bedenken, dass egal was, Teilsummen von immer , es ist nur der Koeffizient, der schnell fällt: (beachte den ziemlich großen Koeffizienten ). Das Problem ist, kümmert sich nicht um Koeffizienten. Θ ( n α + 1 ) Σ n k = 0 K α ~ 1Θ(nα)Θ(nα+1)1k=0nkα1α+1nα+1 Θ1α+1Θ
yo '19.

2
@yo 'Und das bezieht sich auf die Antwort (oder die Frage) wie?
Raphael

7

Eine alternative Art, darüber nachzudenken, ist, wie der Maximalwert von iwird, bevor er zurückgesetzt wird. Wie sich herausstellt, ist es daher einfacher zu überlegen, wie sich die vorherige Sortierreihenfolge Aauf die Laufzeit des Algorithmus auswirkt.

Beachten Sie insbesondere, dass idas Array [A[0], ..., A[N-1]]in aufsteigender Reihenfolge sortiert wird , wenn Sie seinen neuen Maximalwert als N bezeichnen .

Was passiert also, wenn wir das Element A[N]zum Mix hinzufügen ?

Die Mathematik:

wir an, es passt an Position . Dann benötigen wir Schleifeniterationen (die ich ), um es an die Stelle , -Iterationen, um es an die Stelle , und im Allgemeinen: N Schritte N - 1 N + ( N - 1 ) N - 2pNNstepsN1N+(N1)N2

stepsN(pN)=N+(N1)+(N2)++(pN+1)=12(N(N+1)pN(pN+1))

Für ein zufällig sortiertes Array nimmt die Gleichverteilung auf für jedes mit: { 0 , 1 , , N } NpN{0,1,,N}N

E(stepsN(pN))=a=1NP(pN=a)stepsN(a)=a=1N1N12(N(N+1)a(a+1))=12(N(N+1)13(N+1)(N+2))=13(N21)=Θ(N2)

Die Summe kann nach Faulhabers Formel oder dem Wolfram-Alpha-Link unten angezeigt werden.

Für ein umgekehrt sortiertes Array ist für alle und wir erhalten:NpN=0N

stepsN(pN)=12N(N+1)

Genau, es dauert streng länger als jeder andere Wert von .pN

Für ein bereits sortiertes Array ist und , wobei die Terme niedrigerer Ordnung relevant werden.Schritte N ( p N ) = 0pN=NstepsN(pN)=0

Gesamtzeit:

Um die Gesamtzeit zu erhalten, fassen wir die Schritte über das ganze bis . (Wenn wir sehr vorsichtig wären, würden wir die Swap- und Loop-Iterationen zusammenfassen und uns um die Start- und Endbedingungen kümmern, aber es ist einigermaßen einfach zu erkennen, dass sie in den meisten Fällen nicht zur Komplexität beitragen.) .N

Und wieder mit Linearität der Erwartung und Faulhabers Formel:

Expected Total Steps=E(N=1nstepsN(pN))=N=1nE(stepsN(pN))=Θ(n3)

Wenn aus irgendeinem Grund nicht (z. B. ist die Verteilung der Arrays, die wir betrachten, bereits sehr nah an der Sortierung), muss dies nicht immer der der Fall sein. Es sind jedoch sehr spezifische Verteilungen für , um dies zu erreichen!Θ ( N 2 ) p NstepsN(pN)Θ(N2)pN

Relevante Lektüre:


@Raphael - danke für die vorgeschlagenen Verbesserungen, ich habe ein wenig mehr Details hinzugefügt. Nun, die Zufallsvariablen sind das (aus , der Satz von Ordnungen von ), daher werden die Erwartungen technisch über Ω ΩpiΩAΩ
David E

Different ; Ich meinte die Landau. Ω
Raphael

3

Haftungsausschluss:

Dies ist kein Beweis (es scheint, dass einige Leute denken, ich hätte es gepostet, als wäre es das). Dies ist nur ein kleines Experiment, das das OP durchführen könnte, um seine oder ihre Zweifel an der Zuweisung zu klären:

Egal, wie oft ich es mit einem umgekehrt sortierten Array durchgehe, es scheint mir, dass es und nicht .Θ ( n 3 )Θ(n2)Θ(n3)

Mit solch einem einfachen Code sollte der Unterschied zwischen und nicht schwer zu erkennen sein, und in vielen praktischen Fällen ist dies ein nützlicher Ansatz, um Ahnungen zu überprüfen oder Erwartungen anzupassen.Θ ( n 3 )Θ(n2)Θ(n3)


@Raphael hat Ihre Frage bereits beantwortet, aber nur zum Spaß, indem er die Ausgabe dieses Programms unter Verwendung dieses Gnuplot-Skripts an und Exponentenwerte von und und die folgenden Diagramme erstellte ( Die erste ist eine normale Skala und die zweite ist eine logarithmische Skala.2,99796166833222 2,99223727692339f(x)=axb+cx2.997961668332222.99223727692339

normal loglog

Ich hoffe, das hilft¨


2
Sie können jede Funktion an diese Werte anpassen. Siehe auch hier .
Raphael

3
@Raphael Wenn Sie nicht auf diese Weise auswählen möchten, können Sie keine Funktion anpassen (zum Beispiel können Sie keine konstante Funktion mit einer angemessenen Genauigkeit anpassen). Dies ist kein Beweis, aber es gibt bereits eine Antwort, die eine Skizze liefert. Zum Nutzen kann ich Ihren eigenen Beitrag zitieren, den Sie verlinkt haben: "Ich muss zustimmen, dass dies ein sehr nützlicher Ansatz ist, der manchmal sogar zu wenig genutzt wird." Darüber hinaus meinte der OP, es sollte und nicht . Warum also nicht experimentieren und sehen, ob seine Vermutung richtig war? Fortsetzung Θ ( n 3 )Θ(n2)Θ(n3)
dtldarek

2
Dies liefert den Beweis, dass der Algorithmus aber die Frage lautet warum . Es geht um eine Erklärung des Phänomens, nicht um eine Bestätigung. Θ(n3)
David Richerby

2
@DavidRicherby Bedeutet das, dass diese Antwort nicht nützlich ist?
Dtldarek

3
@Magicsowon Es ist eine Frage- und Antwortseite, kein Forum. Wir suchen nach Antworten auf die Frage und nicht nach Diskussionen.
David Richerby

3

Angenommen, Sie haben ein Array.

array a[10] = {10,8,9,6,7,4,5,2,3,0,1}

Ihr Algorithmus führt Folgendes aus

Scan(1) - Swap (10,8) => {8,10,9,6,7,4,5,2,3,0,1}  //keep looking at "10"
Scan(2) - Swap (10,9) => {8,9,10,6,7,4,5,2,3,0,1}
...
Scan(10) - Swap(10,1) => {8,9,6,7,4,5,2,3,0,1,10}

Grundsätzlich wird das höchste Element an das Ende des Arrays verschoben, und dabei wird bei jedem Scan erneut gestartet. Dabei werden nur die O(n^2)Bewegungen für dieses eine Element ausgeführt. Da es jedoch n Elemente gibt , müssen wir dieses nMal wiederholen . Dies ist kein formaler Beweis, aber es hilft auf "unformale" Weise zu verstehen, warum die Laufzeit ist O(n^3).


4
Was bedeutet dies gegenüber anderen Antworten? Eine Erklärung, was der Algorithmus macht, wurde bereits gegeben, und Ihre Argumentation für die Laufzeit ist bestenfalls lückenhaft. (Der schlimmste Fall verhält sich nicht linear!)
Raphael

2
Manchmal ist es sinnvoll, dieselbe Idee auf verschiedene Arten zu erklären (mit Formalismus; mit einem einfachen Beispiel, um "die Intuition zu pumpen"), besonders wenn die Person, die die Frage stellt, neu auf dem Gebiet ist. Mir scheint, dies fügt hinzu, dass es auf eine Weise präsentiert wird, die die Intuition unterstützen könnte.
DW

Da ich eine Antwort auf meinen Kommentar in einem Flag erhalten habe (mach das nicht!): "Worst-case verhält sich nicht linear!" - Ich meine die algebraischen Eigenschaften des Worst-Case-Operators. Grob gesagt, verwenden Sie WorstCase (1 + ... + n) "=" WorstCase (1) + ... + WorstCase (n), aber diese Identität gilt nicht.
Raphael

1
Ich bin neu in diesem Bereich und eine Erklärung mit einem konkreten, ausgesprochenen Beispiel hat mir definitiv geholfen, mich ein Bild über das Problem zu machen. Jetzt macht die akzeptierte Lösung für mich mehr Sinn.
Vaer-k

0

Die Logik scheint die Elemente im Array in aufsteigender Reihenfolge zu sortieren.

Angenommen, die kleinste Zahl befindet sich am Ende des Arrays (a [n]). Damit es an den richtigen Ort kommt, sind (n + (n-1) + (n-2) + ... 3 + 2 + 1) Operationen erforderlich. = O (n2).

Für ein einzelnes Element im Array sind O (n2) -Ops erforderlich. Für Elemente ist es also O (n3).


5
Was bedeutet dies gegenüber anderen Antworten? Eine Erklärung, was der Algorithmus macht, wurde bereits gegeben, und Ihre Argumentation für die Laufzeit ist bestenfalls lückenhaft. (Der schlimmste Fall verhält sich nicht linear!)
Raphael

Tolle Erklärung. Dies bietet eine andere, intuitivere Perspektive auf das Problem, die in anderen Antworten nicht erläutert wird. (Ganz zu schweigen von der kurzen und leicht verständlichen Zeit.)
2501

1
@ 2501 Nein, es ist falsch. Wenn Sie diese "Intuition" für den Dijkstra-Algorithmus verwenden, erhalten Sie eine quadratische Laufzeit (in der Anzahl der Knoten), was falsch ist.
Raphael

@Raphael Nein, es ist richtig, wie in der Antwort erklärt. Diese Erklärung gilt für diesen Algorithmus, nicht für andere. Während es für sie falsch sein kann, beweist diese Behauptung nicht, dass es für dieses falsch ist.
2501

@Raphael Ich habe die Erklärung in der akzeptierten Antwort nicht verstanden. Also habe ich das gelöst und versucht, es in einfachen Worten ohne technische Begriffe zu erklären. Das ist also für Mitglieder wie mich, die die akzeptierte Antwort nicht verstehen konnten. Ich bin froh, dass jemand dies nützlich findet.
mk ..
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.