Quicksort June 6, 2010 Quicksort (C.A.R. Hoare, Computer Journal, 1962) Quicksort (C.A.R. Hoare, Computer Journal, 1962) I Klassisches, sehr beliebtes, weil im Mittel sehr effizientes Sortierverfahren Quicksort (C.A.R. Hoare, Computer Journal, 1962) I Klassisches, sehr beliebtes, weil im Mittel sehr effizientes Sortierverfahren I In vielen Systemen (Unix) das Standardverfahren Quicksort (C.A.R. Hoare, Computer Journal, 1962) I Klassisches, sehr beliebtes, weil im Mittel sehr effizientes Sortierverfahren I In vielen Systemen (Unix) das Standardverfahren I Basiert auf “dynamischem” (datengetriebenem) divide-and-conquer Quicksort (C.A.R. Hoare, Computer Journal, 1962) I Klassisches, sehr beliebtes, weil im Mittel sehr effizientes Sortierverfahren I In vielen Systemen (Unix) das Standardverfahren I Basiert auf “dynamischem” (datengetriebenem) divide-and-conquer I Wenig zusätzlicher Speicherplatz (O(log2 n) bei Listelänge n) Quicksort (C.A.R. Hoare, Computer Journal, 1962) I Klassisches, sehr beliebtes, weil im Mittel sehr effizientes Sortierverfahren I In vielen Systemen (Unix) das Standardverfahren I Basiert auf “dynamischem” (datengetriebenem) divide-and-conquer I Wenig zusätzlicher Speicherplatz (O(log2 n) bei Listelänge n) I Vorsicht: worst-case Verhalten ist ineffizient! Quicksort (C.A.R. Hoare, Computer Journal, 1962) I Klassisches, sehr beliebtes, weil im Mittel sehr effizientes Sortierverfahren I In vielen Systemen (Unix) das Standardverfahren I Basiert auf “dynamischem” (datengetriebenem) divide-and-conquer I Wenig zusätzlicher Speicherplatz (O(log2 n) bei Listelänge n) I Vorsicht: worst-case Verhalten ist ineffizient! I Paradoxon: worst-case tritt z.B. bei schon sortierten Listen ein Quicksort (C.A.R. Hoare, Computer Journal, 1962) I Klassisches, sehr beliebtes, weil im Mittel sehr effizientes Sortierverfahren I In vielen Systemen (Unix) das Standardverfahren I Basiert auf “dynamischem” (datengetriebenem) divide-and-conquer I Wenig zusätzlicher Speicherplatz (O(log2 n) bei Listelänge n) I Vorsicht: worst-case Verhalten ist ineffizient! I Paradoxon: worst-case tritt z.B. bei schon sortierten Listen ein I Viele Varianten und Verbesserungen, um worst-case-Verhalten sehr unwahrscheinlich zu machen R. Sedgewick in Algorithms: It is tempting to try to develop ways to improve Quicksort: a faster sorting algorithm is computer science’s “better mousetrap”. Almost from the moment Hoare first published the algorithm, “improved” version have been appearing in the literature. R. Sedgewick in Algorithms: It is tempting to try to develop ways to improve Quicksort: a faster sorting algorithm is computer science’s “better mousetrap”. Almost from the moment Hoare first published the algorithm, “improved” version have been appearing in the literature. Many ideas have been tried and analyzed, but it is easy to be deceived, because the algorithm is so well balanced that the effects of improvements in one part of the program can be more than offset by the effects of bad performance in another part of the program. R. Sedgewick in Algorithms: It is tempting to try to develop ways to improve Quicksort: a faster sorting algorithm is computer science’s “better mousetrap”. Almost from the moment Hoare first published the algorithm, “improved” version have been appearing in the literature. Many ideas have been tried and analyzed, but it is easy to be deceived, because the algorithm is so well balanced that the effects of improvements in one part of the program can be more than offset by the effects of bad performance in another part of the program. A carefully tuned version of Quicksort is likely to run significantly faster on most computers than any other sorting method. R. Sedgewick in Algorithms: It is tempting to try to develop ways to improve Quicksort: a faster sorting algorithm is computer science’s “better mousetrap”. Almost from the moment Hoare first published the algorithm, “improved” version have been appearing in the literature. Many ideas have been tried and analyzed, but it is easy to be deceived, because the algorithm is so well balanced that the effects of improvements in one part of the program can be more than offset by the effects of bad performance in another part of the program. A carefully tuned version of Quicksort is likely to run significantly faster on most computers than any other sorting method. However, it must be cautioned that tuning any algorithm can make it more fragile, leading to undesirable and unexpected effects for some inputs I Zur Implementierung und detailierten Analyse: R. Sedgewick, Ouicksort (Diss.), R. Sedgewick, Implementing Quicksort Programs, Communications of the ACM, 1978, J.L. Bentley, M.D. McIlroy, Software Practice and Experience, 1993 (→ Unix) Divide-and-conquer für das Sortieren I A totalgeordneten Menge Divide-and-conquer für das Sortieren I A totalgeordneten Menge I a = ha1 , a2 , . . . , an i Liste von Elementen aus A Divide-and-conquer für das Sortieren I A totalgeordneten Menge I a = ha1 , a2 , . . . , an i Liste von Elementen aus A I Das Element ak in der Position k ist ein Splitter für a, wenn gilt ai ≤ ak ≤ aj für alle i < k und alle j > k. Divide-and-conquer für das Sortieren I A totalgeordneten Menge I a = ha1 , a2 , . . . , an i Liste von Elementen aus A I Das Element ak in der Position k ist ein Splitter für a, wenn gilt ai ≤ ak ≤ aj für alle i < k und alle j > k. I Ist ak Splitter für a, so kann man das Sortieren der Liste a durch einen divide-and-conquer-Ansatz erledigen, indem beide Teillisten a` = ha1 , a2 , . . . , ak−1 i und ar = hak+1 , ak+2 , . . . , an i in-place separat sortiert werden – denn das Element ak steht bezüglich des Sortierens bereits an der richtigen Stelle. Divide-and-conquer für das Sortieren I A totalgeordneten Menge I a = ha1 , a2 , . . . , an i Liste von Elementen aus A I Das Element ak in der Position k ist ein Splitter für a, wenn gilt ai ≤ ak ≤ aj für alle i < k und alle j > k. I Ist ak Splitter für a, so kann man das Sortieren der Liste a durch einen divide-and-conquer-Ansatz erledigen, indem beide Teillisten a` = ha1 , a2 , . . . , ak−1 i und ar = hak+1 , ak+2 , . . . , an i in-place separat sortiert werden – denn das Element ak steht bezüglich des Sortierens bereits an der richtigen Stelle. I Schematisch sort(a) = hsort(a` ), ak , sort(ar )i Beispiel – (3, 1, 2, 4, 7, 5, 6) hat 4 als Splitter – (5, 1, 2, 4, 7, 3, 6) hat keinen Splitter – (1, 2, 3, 4, 5, 6, 7) hat alle 7 Positionen als Splitter Splitter sind selten! I Splitter sind selten! Die “meisten” Listen haben überhaupt keinen Splitter. Splitter sind selten! I Splitter sind selten! Die “meisten” Listen haben überhaupt keinen Splitter. I Sn Menge der n! Permutationen einer n-elementigen totalgeordneten Menge, z.B. der Zahlen {1, 2, . . . , n}. Splitter sind selten! I Splitter sind selten! Die “meisten” Listen haben überhaupt keinen Splitter. I Sn Menge der n! Permutationen einer n-elementigen totalgeordneten Menge, z.B. der Zahlen {1, 2, . . . , n}. Quantitativ I I I Die Position k ist ein Splitter in (k − 1)! · (n − k)! Permutationen aus Sn . Die Gesamtzahl sn der Splitter in Sn sn := n X k=1 (k − 1)! (n − k)! = (n − 1)! · −1 n−1 X n−1 k=0 k Splitter sind selten! I Splitter sind selten! Die “meisten” Listen haben überhaupt keinen Splitter. I Sn Menge der n! Permutationen einer n-elementigen totalgeordneten Menge, z.B. der Zahlen {1, 2, . . . , n}. Quantitativ I I I Die Position k ist ein Splitter in (k − 1)! · (n − k)! Permutationen aus Sn . Die Gesamtzahl sn der Splitter in Sn sn := n X k=1 I (k − 1)! (n − k)! = (n − 1)! · −1 n−1 X n−1 k=0 k Für die mittlere Anzahl von Spittern in einer Permutation aus Sn gilt sn 2 sn = ∼ n! n Hoare’s Idee I Mittels einer einfachen Umordnung einer zu sortierenden Liste das Vorhandensein eines Splitter in einer bekannten Position zu garantieren Hoare’s Idee I Mittels einer einfachen Umordnung einer zu sortierenden Liste das Vorhandensein eines Splitter in einer bekannten Position zu garantieren I Das leistet der Algorithmus Split (auch Partition genannt) Hoare’s Idee I Mittels einer einfachen Umordnung einer zu sortierenden Liste das Vorhandensein eines Splitter in einer bekannten Position zu garantieren I Das leistet der Algorithmus Split (auch Partition genannt) I Verfahren rekursiv auf Teillisten vor und hinter dem Splitter anwenden Hoare’s Idee I Mittels einer einfachen Umordnung einer zu sortierenden Liste das Vorhandensein eines Splitter in einer bekannten Position zu garantieren I Das leistet der Algorithmus Split (auch Partition genannt) I Verfahren rekursiv auf Teillisten vor und hinter dem Splitter anwenden Komplexitätsanalyse I I I Im worst-case ist das ein O(n2 )-Verfahren Interessant ist der average-case bei zufällig gewählten pivots Hoare’s Idee I Mittels einer einfachen Umordnung einer zu sortierenden Liste das Vorhandensein eines Splitter in einer bekannten Position zu garantieren I Das leistet der Algorithmus Split (auch Partition genannt) I Verfahren rekursiv auf Teillisten vor und hinter dem Splitter anwenden Komplexitätsanalyse I I I I Im worst-case ist das ein O(n2 )-Verfahren Interessant ist der average-case bei zufällig gewählten pivots V quick (n) : die mittlere Anzahl von Vergleichoperationen des Algorithmus quicksort für Listenlänge n 2 6 1 8 7 5 3 4 2 6 1 8 7 5 3 4 2 6 1 8 7 5 3 4 5 6 1 8 7 2 3 4 2 6 1 8 7 5 3 4 5 6 1 8 7 2 3 4 5 6 1 8 7 2 3 4 2 6 1 8 7 5 3 4 5 6 1 8 7 2 3 4 5 6 1 8 7 2 3 4 5 1 6 8 7 2 3 4 2 6 1 8 7 5 3 4 5 6 1 8 7 2 3 4 5 6 1 8 7 2 3 4 5 1 6 8 7 2 3 4 2 6 1 8 7 5 3 4 5 6 1 8 7 2 3 4 5 6 1 8 7 2 3 4 5 1 6 8 7 2 3 4 5 1 2 8 7 6 3 4 2 6 1 8 7 5 3 4 5 6 1 8 7 2 3 4 5 6 1 8 7 2 3 4 5 1 6 8 7 2 3 4 5 1 2 8 7 6 3 4 5 1 2 8 7 6 3 4 2 6 1 8 7 5 3 4 5 6 1 8 7 2 3 4 5 6 1 8 7 2 3 4 5 1 6 8 7 2 3 4 5 1 2 8 7 6 3 4 5 1 2 8 7 6 3 4 5 1 2 3 7 6 8 4 2 6 1 8 7 5 3 4 5 6 1 8 7 2 3 4 5 6 1 8 7 2 3 4 5 1 6 8 7 2 3 4 5 1 2 8 7 6 3 4 5 1 2 8 7 6 3 4 5 1 2 3 7 6 8 4 5 1 2 3 7 6 8 4 2 6 1 8 7 5 3 4 5 6 1 8 7 2 3 4 5 6 1 8 7 2 3 4 5 1 6 8 7 2 3 4 5 1 2 8 7 6 3 4 5 1 2 8 7 6 3 4 5 1 2 3 7 6 8 4 5 1 2 3 7 6 8 4 5 1 2 3 4 6 8 7 2 6 1 8 7 5 3 4 5 6 1 8 7 2 3 4 5 6 1 8 7 2 3 4 5 1 6 8 7 2 3 4 5 1 2 8 7 6 3 4 5 1 2 8 7 6 3 4 5 1 2 3 7 6 8 4 5 1 2 3 7 6 8 4 5 1 2 3 4 6 8 7 4 1 2 3 5 6 8 7 Algorithm 1 Vertauschen von zwei Listenelementen procedure Swap(A :: array (integer ), u, v :: integer ) local tmp if not u = v then tmp ← A[u] A[u] ← A[v ] A[v ] ← tmp end if end procedure Algorithm 2 Splitten einer Liste mit randomisiertem Pivotelement procedure Split(A :: array (integer ),left,right,pivotpos :: integer ) local i, j, r r ← rand(left..right)() Swap(A, left, r ) i ← left for j = left + 1..right do if A[j] < A[left] then i ←i +1 Swap(A, i, j) end if end for Swap(A, left, i) pivotpos ← i end procedure Algorithm 3 Quicksort rekursiv procedure Qksort(A :: array (integer ), left, right :: integer ) local k if left < right then Split(A, left, right, k) Qksort(A, left, k − 1) Qksort(A, k + 1, right) end if end procedure Algorithm 4 Quicksort procedure Quicksort(A :: array (integer )) Qksort(A, 1, length(A)) end procedure Kombinatorik/Stochastik des Splittens I σ ∈ Sn zufällige Permutation von {1, 2, . . . , n} Kombinatorik/Stochastik des Splittens I σ ∈ Sn zufällige Permutation von {1, 2, . . . , n} I zufälliges Element k ∈ {1, 2, . . . , n} als Pivotelement Kombinatorik/Stochastik des Splittens I σ ∈ Sn zufällige Permutation von {1, 2, . . . , n} I zufälliges Element k ∈ {1, 2, . . . , n} als Pivotelement Split erzeugt I I I zufällige Permutation σ ` ∈ Sk−1 der Menge {1, 2, . . . , k − 1} (gleichverteilt) zufällige Permutation σ r der Menge {k + 1, k + 2, . . . , n} (gleichverteilt) Kombinatorik/Stochastik des Splittens I σ ∈ Sn zufällige Permutation von {1, 2, . . . , n} I zufälliges Element k ∈ {1, 2, . . . , n} als Pivotelement Split erzeugt I I I I zufällige Permutation σ ` ∈ Sk−1 der Menge {1, 2, . . . , k − 1} (gleichverteilt) zufällige Permutation σ r der Menge {k + 1, k + 2, . . . , n} (gleichverteilt) Symbolisch: σ 7→ hσ ` , k, σ r i Die Quicksort-Rekursion I Aus der Stochastik des Splittens ergibt sich n 1 X V quick (n) = (n−1)+ · V quick (k − 1) + V quick (n − k) n k=1 Die Quicksort-Rekursion I Aus der Stochastik des Splittens ergibt sich n 1 X V quick (n) = (n−1)+ · V quick (k − 1) + V quick (n − k) n k=1 I dies lässt sich umformen zu n−1 2 X V quick (n) = (n−1)+ · V quick (k) (n ≥ 2), n k=0 V quick (1) = 0 Die Quicksort-Rekursion I Aus der Stochastik des Splittens ergibt sich n 1 X V quick (n) = (n−1)+ · V quick (k − 1) + V quick (n − k) n k=1 I dies lässt sich umformen zu n−1 2 X V quick (n) = (n−1)+ · V quick (k) (n ≥ 2), n V quick (1) = 0 k=0 I Abgekürzt geschrieben mit F (n) = V quick (n): n−1 2 X F (n) = n − 1 + · F (k) n k=0 F (0) = F (1) = 0. Einige Werte F (1) = 0 F (2) = 1 F (3) = 8/3 = 2.66... F (4) = 29/6 = 4.83... F (5) = 37/5 = 7.4 F (6) = 10.3 F (7) = 13.48... F (8) = 16.92... F (9) = 20.57... F (10) = 30791/1260 = 24.43... F (100) = 647.85... F (1000) = 10985.9... Die Komplexitätsaussage I Harmonische Zahlen n 1 1 1 X1 1 Hn = 1 + + + . . . + = = ln n + γ + O( ) 2 3 n j n j=1 Die Komplexitätsaussage I Harmonische Zahlen n 1 1 1 X1 1 Hn = 1 + + + . . . + = = ln n + γ + O( ) 2 3 n j n j=1 I Theorem: Für die mittlere Anzahl der Vergleichsoperationen von Quicksort gilt V quick (n) = F (n) = 2 (n + 1) Hn − 4 n und somit V quick (n) ∼ 2 n ln n ∈ Θ(n log n) Figure: Graph von F (n) (blau) und von 2 n ln n (rot) Figure: Graph von F (n)/(2 n ln n) Weitere Werte F (104 ) = 155771.6... F (105 ) = 0.2018... 107 F (106 ) = 0.2478... 108 F (107 ) = 0.2939... 109 F (108 ) = 0.3399... 1010 F (109 ) = 0.3860... 1011 F (1010 ) = 0.4320... 1012 Lösung der Quicksort-Rekursion I Rekursionsbeziehung n−1 2X F (n) = n − 1 + F (i) n i=0 Lösung der Quicksort-Rekursion I Rekursionsbeziehung n−1 2X F (n) = n − 1 + F (i) n i=0 I Äquivalent n F (n) = n(n − 1) + 2 n X i=1 F (i − 1) (n > 0) Lösung der Quicksort-Rekursion I Rekursionsbeziehung n−1 2X F (n) = n − 1 + F (i) n i=0 I Äquivalent n F (n) = n(n − 1) + 2 n X F (i − 1) (n > 0) i=1 I Durch Subtraktion n F (n) − (n − 1) F (n − 1) = 2 (n − 1) + 2 F (n − 1) Lösung der Quicksort-Rekursion I Rekursionsbeziehung n−1 2X F (n) = n − 1 + F (i) n i=0 I Äquivalent n F (n) = n(n − 1) + 2 n X F (i − 1) (n > 0) i=1 I Durch Subtraktion n F (n) − (n − 1) F (n − 1) = 2 (n − 1) + 2 F (n − 1) I Umgeformt n F (n) − (n + 1) F (n − 1) = 2 (n − 1). Lösung der Quicksort-Rekursion (Forts.) I F (n) durch (n + 1) G (n) ersetzen G (n) − G (n − 1) = 2 (n − 1) (n > 0) , G (0) = 0 n (n + 1) Lösung der Quicksort-Rekursion (Forts.) I F (n) durch (n + 1) G (n) ersetzen G (n) − G (n − 1) = I 2 (n − 1) (n > 0) , G (0) = 0 n (n + 1) Teleskop-Trick G (n) = n X i=1 n X {G (i) − G (i − 1)} 2 (i − 1) i (i + 1) i=1 n X 2 1 = 2 − i +1 i i=1 n n X X 2 2 1 = 2 − +2 i +1 i i i=1 i=1 2 = 2 − 2 + 2 Hn n+1 =