Algorithmus zum Zusammenführen von zwei sortierten Arrays mit einer minimalen Anzahl von Vergleichen


24

Gegeben sind zwei sortierte Arrays a , b vom Typ T mit der Größe n und m . Ich suche nach einem Algorithmus, der die zwei Reihen in eine neue Reihe (der maximalen Größe n + m) zusammenführt.

Wenn Sie eine billige Vergleichsoperation haben, ist dies ziemlich einfach. Nehmen Sie einfach vom Array mit dem niedrigsten ersten Element, bis eines oder beide Arrays vollständig durchlaufen sind, und fügen Sie dann die verbleibenden Elemente hinzu. In etwa so /programming/5958169/how-to-merge-two-sorted-arrays-into-a-sorted-array

Die Situation ändert sich jedoch, wenn zwei Elemente verglichen werden. Dies ist wesentlich teurer als das Kopieren eines Elements vom Quell-Array in das Ziel-Array . Zum Beispiel könnten Sie ein Array von großen Ganzzahlen mit beliebiger Genauigkeit oder Strings haben, bei denen ein Vergleich ziemlich teuer sein kann. Nehmen Sie einfach an, dass das Erstellen von Arrays und das Kopieren von Elementen kostenlos ist und dass nur der Vergleich von Elementen kostet.

In diesem Fall möchten Sie die beiden Arrays mit einer minimalen Anzahl von Elementvergleichen zusammenführen . Hier sind einige Beispiele, bei denen Sie weitaus bessere Ergebnisse erzielen sollten als mit dem einfachen Zusammenführungsalgorithmus:

a = [1,2,3,4, ... 1000]
b = [1001,1002,1003,1004, ... 2000]

Oder

a = [1,2,3,4, ... 1000]
b = [0,100,200, ... 1000]

Es gibt einige Fälle, in denen der einfache Zusammenführungsalgorithmus optimal ist

a = [1,3,5,7,9,....,999]
b = [2,4,6,8,10,....,1000]

Daher sollte der Algorithmus im Idealfall n + m-1 Vergleiche in angemessener Weise herabsetzen und maximal durchführen, falls die Arrays verschachtelt sind oder zumindest nicht wesentlich schlechter sind.

Eine Sache, die sich für Listen mit einem großen Größenunterschied recht gut eignet, ist die Verwendung der binären Suche, um die Elemente des kleineren Arrays in das größere Array einzufügen. Dies verschlechtert sich jedoch nicht, wenn beide Listen gleich groß und verschachtelt sind.

Das einzige, was für die Elemente verfügbar ist, ist eine (Gesamt-) Ordnungsfunktion, so dass ein Schema, das Vergleiche billiger macht, nicht möglich ist.

Irgendwelche Ideen?

Ich habe mir dieses Stück in Scala ausgedacht . Ich glaube, es ist in Bezug auf die Anzahl der Vergleiche optimal, aber ich kann es nicht beweisen. Zumindest ist es viel einfacher als die Dinge, die ich in der Literatur gefunden habe.

Und seit dem ursprünglichen Posting habe ich einen Blog-Post darüber geschrieben, wie das funktioniert.


2
Es gibt keine Möglichkeit, weniger Vergleiche als beim "einfachen Zusammenführungsalgorithmus" anzustellen. Sie können versuchen, Randfälle wie die zuerst genannten zu behandeln, dies wird jedoch den Durchschnittsfall verschlechtern.
Mephy

5
@Mephy: Erleuchten Sie uns und geben Sie uns einen formellen Beweis, bitte. Wenn Sie dies nicht können, können Sie Ihren Kommentar löschen (oder zumindest verfeinern).
Doc Brown

4
@DocBrown Wenn ich einen formellen Beweis hätte, würde ich eine Antwort geben, keinen Kommentar. Wie auch immer, es ist ein ziemlich offensichtliches lineares Problem, da der Versuch, eine besser als lineare Lösung zu finden, mindestens lineare Zeit benötigt.
Mephy

4
@Mephy: Ich schlage vor, Sie nehmen sich die Zeit, die unten stehende Antwort zu lesen, und überlegen zweimal, was Sie geschrieben haben.
Doc Brown

4
@Mephy Die meisten Dinge, die offensichtlich sind ("Sie können nicht in weniger als O (n ^ 2) multiplizieren", "wenn ich die Tür ändere, die ich ausgewählt habe, werden sich meine Gewinnchancen nicht verbessern" , "können Sie 't sort in less than O (n log n) ", ..) sind falsch. Die Verwendung eines binären Suchansatzes auf der kürzeren Liste sollte beispielsweise den Durchschnittsfall möglicherweise verbessern.
Voo

Antworten:


31

Der normale Zusammenführungssortieralgorithmus - Zusammenführungsschritt, bei dem normalerweise n + m -1 Vergleiche angewendet werden, wobei eine Liste die Größe n und die andere die Größe m hat. Die Verwendung dieses Algorithmus ist die einfachste Methode, um zwei sortierte Listen zu kombinieren.

Wenn die Vergleiche zu teuer sind, können Sie zwei Dinge tun - entweder Sie minimieren die Anzahl der Vergleiche oder Sie minimieren die Kosten für Vergleiche.

Konzentrieren wir uns auf die Minimierung der Vergleichskosten. Sie und nur Sie können entscheiden, ob die zu vergleichenden Daten quantisiert werden können oder nicht. Wenn Sie sie quantisieren können, ist dies eine Form der Implementierung einer Hash-Methode, die die Reihenfolge beibehält. ZB wenn Ihre Daten nach Name verglichen werden, dann der erste tname, ... Sie können den ersten zu Buchstaben des Namens "Klaehn, Ruediger" nehmen und Ihr Datenelement zu "Kl.Ru" reduzieren / quantisieren, wenn Sie es vergleichen Bei "Packer, The" behalten Sie die Reihenfolge "Pa.Th" bei - Sie können jetzt einen günstigeren Vergleichsalgorithmus anwenden und die reduzierten Werte vergleichen. Wenn Sie jedoch ein anderes "Kl.Ru" finden, haben Sie jetzt einen nahen Wert und wechseln möglicherweise zu einem teureren Ansatz, wenn Sie diese Elemente vergleichen.

Wenn Sie diesen quantisierten Wert aus Ihren Daten extrahieren können, anstatt ihn zu vergleichen, vergleichen Sie zuerst den quantisierten oder gehashten Wert. Bitte beachten Sie, dass dieser Wert nur einmal berechnet werden muss, damit Sie ihn beim Erstellen des Datenelements berechnen können.

Ich erwähnte auch einen anderen Weg, um Ihre Vergleiche zu minimieren.

Ich habe mir das klassische Buch TAOCP-Volume 3-Sorting and Searching (S. 197-207, Abschnitt 5.3.2) angesehen, das 10 Seiten zu diesem Thema enthält. Ich habe zwei Hinweise auf Algorithmen gefunden, die schneller sind als n + m-1 Vergleiche.

Da ist zum einen der Hwang-Lin-Merge-Algorithmus und zum anderen eine Verbesserung von Glenn K Manacher - beide werden von TAOCP und ein Algorithmus von Christen angeführt, der sich der Untergrenze der benötigten Vergleiche unter speziellen Bedingungen für die Länge n und m nähert der Listen.

Der Algorithmus von Manacher wurde im Journal of the ACM Vol vorgestellt. 26 Nummer 3 auf den Seiten 434-440: "Signifikante Verbesserungen des" Hwan-Lin "-Mischalgorithmus". Die Liste mit m Elementen und die Liste mit n Elementen können unterschiedlich lang sein, sie müssen jedoch auch nach der Anzahl der Elemente geordnet sein, die sie enthalten. m <= n

Der Hwang-Lin-Algorithmus zerlegt die zusammenzuführenden Listen in kleinere Listen und sortiert die Listen, indem er das erste Element jeder Unterliste vergleicht und entscheidet, ob einige Elemente in der Unterliste verglichen werden müssen oder nicht. Ist die erste Liste kleiner als die zweite Liste, so ist die Chance groß, dass aufeinanderfolgende Elemente der längeren Liste ohne Vergleich in die resultierende Liste übernommen werden können. Ist das erste Element der kleinen Liste größer als das erste Element der geteilten größeren Liste, können alle Elemente vor der Unterliste ohne Vergleich kopiert werden.

Eine durchschnittliche Fallanalyse des Verschmelzungsalorithmus von Hwang und Lin (Vega, Frieze, Santha) in Abschnitt 2 enthält einen Pseudocode des HL-Algorithmus. Welches ist viel besser als meine Beschreibung. Und Sie können sehen, warum es weniger Vergleiche gibt - der Algorithmus verwendet eine binäre Suche, um den Index zu finden, in den das Element aus der kürzeren Liste eingefügt werden kann.

Wenn die Listen nicht wie in Ihrem letzten Beispiel verschachtelt sind, sollten Sie in den meisten Fällen eine kleinere und eine größere Liste haben. Dies ist der Zeitpunkt, an dem der HL-Algorithmus eine bessere Leistung erzielt.


Vielen Dank für Ihren Kommentar zu diesem Thema. Ich habe meine Antwort überprüft und festgestellt, dass Knuth ganze 10 Seiten für dieses Thema aufgewendet hat. Und dann nahm ich The JACM aus meinem Bücherregal und schaute dort nach. Ich werde meine Antwort verbessern. - Keine Abstimmungen nötig. Der Hash- (Quantisierer-) Algorithmus ist eine einfache Idee, die auf viele Datensätze angewendet werden kann - aber nur derjenige, der gefragt hat, ist der einzige, der entscheidet, ob er auf seine Daten anwendbar ist oder nicht.
Thepacker

4
Nachdem Sie Ihre Antwort verbessert haben, haben alle, die Sie abgelehnt haben, die Möglichkeit, Sie erneut zu verbessern ;-)
Doc Brown

+1 für die Feststellung, dass die Standardzusammenführung nicht optimal ist, wenn die Größen sehr unterschiedlich sind.
Florian F

1

Angenommen, die beiden Arrays haben N- und M-Elemente, N ≥ M, und alle Elemente sind unterschiedlich.

Wenn das sortierte Array ein Element x von N gefolgt von einem Element y von M oder umgekehrt enthält, müssen x und y verglichen worden sein, sonst würden wir nicht wissen, in welcher Reihenfolge sie gehören. (Es kann keine Kette von anderen Elementen a, b, c geben, bei denen wir beispielsweise wissen, dass x <a <b <c <y ist, weil es keine Elemente zwischen x und y gibt. Daher müssen x und y verglichen worden sein direkt.

Wenn N> M ist, ist es möglich, ein Array zu haben, in dem jedem Element von M ein Element von N vorangestellt und gefolgt wird. Dies bedeutet, dass mindestens 2 M Vergleiche erforderlich sind - auch wenn Sie einen nicht deterministischen Sortieralgorithmus verwenden, der dies ermöglicht eine perfekte Vermutung, welche Zahlen zu vergleichen sind. (Was das bedeutet: Angenommen, Sie haben N große, M = 1. Die binäre Suche dauert O (log2 N) Schritte; ein nicht deterministischer Algorithmus würde erraten, zu welchen zwei Elementen das eine Element des zweiten Arrays gehört, und zwei Vergleiche anstellen bestätige die Vermutung).

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.