Movatterモバイル変換


[0]ホーム

URL:


Zum Inhalt springen
WikipediaDie freie Enzyklopädie
Suche

Mergesort

aus Wikipedia, der freien Enzyklopädie
Beispiel, wie Mergesort eine Liste sortiert. Die Listenelemente werden durch Punkte dargestellt. Die waagerechte Achse gibt an, wo sich ein Element in der Liste befindet, die senkrechte Achse gibt an, wie groß ein Element ist.

Mergesort (vonenglischmerge ‚verschmelzen‘ undsort ‚sortieren‘) ist einstabilerSortieralgorithmus, der nach dem Prinzipteile und herrsche (divide and conquer) arbeitet. Er wurde erstmals1945 durchJohn von Neumann vorgestellt.[1]

Funktionsweise

[Bearbeiten |Quelltext bearbeiten]

Mergesort betrachtet die zu sortierenden Daten als Liste und zerlegt sie in kleinere Listen, die jede für sich sortiert werden. Die kleinen sortierten Listen werden dann im Reißverschlussverfahren zu größeren sortierten Listen zusammengefügt (engl.(to) merge), bis eine sortierte Gesamtliste erreicht ist. Das Verfahren arbeitet bei Arrays in der Regel nichtin-place, es sind dafür aber (trickreiche) Implementierungen bekannt, in welchen die Teil-Arrays üblicherweise rekursiv zusammengeführt werden.[2]Verkettete Listen sind besonders geeignet zur Implementierung von Mergesort, dabei ergibt sich die in-place-Sortierung fast von selbst.

Veranschaulichung der Funktionsweise

[Bearbeiten |Quelltext bearbeiten]
Funktionsweise

Das Bild veranschaulicht die drei wesentlichen Schritte einesTeile-und-herrsche-Verfahrens, wie sie im Rahmen von Mergesort umgesetzt werden. Der Teile-Schritt ist ersichtlich trivial (die Daten werden einfach in zwei Hälften aufgeteilt). Die wesentliche Arbeit wird beim Verschmelzen (merge) geleistet – daher rührt auch der Name des Algorithmus. BeiQuicksort ist hingegen der Teile-Schritt aufwendig und der Merge-Schritt einfacher (nämlich eineKonkatenierung).

Bei der Betrachtung des in der Grafik dargestellten Verfahrens sollte man sich allerdings bewusst machen, dass es sich hier nur um eine von mehrerenRekursionsebenen handelt. So könnte etwa die Sortierfunktion, welche die beiden Teile 1 und 2 sortieren soll, zu dem Ergebnis kommen, dass diese Teile immer noch zu groß für die Sortierung sind. Beide Teile würden dann wiederum aufgeteilt und der Sortierfunktion rekursiv übergeben, so dass eine weitere Rekursionsebene geöffnet wird, welche dieselben Schritte abarbeitet. Im Extremfall (der bei Mergesort sogar der Regelfall ist) wird das Aufteilen so weit fortgesetzt, bis die beiden Teile nur noch aus einzelnen Datenelementen bestehen und damit automatisch sortiert sind.

Implementierung

[Bearbeiten |Quelltext bearbeiten]

Der folgendePseudocode illustriert die Arbeitsweise desAlgorithmus, wobeiliste die zu sortierenden Elemente enthält.

funktion mergesort(liste);  falls (Größe von liste <= 1) dann antworte liste  sonst     halbiere die liste in linkeListe, rechteListe     linkeListe = mergesort(linkeListe)     rechteListe = mergesort(rechteListe)     antworte merge(linkeListe, rechteListe)
funktion merge(linkeListe, rechteListe);  neueListe  solange (linkeListe und rechteListe nicht leer)       falls (erstes Element der linkeListe <= erstes Element der rechteListe)       dann füge erstes Element linkeListe in die neueListe hinten ein und entferne es aus linkeListe       sonst füge erstes Element rechteListe in die neueListe hinten ein und entferne es aus rechteListe  solange_ende  solange (linkeListe nicht leer)       füge erstes Element linkeListe in die neueListe hinten ein und entferne es aus linkeListe  solange_ende  solange (rechteListe nicht leer)       füge erstes Element rechteListe in die neueListe hinten ein und entferne es aus rechteListe  solange_ende  antworte neueListe

Beispiel

[Bearbeiten |Quelltext bearbeiten]

Im letzten Verschmelzungsschritt ist das Reißverschlussverfahren beim Verschmelzen (in der Abb. „Mischen:“) angedeutet. Blaue Pfeile verdeutlichen den Aufteilungsschritt, grüne Pfeile die Verschmelzungsschritte.

Es folgt ein Beispielcode analog zum obigen Abschnitt „Implementierung“ für den rekursiven Sortieralgorithmus. Er teilt rekursiv absteigend die Eingabe in 2 kleinere Listen, bis diese trivialerweise sortiert sind, und verschmilzt sie auf dem rekursiven Rückweg, wodurch sie sortiert werden.

function merge_sort(listx)if length(x) ≤ 1thenreturnx      // Kurzesx ist trivialerweise sortiert.varl := empty listvarr := empty listvarnx := length(x)−1    // Teilex in die zwei Hälftenl undr ...fori := 0to floor(nx/2)do        appendx[i] tolfori := floor(nx/2)+1tonxdo        appendx[i] tor    // ... und sortiere beide (einzeln).l := merge_sort(l)r := merge_sort(r)    // Verschmelze die sortierten Hälften.return merge(l,r)

Beispielcode zum Verschmelzen zweier sortierter Listen.

function merge(listl, listr)vary := empty list    // Ergebnislistevarnl := length(l)−1varnr := length(r)−1varil := 0fori := 0tonl+nr+1doifil >nlthen            appendr[iil] toycontinueifil <inrthen            appendl[il] toyil :=il+1continue        // Jetzt ist 0 ≤ilnl und 0 ≤iilnr.ifl[il] ≤r[iil]then            appendl[il] toyil :=il+1else            appendr[iil] toyreturny

Java-Implementation

[Bearbeiten |Quelltext bearbeiten]

Eine iterative Implementation in der ProgrammierspracheJava unter Verwendung vonverketteten Listen könnte folgendermaßen aussehen:

(Es wird einemerge()-Funktion zum Verschmelzen zweier Listen verwendet, die im Absatz darunter erläutert wird.)

voidmergeSort(List<Integer>l){intn=l.size();//Erstelle n Teillisten. (1 Element pro Liste) (Divide)LinkedList<LinkedList<Integer>>groups=newLinkedList<LinkedList<Integer>>();for(inti=0;i<n;i++){LinkedList<Integer>list=newLinkedList<>();list.add(l.get(i));groups.add(list);}//Solange mehrere Gruppen existieren:while(groups.size()>1){//Merge die ersten beiden Listen im Vorrat und hänge die verbundene Liste hinten an.groups.addLast(merge(groups.removeFirst(),groups.removeFirst()));}//Überschreibe die unsortierte Liste mit der sortierten Versionlist=groups.getFirst();}

Der Merge-Schritt im Detail

[Bearbeiten |Quelltext bearbeiten]

Gegeben sind zwei in sich sortierte ListenA{\displaystyle A} undB{\displaystyle B}, die zu einer sortierten ListeC{\displaystyle C} zusammengefügt werden sollen.

Man vergleicht nun die beiden kleinsten Elemente (am Anfang der ListenA{\displaystyle A} undB{\displaystyle B}), fügt das kleinere zuC{\displaystyle C} hinzu und nimmt es aus der jeweiligen ListeA{\displaystyle A} oderB{\displaystyle B} heraus.

Dies wird so lange wiederholt, bis eine der beiden Listen A oder B leer ist, danach wird der Rest aus der anderen ListeA{\displaystyle A} oderB{\displaystyle B} (in der noch Einträge vorhanden sind) ans Ende vonC{\displaystyle C} gehängt.

Der Mergeschritt braucht genau immer|A|+|B|{\displaystyle |A|+|B|} Operationen, da jedes Element aus beiden Listen in konstanter Zeit gelöscht und hinzugefügt werden kann. Die Laufzeit beträgt folglich:O(|A|+|B|){\displaystyle {\mathcal {O}}(|A|+|B|)}

LinkedList<Integer>merge(LinkedList<Integer>linkeListe,LinkedList<Integer>rechteListe){LinkedList<Integer>temp=newLinkedList<>();// Solange noch nicht beide Listen hinzugefügt wordenwhile(!linkeListe.isEmpty()&&!rechteListe.isEmpty()){if(linkeListe.getFirst()<=rechteListe.getFirst()){temp.addLast(linkeListe.removeFirst());}else{temp.addLast(rechteListe.removeFirst());}}// Füge Rest hinzu, falls etwas übrig ist.temp.addAll(linkeListe);temp.addAll(rechteListe);returntemp;}

Komplexität

[Bearbeiten |Quelltext bearbeiten]

Mergesort ist ein stabiles Sortierverfahren, vorausgesetzt der Merge-Schritt ist entsprechend implementiert. SeineKomplexität beträgt im Worst-, Best- und Average-Case inLandau-Notation ausgedrückt stetsO(nlog(n)){\displaystyle {\mathcal {O}}(n\cdot \log(n))}.

Damit ist Mergesort hinsichtlich der Komplexität Quicksort grundsätzlich überlegen, da Quicksort (ohne besondere Vorkehrungen) einWorst-Case-Verhalten vonΘ(n2){\displaystyle \Theta (n^{2})} besitzt. Es benötigt jedoch zusätzlichen Speicherplatz (der GrößenordnungO(n){\displaystyle {\mathcal {O}}(n)}), ist also kein In-place-Verfahren.

Für die LaufzeitT(n){\displaystyle T(n)} von Mergesort bein{\displaystyle n} zu sortierenden Elementen gilt die Rekursionsformel

T(n)=T(n2)Aufwand, den einen Teil zu sortieren+T(n2)Aufwand, den anderen Teil zu sortieren+O(n)Aufwand, die beiden Teile zu verschmelzen{\displaystyle T(n)=\underbrace {T(\lfloor {\tfrac {n}{2}}\rfloor )} _{\text{Aufwand, den einen Teil zu sortieren}}+\underbrace {T(\lceil {\tfrac {n}{2}}\rceil )} _{\text{Aufwand, den anderen Teil zu sortieren}}+\underbrace {{\mathcal {O}}(n)} _{\text{Aufwand, die beiden Teile zu verschmelzen}}}

mit dem RekursionsanfangT(1)=1{\displaystyle T(1)=1}.

Nach demMaster-Theorem kann die Rekursionsformel durch2T(n2)+n{\displaystyle 2\,T(\lfloor {\tfrac {n}{2}}\rfloor )+n} bzw.2T(n2)+n{\displaystyle 2\,T(\lceil {\tfrac {n}{2}}\rceil )+n} approximiert werden mit jeweils der Lösung (2. Fall des Mastertheorems, s. dort)T(n)=O(nlog(n)){\displaystyle T(n)={\mathcal {O}}(n\cdot \log(n))}.

Durchschnittliche und maximale Anzahl Vergleiche        

Sindl0,l1{\displaystyle l_{0},l_{1}} die Längen der zu verschmelzenden und vorsortierten FolgenF0,F1,{\displaystyle F_{0},F_{1},} dann gilt für die AnzahlM{\displaystyle M} der erforderlichen Vergleiche fürs sortierende Verschmelzen

min(l0,l1)Ml0+l11{\displaystyle \min(l_{0},l_{1})\leq M\leq l_{0}+l_{1}-1},

da erstens eine Folge komplett vor der anderen liegen kann, d. h., es istF0[l0]F1[1]{\displaystyle F_{0}[l_{0}]\prec F_{1}[1]} bzw.F1[l1]F0[1],{\displaystyle F_{1}[l_{1}]\prec F_{0}[1],} oder es ist zweitensF0[l01]F1[l1]F0[l0]{\displaystyle F_{0}[l_{0}-1]\prec F_{1}[l_{1}]\prec F_{0}[l_{0}]} (bzw. umgekehrt), sodass die Elemente bis zum letzten Element in jeder Folge verglichen werden müssen. Dabei ist jeweils angenommen, dass das Vergleichen der zwei Folgen bei den Elementen mit niedrigem Index beginnt. Mit

Vm(l0+l1):=l0+l11{\displaystyle V_{m}(l_{0}+l_{1}):=l_{0}+l_{1}-1}

(Subskriptm{\displaystyle m} fürmaximal) sei die maximale Anzahl der Vergleiche fürsVerschmelzen bezeichnet.Für die maximale AnzahlSm(n){\displaystyle S_{m}(n)} an Vergleichen für einen ganzenMergesort-Lauf vonn{\displaystyle n} Elementen errechnet sich daraus

Sm(n)=nl2l+1,{\displaystyle S_{m}(n)=nl-2^{l}+1,} mitl:=log2(n).{\displaystyle l:=\lceil \log _{2}(n)\rceil .}

Sm(n){\displaystyle S_{m}(n)} ist die FolgeA001855 inOEIS.

Für eine Gleichverteilung lässt sich auch die durchschnittliche AnzahlV(l0,l1){\displaystyle V_{\varnothing }(l_{0},l_{1})} (Subskript{\displaystyle {}_{\varnothing }} fürdurchschnittlich) der Vergleiche genau berechnen, und zwar ist fürl1=l0{\displaystyle l_{1}=l_{0}}

V(l0,l0)=(k=l02l01(k1l01)k)/(k=l02l01(k1l01))=2l02/(l0+1){\displaystyle {\begin{aligned}V_{\varnothing }(l_{0},l_{0})\;\;\;\;\;&=\left(\sum _{k=l_{0}}^{2l_{0}-1}{\binom {k-1}{l_{0}-1}}k\right)\,{\bigg /}\left(\sum _{k=l_{0}}^{2l_{0}-1}{\binom {k-1}{l_{0}-1}}\right)\\&=2\,l_{0}^{2}/(l_{0}+1)\end{aligned}}}

und fürl1=l01{\displaystyle l_{1}=l_{0}-1}

V(l0,l01)=(k=l02l01(k1l01)k+k=l012l02(k1l02)k)/(k=l02l01(k1l01)+k=l012l02(k1l02))=2l02/(l0+1)1.{\displaystyle {\begin{aligned}V_{\varnothing }(l_{0},l_{0}-1)&=\left(\sum _{k=l_{0}}^{2l_{0}-1}{\binom {k-1}{l_{0}-1}}k\;+\sum _{k=l_{0}-1}^{2l_{0}-2}{\binom {k-1}{l_{0}-2}}k\right)\\&\;{\bigg /}\left(\sum _{k=l_{0}}^{2l_{0}-1}{\binom {k-1}{l_{0}-1}}\;\;\;+\sum _{k=l_{0}-1}^{2l_{0}-2}{\binom {k-1}{l_{0}-2}}\;\;\right)\\&=2\,l_{0}^{2}/(l_{0}+1)-1.\end{aligned}}}

Dabei istk{\displaystyle k} die Anzahl der Vergleiche für die Anordnung

uvw0k111l1+l0k{\displaystyle \underbrace {u\,v\dotso w\,0} _{k}\,\underbrace {1\,1\dotso 1} _{l_{1}+l_{0}-k}}   ,

wobei0{\displaystyle 0} für das letzte (das am höchsten sortierende) Element in der FolgeF0{\displaystyle F_{0}} steht, die (nicht von einem Element ausF0{\displaystyle F_{0}} unterbrochenen)1{\displaystyle 1}en zuF1{\displaystyle F_{1}} gehören undu,v,w{\displaystyle u,v,w} (die auch fehlen können) entweder zuF0{\displaystyle F_{0}} oder zuF1{\displaystyle F_{1}} gehören. Der in den Summenformeln beigegebeneBinomialkoeffizient zählt die verschiedenen Möglichkeiten füruvw.{\displaystyle u\,v\dotso w.}

Für die durchschnittliche AnzahlS(n){\displaystyle S_{\varnothing }(n)} an Vergleichen für einen ganzen Mergesort-Lauf vonn{\displaystyle n} Elementen errechnet man daraus

n{\displaystyle n}S(n){\displaystyle S_{\varnothing }(n)}Sm(n){\displaystyle S_{m}(n)}(Sm(n){\displaystyle (S_{m}(n)-}
S(n))/n{\displaystyle S_{\varnothing }(n))/n}
21,000010,00000
32,666730,11111
44,666750,08333
69,8333110,19444
815,733170,15833
1229,952330,25397
1645,689490,20694
2482,059890,28922
32121,501290,23452
48210,202250,30839
64305,053210,24920
96514,445450,31838
128736,137690,25677
1921218,912810,32348
2561726,317930,26061
3842819,829450,32606
5123962,640970,26255
7686405,666570,32736
10248947,292170,26352
153614345,0148490,32801
204819940,0204810,26401
307231760,0327690,32833
409643974,0450570,26426
614469662,0716810,32849
819296139,0983050,26438
122881,5161E51556490,32857
163842,0866E52129930,26444
245763,278E53358730,32862
327684,5009E54587530,26447
491527,0474E57208970,32864
655369,6571E59830410,26448
983041,5078E615400970,32865
1310722,0625E620971530,26449
1966083,2122E632768010,32865
2621444,3871E644564490,26450
3932166,8176E669468170,32865
5242889,2985E694371850,26450
7864321,4422E7146800650,32865
10485761,9646E7199229450,26450
15728643,0416E7309329930,32866
20971524,1388E7419430410,26450
31457286,3978E7650117130,32866
41943048,6971E7880803850,26450
62914561,3425E81363148810,32866
83886081,8233E81845493770,26450
125829122,8108E82852126730,32866
167772163,8144E83858759690,26450

und findetSm(n)0,3286560975nS(n)Sm(n).{\displaystyle S_{m}(n)-0{,}3286560975\,n\leq S_{\varnothing }(n)\leq S_{m}(n).}

Korrektheit und Terminierung

[Bearbeiten |Quelltext bearbeiten]

Der Rekursionsabbruch stellt dieTerminierung von Mergesort offensichtlich sicher, so dass lediglich noch dieKorrektheit gezeigt werden muss. Dies geschieht, indem wir folgende Behauptung beweisen:

Behauptung: In Rekursionstiefei{\displaystyle i} werden die sortierten Teillisten aus Rekursionstiefei+1{\displaystyle i{+}1} korrekt sortiert.

Beweis: Seio. B. d. A. die(i+1){\displaystyle (i{+}1)}-te Rekursion die tiefste. Dann sind die Teillisten offensichtlich sortiert, da sie einelementig sind. Somit ist ein Teil der Behauptung schon mal gesichert. Nun werden diese sortierten Teillisten eine Rekursionsebene nach oben, also in diei{\displaystyle i}-te Rekursion übergeben. Dort werden diese nach Konstruktion dermerge-Prozedur von Mergesort korrekt sortiert. Somit ist unsere Behauptung erfüllt und die totale Korrektheit von Mergesort bewiesen.

Natural Mergesort

[Bearbeiten |Quelltext bearbeiten]

Natural Mergesort(natürliches Mergesort) ist eine Erweiterung von Mergesort, diebereits vorsortierte Teilfolgen, so genannteruns, innerhalb der zu sortierenden Startliste ausnutzt. Die Basis für den Mergevorgang bilden hier nicht die rekursiv oder iterativ gewonnenen Zweiergruppen, sondern die in einem ersten Durchgang zu bestimmendenruns:

Startliste    : 3--4--2--1--7--5--8--9--0--6Runs bestimmen: 3--4  2  1--7  5--8--9  0--6Merge         : 2--3--4  1--5--7--8--9  0--6Merge         : 1--2--3--4--5--7--8--9  0--6Merge         : 0--1--2--3--4--5--6--7--8--9

Diese Variante hat den Vorteil, dass sortierte Folgen „erkannt“ werden und die Komplexität im Best-CaseO(n){\displaystyle {\mathcal {O}}(n)} beträgt. Average- und Worst-Case-Verhalten ändern sich hingegen nicht.

Außerdem eignet sich Mergesort gut für größere Datenmengen, die nicht mehr im Hauptspeicher gehalten werden können – es müssen jeweils nur beim Verschmelzen in jeder Ebene zweiListen vom externen Zwischenspeicher (z. B. Festplatte) gelesen und eine dorthin geschrieben werden. Eine Variante nutzt den verfügbaren Hauptspeicher besser aus (und minimiert Schreib-/Lesezugriffe auf der Festplatte), indem mehr als nur zwei Teil-Listen gleichzeitig vereinigt werden, und damit die Rekursionstiefe abnimmt.

Paralleler Mergesort

[Bearbeiten |Quelltext bearbeiten]

Mergesort lässt sich aufgrund desTeile-und-herrsche Ansatzes gut parallelisieren. Verschiedene parallele Varianten wurden in der Vergangenheit entwickelt. Manche sind stark verwandt mit der hier vorgestellten sequentiellen Variante, während andere eine grundlegend verschiedene Struktur besitzen und dasK-Wege-Mischen verwenden.

Mergesort mit parallelen Rekursionsaufrufen

[Bearbeiten |Quelltext bearbeiten]

Der sequentielle Mergesort kann in zwei Phasen beschrieben werden, die Teilen-Phase und die anschließende Misch-Phase. Die erste besteht aus vielen rekursiven Aufrufen, die immer wieder den gleichen Aufteilungsprozess durchführen, bis die Teilsequenzen trivial sortiert sind (mit einem oder keinem Element). Ein intuitiver Ansatz ist es, diese rekursiven Aufrufe zu parallelisieren.[3] Der folgende Pseudocode beschreibt den klassischen Mergesort Algorithmus mit paralleler Rekursion unter Verwendung der Schlüsselwörter fork and join.

//Sort elements lo through hi (exclusive) of array A.algorithm mergesort(A, lo, hi)isif lo+1 < hithen  //Two or more elements.        mid := ⌊(lo + hi) / 2⌋fork mergesort(A, lo, mid)        mergesort(A, mid, hi)join        merge(A, lo, mid, hi)

Dieser Algorithmus ist die triviale Modifikation des sequentiellen Algorithmus und ist noch nicht optimal. Sein Speedup ist dementsprechend auch nicht beeindruckend. Er hat einen Spann vonΘ(n){\displaystyle \Theta (n)}, was nur eine Verbesserung um den FaktorΘ(logn){\displaystyle \Theta (\log n)} ist im Vergleich zur sequentiellen Version. Dies liegt hauptsächlich an der sequentiellen Mischmethode, welche der Flaschenhals der parallelen Ausführung ist.

Mergesort mit paralleler Mischmethode

[Bearbeiten |Quelltext bearbeiten]

Ein besserer Parallelismus kann durch eine paralleleMischmethode erreicht werden. Cormen et al. präsentieren eine binäre Variante, welche zwei sortierte Teilsequenzen in eine sortierte Ausgabesequenz mischt.[3]

In der längeren der beiden Sequenzen (falls ungleich lang) wird das Element des mittleren Indexes ausgewählt. Seine Position in der anderen Sequenz wird so bestimmt, dass die Sequenz sortiert bliebe, wenn dieses Element an der bestimmten Stelle eingefügt werden würde. So weiß man, wie viele Elemente insgesamt kleiner sind als dasPivotelement, und die finale Position des Pivots kann in der Ausgabesequenz berechnet werden. Für die so erzeugten Teilfolgen der kleineren und größeren Elemente wird die Mischmethode wieder parallel ausgeführt, bis der Basisfall der Rekursion erreicht ist.

Der folgende Pseudocode illustriert den Mergesort mit modifizierter paralleler Mischmethode (aus Cormen et al.).

/** * A: Input array * B: Output array * lo: lower bound * hi: upper bound * off: offset */algorithm parallelMergesort(A, lo, hi, B, off)is    len := hi - lo + 1if len == 1then        B[off] := A[lo]else let T[1..len] be a new array        mid := ⌊(lo + hi) / 2⌋        mid' := mid - lo + 1fork parallelMergesort(A, lo, mid, T, 1)        parallelMergesort(A, mid + 1, hi, T, mid' + 1)join        parallelMerge(T, 1, mid', mid' + 1, len, B, off)

Um eine Rekurrenzrelation für den Worst Case zu erhalten, müssen die rekursiven Aufrufe von parallelMergesort aufgrund der parallelen Ausführung nur einmal aufgeführt werden. Man erhält

Tsort(n)=Tsort(n2)+Tmerge(n)=Tsort(n2)+Θ(log(n)2){\textstyle T_{\infty }^{\text{sort}}(n)=T_{\infty }^{\text{sort}}\left({\frac {n}{2}}\right)+T_{\infty }^{\text{merge}}(n)=T_{\infty }^{\text{sort}}\left({\frac {n}{2}}\right)+\Theta \left(\log(n)^{2}\right)}.

Für genauere Informationen über die Komplexität der parallelen Mischmethode.

Die Lösung dieser Rekurrenz ist

Tsort=Θ(log(n)3){\textstyle T_{\infty }^{\text{sort}}=\Theta \left(\log(n)^{3}\right)}.

Dieser Algorithmus erreicht eine Parallelisierbarkeit vonΘ(n(logn)2){\displaystyle \Theta {\biggr (}{n \over (\log n)^{2}}{\biggr )}}, was um einiges besser ist als der Parallelismus des vorherigen Algorithmus. Solch ein Sortieralgorithmus kann, wenn er mit einem schnellen stabilen sequentiellen Sortieralgorithmus und einer sequentiellen Mischmethode als Basisfall für das Mischen von zwei kleinen Sequenzen ausgestattet ist, gut in der Praxis funktionieren.[4]

Paralleler Mehrwege-Mergesort

[Bearbeiten |Quelltext bearbeiten]

Es wirkt unnatürlich, Mergesort Algorithmen auf binäre Mischmethoden zu beschränken, da oftmals mehr als zwei Prozessoren zur Verfügung stehen. Ein besserer Ansatz wäre es, einK-Wege-Mischen zu realisieren. Diese Generalisierung mischt im Gegensatz zum binären Mischenk{\displaystyle k} sortierte Sequenzen zu einer sortierten Sequenz. Diese Misch-Variante eignet sich gut zur Beschreibung eines Sortieralgorithmus auf einemPRAM.[5][6]

Grundidee

[Bearbeiten |Quelltext bearbeiten]
Der parallele Mehrwege-Mergesort Algorithmus auf vier Prozessorent0{\displaystyle t_{0}} bist3{\displaystyle t_{3}}.

Gegeben sei eine Folge vonn{\displaystyle n} Elementen. Ziel ist es, diese Sequenz mitp{\displaystyle p} verfügbaren Prozessoren zu sortieren. Die Elemente sind dabei gleich auf alle Prozessoren aufgeteilt und werden zunächst lokal mit einem sequentiellenSortieralgorithmus vorsortiert. Dementsprechend bestehen die Daten nun aus sortierten FolgenS1,...,Sp{\displaystyle S_{1},...,S_{p}} der Längenp{\textstyle \lceil {\frac {n}{p}}\rceil }. Der Einfachheit halber sein{\displaystyle n} ein Vielfaches vonp{\displaystyle p}, so dass füri=1,...,p{\displaystyle i=1,...,p} gilt:|Si|=np{\textstyle \left\vert S_{i}\right\vert ={\frac {n}{p}}}. Jede dieser Sequenzen wird wiederum inp{\displaystyle p} TeilsequenzenSi,1,...,Si,p{\displaystyle S_{i,1},...,S_{i,p}} partitioniert, indem fürj=1,...,p{\displaystyle j=1,...,p} die Trennelementevj{\displaystyle v_{j}} mit globalem Rangk=jnp{\textstyle k=j{\frac {n}{p}}} bestimmt werden. Die korrespondierenden Indizes werden in jeder FolgeSi{\displaystyle S_{i}} mit binärer Sucher ermittelt, sodass die Folgen anhand der Indizes aufgeteilt werden können. Formal definiert gilt somitSi,j:={xSi|rank(vj1)<rank(x)rank(vj)}{\displaystyle S_{i,j}:=\{x\in S_{i}|rank(v_{j-1})<rank(x)\leq rank(v_{j})\}}.

Nun werden die Elemente vonS1,i,...,Sp,i{\displaystyle S_{1,i},...,S_{p,i}} dem Prozessori{\displaystyle i} zugeteilt. Dies sind alle Elemente vom globalen Rang(i1)np{\textstyle (i-1){\frac {n}{p}}} bis zum Ranginp{\textstyle i{\frac {n}{p}}}, die über dieSi{\displaystyle S_{i}} verteilt sind. So erhält jeder Prozessor eine Folge von sortierten Sequenzen. Aus der Tatsache, dass der Rangk{\displaystyle k} der Trennelementevi{\displaystyle v_{i}} global gewählt wurde, ergeben sich zwei wichtige Eigenschaften: Zunächst sind die Trennelemente so gewählt, dass jeder Prozessor nach der Zuteilung der neuen Daten immer noch mitn/p{\textstyle n/p} Elementen betraut ist. Der Algorithmus besitzt also eine perfekteLastverteilung. Außerdem sind alle Elemente des Prozessorsi{\displaystyle i} kleiner oder gleich der Elemente des Prozessorsi+1{\displaystyle i+1}. Wenn nun jeder Prozessor einp-Wege-Mischen lokal durchführt, sind aufgrund dieser Eigenschaft die Elemente global sortiert. Somit müssen die Ergebnisse nur in der Reihenfolge der Prozessoren zusammengesetzt werden.

Trennelementbestimmung

[Bearbeiten |Quelltext bearbeiten]

In der einfachsten Form sindp{\displaystyle p} sortierte FolgenS1,...,Sp{\displaystyle S_{1},...,S_{p}} gleichverteilt aufp{\displaystyle p} Prozessoren sowie ein Rangk{\displaystyle k} gegeben. Gesucht ist nun ein Trennelementx{\displaystyle x} mit globalem Rangk{\displaystyle k} in der Vereinigung der Folgen. Damit kann jede FolgeSi{\displaystyle S_{i}} an einem Indexli{\displaystyle l_{i}} in zwei Teile aufgeteilt werden: Der untere Teil besteht nur aus Elementen, die kleinerx{\displaystyle x} sind, während der obere Teil alle Elemente enthält, welche größer oder gleich alsx{\displaystyle x} sind.

Der hier vorgestellte sequentielle Algorithmus gibt die Indizes der Trennungen zurück, also die Indizesli{\displaystyle l_{i}}in den FolgenSi{\displaystyle S_{i}}, sodassSi[li]{\displaystyle S_{i}[l_{i}]} einen global kleineren Rang alsk{\displaystyle k} hat undrank(Si[li+1])k{\displaystyle \mathrm {rank} \left(S_{i}[l_{i}+1]\right)\geq k} ist.[7]

algorithm msSelect(S : Array of sorted Sequences [S_1,..,S_p], k : int)isfor i = 1to pdo (l_i, r_i) = (0, |S_i|-1)
while there exists i: l_i < r_ido //pick Pivot Element in S_j[l_j],..,S_j[r_j], chose random j uniformly v := pickPivot(S, l, r)for i = 1to pdo     m_i = binarySearch(v, S_i[l_i, r_i]) //sequentiallyif m_1 + ... + m_p >= kthen //m_1+ ... + m_p is the global rank of v     r := m  //vector assignmentelse     l := m
return l

Für die Komplexitätsanalyse wurde das PRAM-Modell gewählt. Die p-fache Ausführung derbinarySearch Methode hat eine Laufzeit inO(plog(n/p)){\displaystyle {\mathcal {O}}\left(p\log \left(n/p\right)\right)}, falls die Daten über allep{\displaystyle p} Prozessoren gleichverteilt anliegen. Die erwartete Rekursionstiefe beträgt wie imQuickselect AlgorithmusO(log(i|Si|))=O(log(n)){\displaystyle {\mathcal {O}}\left(\log \left(\textstyle \sum _{i}|S_{i}|\right)\right)={\mathcal {O}}(\log(n))}. Somit ist die gesamte erwartete LaufzeitO(plog(n/p)log(n)){\displaystyle {\mathcal {O}}\left(p\log(n/p)\log(n)\right)}.

Angewandt auf den parallelen Mehrwege-Mergesort muss diemsSelect Methode parallel ausgeführt werden, um alle Trennelemente vom Ranginp{\textstyle i{\frac {n}{p}}} gleichzeitig zu finden. Dies kann anschließend verwendet werden, um jede Folge inp{\displaystyle p} Teile zu zerschneiden. Es ergibt sich die gleiche GesamtlaufzeitO(plog(n/p)log(n)){\displaystyle {\mathcal {O}}\left(p\,\log(n/p)\log(n)\right)}.

Pseudocode

[Bearbeiten |Quelltext bearbeiten]

Hier ist der komplette Pseudocode für den parallelen Mehrwege-Mergesort. Dabei wird eine Barriere-Synchronisation vor und nach der Trennelementbestimmung angenommen, sodass jeder Prozessor seine Trennelemente und die Partitionierung seiner Sequenz richtig berechnen kann.

/** * d: Unsorted Array of Elements * n: Number of Elements * p: Number of Processors * return Sorted Array */algorithm parallelMultiwayMergesort(d : Array, n : int, p : int)is    o :=new Array[0, n]                         // the output arrayfor i = 1to pdo in parallel                // each processor in parallel        S_i := d[(i-1) * n/p, i * n/p]           // Sequence of length n/p sort(S_i)                                // sort locallysynch v_i := msSelect([S_1,...,S_p], i * n/p)          // element with global rank i * n/psynch (S_i,1 ,..., S_i,p) := sequence_partitioning(si, v_1, ..., v_p) // split s_i into subsequences
 o[(i-1) * n/p, i * n/p] := kWayMerge(s_1,i, ..., s_p,i)  // merge and assign to output array
return o

Analyse

[Bearbeiten |Quelltext bearbeiten]

Zunächst sortiert jeder Prozessor die zugewiesenenn/p{\displaystyle n/p} Elemente lokal mit einem vergleichsbasierten Sortieralgorithmus der KomplexitätO(n/plog(n/p)){\displaystyle {\mathcal {O}}\left(n/p\;\log(n/p)\right)}. Anschließend können die Trennelemente in ZeitO(plog(n/p)log(n)){\displaystyle {\mathcal {O}}\left(p\,\log(n/p)\log(n)\right)} bestimmt werden. Schließlich müssen jede Gruppe vonp{\displaystyle p} Teilstücken gleichzeitig von jedem Prozessor zusammen gemischt werden. Dies hat eine Laufzeit vonO(log(p)n/p){\displaystyle {\mathcal {O}}(\log(p)\;n/p)}, indem ein sequentieller k-Wege Mischalgorithmus verwendet wird. Somit ergibt sich eine Gesamtlaufzeit von

O(nplog(np)+plog(np)log(n)+nplog(p)){\displaystyle {\mathcal {O}}\left({\frac {n}{p}}\log \left({\frac {n}{p}}\right)+p\log \left({\frac {n}{p}}\right)\log(n)+{\frac {n}{p}}\log(p)\right)}.

Praktische Anpassung und Anwendung

[Bearbeiten |Quelltext bearbeiten]

Der Mehrwege-Mergesort Algorithmus ist durch seine hohe Parallelität, was den Einsatz vieler Prozessoren ermöglicht, sehr skalierbar. Dies macht den Algorithmus zu einem brauchbaren Kandidaten für das Sortieren großer Datenmengen, wie sie beispielsweise inComputer-Clustern verarbeitet werden. Da der Speicher in solchen Systemen in der Regel keine limitierende Ressource darstellt, ist der Nachteil der Speicherkomplexität von Mergesort vernachlässigbar. Allerdings werden in solchen Systemen andere Faktoren wichtig, die bei der Modellierung auf einerPRAM nicht berücksichtigt werden. Hier sind unter anderem die folgenden Aspekte zu berücksichtigen: DieSpeicherhierarchie, wenn die Daten nicht in den Cache der Prozessoren passen, oder der Kommunikationsaufwand beim Datenaustausch zwischen den Prozessoren, der zu einem Engpass werden könnte, wenn auf die Daten nicht mehr über den gemeinsamen Speicher zugegriffen werden kann.

Sanders et al. haben in ihrem Paper einenbulk synchronous parallel-Algorithmus für einen mehrstufigen Mehrwege-Mergesort vorgestellt, derp{\displaystyle p} Prozessoren inr{\displaystyle r} Gruppen der Größep{\displaystyle p'} unterteilt. Alle Prozessoren sortieren zuerst lokal. Im Gegensatz zu einem einstufigen Mehrwege-Mergesort werden diese Sequenzen dann inr{\displaystyle r} Teile aufgeteilt und den entsprechenden Prozessorgruppen zugeordnet. Diese Schritte werden innerhalb dieser Gruppen rekursiv wiederholt. So wird die Kommunikation reduziert und insbesondere Probleme mit vielen kleinen Nachrichten vermieden. Die hierarchische Struktur des zugrundeliegenden realen Netzwerks (z. B.Racks,Cluster,...) kann zur Definition der Prozessorgruppen verwendet werden.[6]

Weitere Varianten

[Bearbeiten |Quelltext bearbeiten]

Mergesort war einer der ersten Sortieralgorithmen, bei dem ein optimalerSpeedup erreicht wurde, wobei Richard Cole einen cleveren Subsampling-Algorithmus verwendete, um die O(1)-Zusammenführung sicherzustellen.[8] Andere ausgeklügelte parallele Sortieralgorithmen können die gleichen oder bessere Zeitschranken mit einer niedrigeren Konstante erreichen. David Powers beschrieb beispielsweise 1991 einen parallelisiertenQuicksort (und einen verwandtenRadixsort), der durch implizite Partitionierung inO(logn){\displaystyle O(\log n)} Zeit auf einerCRCW-Parallel Random Access Machine (PRAM) mitn{\displaystyle n} Prozessoren arbeiten kann.[9] Powers zeigt ferner, dass eine Pipeline-Version von Batchers Bitonic Mergesort inO((logn)2){\displaystyle O((\log n)^{2})} Zeit auf einem Butterfly-Sortiernetzwerk in der Praxis schneller ist als seinO(logn){\displaystyle O(\log n)} Sortieralgorithmus auf einer PRAM, und er bietet eine detaillierte Diskussion der versteckten Overheads beim Vergleich, bei der Radix- und der Parallelsortierung.[10]

Sonstiges

[Bearbeiten |Quelltext bearbeiten]

Da Mergesort die Startliste sowie alle Zwischenlisten sequenziell abarbeitet, eignet er sich besonders zur Sortierung vonverketteten Listen. FürArrays wird normalerweise ein temporäres Array derselben Länge des zu sortierenden Arrays als Zwischenspeicher verwendet (das heißt Mergesort arbeitet normalerweise nichtin-place, s. o.). Quicksort dagegen benötigt kein temporäres Array.

Für die Größe des temporären Arrays reicht die halbe Größe der Anzahl der Elemente (n/2) aus. Unoptimierte Codebeispiele arbeiten mit einem Hilfsarray der Größe (n), dies ist für den Mergesort Algorithmus nicht erforderlich.[11]

DieSGI-Implementierung derStandard Template Library (STL) verwendet den Mergesort als Algorithmus zur stabilen Sortierung.[12]

Literatur

[Bearbeiten |Quelltext bearbeiten]

Weblinks

[Bearbeiten |Quelltext bearbeiten]

Einzelnachweise

[Bearbeiten |Quelltext bearbeiten]
  1. Donald E. Knuth:The Art of Computer Programming. 2. Auflage. Vol. 3:Sorting and Searching. Addison-Wesley, 1998,S. 158. 
  2. h2database/h2database. In: GitHub. Abgerufen am 1. September 2016. 
  3. abThomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest, Clifford Stein:Introduction to algorithms. 3. Auflage. MIT Press, Cambridge, Mass. 2009,ISBN 978-0-262-27083-0. 
  4. Victor J. Duvanenko:Parallel Merge Sort. In:Dr. Dobb's Journal & blog.duvanenko.tech.blog and GitHub repo C++ implementationgithub.com
  5. Peter Sanders, Johannes Singler:Multi-Core Standard Template Library,Karlsruher Institut für Technologie, 29. April 2008, abgerufen 5. Februar 2020 (englisch)
  6. abdl.acm.org (Hrsg.):Practical Massively Parallel Sorting | Proceedings of the 27th ACM symposium on Parallelism in Algorithms and Architectures.doi:10.1145/2755573.2755595 (englisch,acm.org [abgerufen am 28. Februar 2020]). 
  7. Peter Sanders:Parallele Algorithmen, Karlsruher Institut für Technologie, 25. November 2019, abgerufen 5. Februar 2020
  8. Richard Cole:Parallel Merge Sort. In:SIAM Journal on Computing.Band 17,Nr. 4, August 1988,ISSN 0097-5397,S. 770–785,doi:10.1137/0217049 (siam.org [abgerufen am 6. März 2020]). 
  9. David M. W. Powers:Parallelized Quicksort and Radixsort with Optimal Speedup. auf semanticscholar.org, in:Proceedings of International Conference on Parallel Computing Technologies,Novosibirsk 1991.
  10. David M. W. Powers:Parallel Unification: Practical Complexity, Australasian Computer Architecture Workshop, Flinders University, January 1995.
  11. H.W. Lang:Sortierverfahren. Mergesort iterativ Hochschule Flensburg, 2005–2022, mit Hilfsarray b=new int[n/2];
  12. stable_sort (Memento vom 13. November 2017 imInternet Archive) auf sgi.com (englisch)
Abgerufen von „https://de.wikipedia.org/w/index.php?title=Mergesort&oldid=260384592
Kategorie:

[8]ページ先頭

©2009-2026 Movatter.jp