Ich weiß, dass die Frage nicht zu spezifisch ist.
Ich möchte nur, dass mir jemand sagt, wie eine normale Zusammenführungssortierung in eine direkte Zusammenführungssortierung (oder eine Zusammenführungssortierung mit konstantem zusätzlichen Speicherplatzaufwand) konvertiert wird.
Alles, was ich (im Internet) finden kann, sind Seiten, auf denen steht "es ist zu komplex" oder "außerhalb des Geltungsbereichs dieses Textes".
Die einzigen bekannten Möglichkeiten zum Zusammenführen an Ort und Stelle (ohne zusätzlichen Speicherplatz) sind zu komplex, um auf ein praktisches Programm reduziert zu werden. ( von hier genommen )
Auch wenn es zu komplex ist, wie sieht das Grundkonzept aus, wie die Zusammenführungssortierung eingerichtet wird?
Antworten:
Knuth ließ dies als Übung (Band 3, 5.2.5). Es gibt In-Place-Zusammenführungssorten. Sie müssen sorgfältig umgesetzt werden.
Erstens ist eine naive In-Place-Zusammenführung wie hier beschrieben nicht die richtige Lösung. Die Leistung wird auf O (N 2 ) herabgestuft .
Die Idee ist, einen Teil des Arrays zu sortieren, während der Rest als Arbeitsbereich zum Zusammenführen verwendet wird.
Zum Beispiel wie die folgende Zusammenführungsfunktion.
Es nimmt die Anordnung
xs
, die zwei sortierten Subarrays werden als Bereiche dargestellt[i, m)
und[j, n)
jeweils. Der Arbeitsbereich beginnt abw
. Vergleichen Sie mit dem Standard-Zusammenführungsalgorithmus, der in den meisten Lehrbüchern angegeben ist. Dieser tauscht den Inhalt zwischen dem sortierten Unterarray und dem Arbeitsbereich aus. Infolgedessen enthält der vorherige Arbeitsbereich die zusammengeführten sortierten Elemente, während die im Arbeitsbereich gespeicherten vorherigen Elemente in die beiden Unterarrays verschoben werden.Es gibt jedoch zwei Einschränkungen, die erfüllt sein müssen:
Wenn dieser Zusammenführungsalgorithmus definiert ist, kann man sich leicht eine Lösung vorstellen, mit der die Hälfte des Arrays sortiert werden kann. Die nächste Frage ist, wie mit dem Rest des unsortierten Teils umgegangen werden soll, der im Arbeitsbereich gespeichert ist, wie unten gezeigt:
Eine intuitive Idee besteht darin, eine weitere Hälfte des Arbeitsbereichs rekursiv zu sortieren, sodass nur 1/4 Elemente noch nicht sortiert wurden.
Der entscheidende Punkt in dieser Phase ist, dass wir die sortierten 1/4 Elemente B früher oder später mit den sortierten 1/2 Elementen A zusammenführen müssen.
Ist der Arbeitsbereich, der nur 1/4 Elemente enthält, groß genug, um A und B zusammenzuführen? Leider ist es nicht.
Die oben erwähnte zweite Einschränkung gibt uns jedoch einen Hinweis, dass wir sie ausnutzen können, indem wir den Arbeitsbereich so anordnen, dass er sich mit einem der beiden Unterarrays überlappt, wenn wir sicherstellen können, dass die Zusammenführungssequenz nicht überschrieben wird.
Anstatt die zweite Hälfte des Arbeitsbereichs zu sortieren, können wir auch die erste Hälfte sortieren und den Arbeitsbereich wie folgt zwischen die beiden sortierten Arrays einfügen:
Dieser Aufbau ordnet effektiv die Überlappung des Arbeitsbereichs mit dem Subarray A an. Diese Idee wird in [Jyrki Katajainen, Tomi Pasanen, Jukka Teuhola. "Praktisches In-Place-Mergesort". Nordic Journal of Computing, 1996].
Das einzige, was noch übrig ist, ist, den obigen Schritt zu wiederholen, wodurch der Arbeitsbereich von 1/2, 1/4, 1/8, ... reduziert wird. Wenn der Arbeitsbereich klein genug wird (zum Beispiel nur noch zwei Elemente übrig), können wir Wechseln Sie zu einer einfachen Einfügungssortierung, um diesen Algorithmus zu beenden.
Hier ist die Implementierung in ANSI C basierend auf diesem Dokument.
Wo wmerge zuvor definiert wurde.
Den vollständigen Quellcode finden Sie hier und die ausführliche Erklärung finden Sie hier
Diese Version ist übrigens nicht die schnellste Zusammenführungssortierung, da mehr Auslagerungsvorgänge erforderlich sind. Meinem Test zufolge ist es schneller als die Standardversion, die bei jeder Rekursion zusätzliche Leerzeichen zuweist. Es ist jedoch langsamer als die optimierte Version, bei der das ursprüngliche Array im Voraus verdoppelt und für die weitere Zusammenführung verwendet wird.
quelle
Knuth left this as an exercise (Vol 3, 5.2.5).
bezieht sich auf ex. 13. [40] Implementieren Sie die vorgeschlagene interne Sortiermethode [am Ende dieses Abschnitts] und erstellen Sie zufällige Daten in O (N) Zeiteinheiten mit nur O (sqrt (N)) zusätzlichen Speicherplätzen. ? ( 40 zeigt ein ziemlich schwieriges oder langwieriges Problem an, das vielleicht als Semesterprojekt in Unterrichtssituationen geeignet ist. )Inklusive des "großen Ergebnisses" beschreibt dieses Papier einige Varianten der In-Place-Merge-Sortierung (PDF):
http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.22.5514&rep=rep1&type=pdf
In-Place-Sortierung mit weniger Zügen
Jyrki Katajainen, Tomi A. Pasanen
Ich denke, das ist auch relevant. Ich habe einen Ausdruck davon herumliegen, der mir von einem Kollegen weitergegeben wurde, aber ich habe ihn nicht gelesen. Es scheint die grundlegende Theorie abzudecken, aber ich bin mit dem Thema nicht vertraut genug, um zu beurteilen, wie umfassend:
http://comjnl.oxfordjournals.org/cgi/content/abstract/38/8/681
Optimale stabile Zusammenführung
Antonios Symvonis
quelle
Es ist wirklich nicht einfach oder effizient, und ich schlage vor, Sie tun es nur, wenn Sie es wirklich müssen (und Sie müssen es wahrscheinlich nicht tun, es sei denn, dies sind Hausaufgaben, da die Anwendungen des Inplace-Zusammenführens größtenteils theoretisch sind). Können Sie nicht stattdessen Quicksort verwenden? Quicksort wird mit ein paar einfacheren Optimierungen sowieso schneller sein und sein zusätzlicher Speicher ist O (log N) .
Wie auch immer, wenn Sie es tun müssen, dann müssen Sie. Folgendes habe ich gefunden: eins und zwei . Ich bin nicht mit der Inplace-Merge-Sortierung vertraut, aber es scheint, dass die Grundidee darin besteht, Rotationen zu verwenden, um das Zusammenführen von zwei Arrays ohne zusätzlichen Speicher zu erleichtern.
Beachten Sie, dass dies sogar langsamer ist als die klassische Zusammenführungssortierung, die nicht vorhanden ist.
quelle
Der entscheidende Schritt besteht darin, die Zusammenführung selbst in Kraft zu setzen. Es ist nicht so schwierig, wie diese Quellen ausmachen, aber Sie verlieren etwas, wenn Sie es versuchen.
Betrachten Sie einen Schritt der Zusammenführung:
Wir wissen , dass die sortierten Sequenz als alles weniger ist anders, dass x weniger als alles andere in A , und daß y kleiner ist als alles andere in B . In dem Fall, in dem x kleiner oder gleich y ist , bewegen Sie einfach Ihren Zeiger auf den Anfang von A auf eins. In dem Fall, in dem y kleiner als x ist , müssen Sie y über das gesamte A mischen, um es zu sortieren . Dieser letzte Schritt macht dies teuer (außer in entarteten Fällen).
Es ist im Allgemeinen billiger (insbesondere wenn die Arrays nur einzelne Wörter pro Element enthalten, z. B. einen Zeiger auf eine Zeichenfolge oder Struktur), etwas Zeit für Zeit auszutauschen und ein separates temporäres Array zu haben, zwischen dem Sie hin und her sortieren.
quelle
Nur als Referenz, hier ist eine schöne Implementierung einer stabilen In-Place-Merge-Sortierung . Kompliziert, aber nicht schlecht.
Am Ende habe ich sowohl eine stabile In-Place-Merge-Sortierung als auch eine stabile In-Place-Quicksortierung in Java implementiert . Bitte beachten Sie, dass die Komplexität O (n (log n) ^ 2) ist.
quelle
Ein Beispiel für eine pufferlose Zusammenführung in C.
Ein Beispiel für adaptives Mergesort (optimiert).
Fügt Support-Code und Änderungen hinzu, um die Zusammenführung zu beschleunigen, wenn ein Hilfspuffer beliebiger Größe verfügbar ist (funktioniert immer noch ohne zusätzlichen Speicher). Verwendet Vorwärts- und Rückwärtszusammenführung, Ringrotation, Zusammenführen und Sortieren kleiner Sequenzen und iteratives Zusammenführen.
quelle
Diese Antwort enthält ein Codebeispiel , das den im Artikel Praktisches In-Place-Zusammenführen beschriebenen Algorithmus implementiert von Bing-Chao Huang und Michael A. Langston beschrieben ist. Ich muss zugeben, dass ich die Details nicht verstehe, aber die gegebene Komplexität des Zusammenführungsschritts ist O (n).
Aus praktischer Sicht gibt es Hinweise darauf, dass reine In-Place-Implementierungen in realen Szenarien keine bessere Leistung erbringen. Beispielsweise definiert der C ++ - Standard std :: inplace_merge , was bedeutet , dass der Name eine direkte Zusammenführungsoperation impliziert.
Unter der Annahme, dass C ++ - Bibliotheken normalerweise sehr gut optimiert sind, ist es interessant zu sehen, wie sie implementiert werden:
1) libstdc ++ (Teil der GCC-Codebasis): std :: inplace_merge
Die Implementierung delegiert an __inplace_merge , wodurch das Problem umgangen wird , indem versucht wird, einen temporären Puffer zuzuweisen:
Andernfalls wird auf eine Implementierung ( __merge_without_buffer ) zurückgegriffen, die keinen zusätzlichen Speicher benötigt, aber nicht mehr in O (n) -Zeit ausgeführt wird.
2) libc ++ (Teil der Clang-Codebasis): std :: inplace_merge
Sieht ähnlich aus. Es delegiert an eine Funktion , die auch versucht, einen Puffer zuzuweisen . Je nachdem, ob genügend Elemente vorhanden sind, wird die Implementierung ausgewählt. Die Fallback-Funktion für konstanten Speicher heißt __buffered_inplace_merge .
Vielleicht ist sogar der Fallback noch O (n) -Zeit, aber der Punkt ist, dass sie die Implementierung nicht verwenden, wenn temporärer Speicher verfügbar ist.
Beachten Sie, dass der C ++ - Standard Implementierungen explizit die Freiheit gibt, diesen Ansatz zu wählen, indem die erforderliche Komplexität von O (n) auf O (N log N) verringert wird:
Dies kann natürlich nicht als Beweis dafür angesehen werden, dass ein konstanter Raum an Ort und Stelle, der in O (n) Zeit verschmilzt, niemals verwendet werden sollte. Wenn es jedoch schneller wäre, würden die optimierten C ++ - Bibliotheken wahrscheinlich auf diese Art der Implementierung umsteigen.
quelle
Dies ist meine C-Version:
quelle
Es gibt eine relativ einfache Implementierung der In-Place-Merge-Sortierung unter Verwendung der ursprünglichen Technik von Kronrod, jedoch mit einer einfacheren Implementierung. Ein bildliches Beispiel, das diese Technik veranschaulicht, finden Sie hier: http://www.logiccoder.com/TheSortProblem/BestMergeInfo.htm .
Es gibt auch Links zu detaillierteren theoretischen Analysen desselben Autors, die mit diesem Link verbunden sind.
quelle
Ich habe gerade versucht, einen Zusammenführungsalgorithmus für die Zusammenführungssortierung in JAVA mithilfe des Einfügesortieralgorithmus zu verwenden. Dabei wurden die folgenden Schritte ausgeführt.
1) Es stehen zwei sortierte Arrays zur Verfügung.
2) Vergleichen Sie die ersten Werte jedes Arrays; und platzieren Sie den kleinsten Wert in das erste Array.
3) Platzieren Sie den größeren Wert mithilfe der Einfügesortierung (von links nach rechts durchlaufen) in das zweite Array.
4) Vergleichen Sie dann erneut den zweiten Wert des ersten Arrays und den ersten Wert des zweiten Arrays und machen Sie dasselbe. Wenn jedoch ein Austausch stattfindet, gibt es einen Hinweis darauf, dass der Vergleich der weiteren Elemente übersprungen wird, aber nur ein Austausch erforderlich ist.
Ich habe hier einige Optimierungen vorgenommen. um kleinere Vergleiche in der Einfügesortierung zu halten.
Der einzige Nachteil, den ich bei diesen Lösungen festgestellt habe, ist, dass die Array-Elemente im zweiten Array stärker ausgetauscht werden müssen.
z.B)
First___Array: 3, 7, 8, 9
Zweites Array: 1, 2, 4, 5
Dann bewirkt 7, 8, 9, dass das zweite Array alle seine Elemente jedes Mal um eins vertauscht (nach links bewegt), um sich im letzten zu platzieren.
Die Annahme, dass hier Gegenstände ausgetauscht werden, ist im Vergleich zum Vergleich zweier Gegenstände vernachlässigbar.
https://github.com/skanagavelu/algorithams/blob/master/src/sorting/MergeSort.java
quelle