2009-12-04 1 views
8

Es ist eine bekannte Sache mit Quicksort, dass, wenn der Datensatz in oder fast in der Sortierreihenfolge ist, die Leistung auf schreckliche Weise abnimmt. In diesem Fall ist die normalerweise sehr langsame Einfügesortierung die beste Wahl. Die Frage ist zu wissen, wann was zu verwenden ist.Analysealgorithmus vorsortieren?

Gibt es einen Algorithmus zum Durchlaufen eines Datensatzes, zum Anwenden eines Vergleichsfaktors und zum Zurückgeben eines Berichts darüber, wie nahe der Datensatz in der Sortierreihenfolge sein soll? Ich bevorzuge Delphi/Pascal, aber ich kann andere Sprachen lesen, wenn das Beispiel nicht übermäßig komplex ist.

+1

Diese Langsamkeit von Quicksort mit vorsortierten Sequenzen ist nur ein Problem, AFAIK, wenn die Implementierung in Bezug auf die Wahl eines Pivot-Elements zu einfach ist. Siehe zum Beispiel http://www.cprogramming.com/tutorial/computersciencetheory/quicksort.html. – Dirk

Antwort

9

Wie Sie erwarten würden, ist ziemlich viel darüber nachzudenken. Die Median-of-Three-Methode bedeutet, dass das Worst-Case-Verhalten von Quicksort für sortierte Daten nicht auftritt, sondern für weniger offensichtliche Fälle.

Introsort ist ziemlich aufregend, da es Quicksorts quadratischen Worst-Case insgesamt vermeidet. Statt Ihrer natürlichen Frage: "Wie stelle ich fest, dass die Daten fast sortiert sind", fragt sie sich selbst, wie sie sich entwickelt, "dauert das zu lange?". Wenn die Antwort ja lautet, wechselt sie von Quicksort zu Heapsort.

Timsort kombiniert Merge-Sort mit Insertion-Sort und führt sehr gut bei sortierten oder umgekehrt sortierten Daten und bei Daten, die sortierte oder umgekehrt sortierte Subsets enthalten.

Also wahrscheinlich die Antwort auf Ihre Frage ist, "Sie brauchen keine Pre-Pass-Analyse, Sie brauchen einen adaptiven Sortieralgorithmus".

+0

+1 für timsort link –

+0

+1 wow, timsort sieht ziemlich ordentlich aus. – wowest

0

Ich habe noch keine Vorsortierungsanalyse gehört, aber ich bin der Meinung, dass Sie, wenn Sie den Datensatz zur Analyse durchgehen, bereits die Leistung Ihrer gesamten Sortierzeit reduzieren.

+2

Das ist ein guter Punkt, aber wenn der Analysedurchlauf O (n) ist, wird er nicht die asymptotische Sortierzeit dominieren. Und wenn es helfen kann, eine O (n^2) Worst-Case-Sortierzeit zu vermeiden, könnte dies ein Nettovorteil bei der Sortierzeit für große Datensätze sein. – ddaa

+1

@ddaa: Das gilt für Vergleichssorten, aber O (n) Sortierung ist mit Radix Sort oder Bucket Sort möglich. Wenn wir diese Algorithmen einbeziehen, könnte die Sortierzeit von der Analysezeit dominiert werden ... –

+1

@Jason: Sie würden diese Analyse nicht für Daten durchführen, die Sie im Begriff sind zu sortieren. Die Frage ist über die Wahl zwischen Quicksort und Insertion Sortierung, und Sie haben vor, weder ... –

0

Eine mögliche Lösung besteht darin, das erste, das letzte und das mittlere Element im aktuellen Sortierbereich (während der QuickSort-Operation) zu verwenden und das mittlere Element als Pivotelement auszuwählen.

+0

Ihr bester Fall ist immer noch O (N log N), wobei Insertion sort für fast sortierte Daten O (N) ist. – wowest

0

Um vollständig zu analysieren für den Zweck der Entscheidung, welcher Algorithmus zu verwenden ist, werden Sie fast die Arbeit des Sortierens tun. Sie könnten so etwas wie die Werte zu einem kleinen Prozentsatz zufälliger, aber zunehmender Indizes überprüfen (dh eine kleine Stichprobe der Elemente analysieren).

3

Es gibt auch SmoothSort, das anscheinend ziemlich schwierig zu implementieren ist, aber es variiert zwischen O (N log N) und O (N), je nachdem, wie sortiert die Daten beginnen sollen.

http://en.wikipedia.org/wiki/Smoothsort

Lange heikel PDF: http://www.cs.utexas.edu/users/EWD/ewd07xx/EWD796a.PDF

Allerdings, wenn Ihre Daten wirklich sehr groß ist, und Sie müssen es seriell zuzugreifen, ist mergesort wahrscheinlich das beste. Es ist immer O (N log N) und es hat ausgezeichnete "Lokalität" -Eigenschaften.

0

Sie müssten immer noch alle Datensätze durchlaufen, um festzustellen, ob sie sortiert sind oder nicht. Um die Leistung zu verbessern, beginnen Sie mit dem ersten Datensatz und führen Sie den Rest durch, bis Sie entweder etwas nicht richtig sortiert bemerken oder das Ende erreichen Die Liste. Wenn Sie einen Fehler finden, dann sortieren Sie nur die Artikel von dieser Position bis zum Ende (da der Anfang der Liste bereits sortiert ist).

Bei jedem Artikel im zweiten Teil, sehen Sie, ob der Artikel < als das letzte Element im ersten Teil ist und wenn ja, verwenden Sie eine Einfügesortierung nur in den ersten Teil. Ansonsten Quicksort gegen alle anderen Gegenstände im zweiten Teil. Auf diese Weise ist die Sortierung für den jeweiligen Fall optimiert.

0

QuickSort beng ein Problem nur, wenn die Datenmenge sehr groß ist und bereits größtenteils sortiert, würde ich die folgenden Heuristiken verwenden (eine vollständige geblasen Lösung pending):

  • Kümmern Sie sich nicht, wenn Datensatz Größe ist unterhalb der Schwelle.

  • Wenn Sie einen schnellen (indizierten) Zugriff auf Datensätze (Elemente) haben, nehmen Sie ein Beispiel mit 1 Datensatz in jedem N-Datensatz und sehen Sie, ob sie bereits sortiert sind. Sollte für eine kleine Probe schnell genug sein und Sie können dann entscheiden, ob Sie schnell sortieren möchten oder nicht.

+0

aber das Beispiel schlägt fehl, wenn 1 Datensatz in jedem N sortiert ist, aber +1 Datensatz in jedem N nicht. Sie müssen möglicherweise noch jeden Datensatz lesen, um zu sehen, ob EINER von ihnen nicht abgetastet ist. – skamradt

+0

Einverstanden, aber es gibt statistisch sehr wenig Chance, dass die Stichprobe so viel von der Gesamtbevölkerung abweichen würde, vor allem wenn Sie ein wenig N zufäll. –

0

Um einen konzeptionellen Punkt zu machen, den die Leute noch nicht gemacht haben: Quicksort ist ein common-sense Divide-and-Conquer-Algorithmus mit einem offensichtlichen Fehler in seltenen Fällen. Angenommen, Sie möchten einen Stapel Studentenpapiere sortieren. (Was ich mit einiger Regelmäßigkeit tun muss.) Im Quicksort-Algorithmus wählen Sie etwas Papier, den Drehpunkt. Dann teilen Sie die anderen Papiere je nachdem, ob sie vor oder nach dem Drehpunkt sind. Wiederholen Sie das dann mit den beiden Substapeln. Was ist der Fehler? Der Pivot könnte ein Name sein, der nahe einem Ende der Liste statt in der Mitte liegt, so dass es nicht viel bringt, ihn in zwei Stapel zu teilen.

Merge sort ist ein anderer Divide-and-Conquer-Algorithmus, der in einer anderen Reihenfolge funktioniert. Sie können zwei sortierte Listen in linearer Zeit zusammenführen. Unterteilen Sie die Papiere in zwei gleiche oder fast gleiche Stapel, sortieren Sie sie dann rekursiv und fügen Sie sie dann zusammen. Merge sort hat keine Fehler. Ein Grund dafür, dass Quicksort beliebter ist als das Zusammenführen, ist historisch: Quicksort ist schnell (normalerweise) und es funktioniert ohne zusätzlichen Speicher. Heutzutage kann es jedoch wichtiger sein, Vergleiche zu speichern, als Speicher zu sparen, und die tatsächliche Umordnung wird oft durch das Permutieren von Zeigern abstrahiert. Wenn die Dinge schon immer so gewesen wären, dann würde ich vermuten, dass Merge Sort einfach populärer gewesen wäre als Quicksort. (Und das Hinzufügen von "schnell" zum Namen war eine gute Verkaufsmethode.)

+0

Von meinem POV ist der Vorteil einer In-Place-Sortierung nicht so sehr, dass es * Speicher * spart, da es eine Speicherzuweisung speichert und daher nicht fehlschlagen kann. Wenn Sie also ein Array sortieren, haben Quicksort/Heapsort/Insertion Sort/Bubble Sort alle schönere Benutzeroberflächen als Mergesort. Wenn Mergesort dem Quicksort vorgezogen würde, könnten Sie natürlich versuchen, den Speicher zuzuweisen, und wenn es nicht klappt, machen Sie stattdessen einen Quicksort. Wenn Sie ohnehin ein zweites Array von Zeigern zuweisen und diese sortieren, dann führen Sie die Möglichkeit eines Fehlers dort ein und können daher Fehler auch anderswo zulassen. –

+0

@SteveJessop Das ist ein guter Punkt. Diese Besorgnis ist zwar in einigen Fällen immer noch von Bedeutung, ist aber auch etwas veraltet. Ich stimme zu, dass es für die äußere Umgebung nicht-trivial ist, Speicher jedem Client-Programm oder jeder Funktion, die es wollen, gerecht zuzuweisen. Aber auch das ist in vielen Umgebungen im Laufe der Zeit besser geworden. –

+0

Ich glaube nicht, dass es wirklich eine Frage der Fairness ist, so sehr wie das, was passiert, wenn man ausgeht, und ob man dazu robust ist. Wenn die Zuweisung fehlschlagen kann, schreiben Sie Ihr Programm in eine Richtung. Wenn stattdessen das Betriebssystem etwas aus dem Wasser bläst, bis es genügend Speicher hat, um die Anforderung oder den Seitenfehler beim ersten Zugriff zu erfüllen, schreiben Sie Ihr Programm anders. Einige Sprachen nehmen einen mittleren Pfad, in dem Sie * theoretisch * Out-of-memory-Exceptions abfangen und fortfahren können, aber in der Praxis nicht, lassen Sie die Ausnahme Sie töten. Ich nehme an, das könnte als "up-to-date" Methode betrachtet werden ;-) –