H.-Christian Estler Paderborn 23. April 2007 Übungen zur Vorlesung Datenstrukturen und Algorithmen SS 2007 Beispiellösung Blatt 1 Aufgabe 1: Gegeben: Laufzeitfunktion des Algorithmus A fA (n) = 2n2 Laufzeitfunktion des Algorithmus B fB (n) = 50n log n Computer C1 berechnet 1016 Basisoperationen pro Sekunde Computer C2 berechnet 6 ∗ 109 Basisoperationen pro Sekunde a) Berechnung der Rechenzeiten t1 , t2 für n = 1013 Algorithmus A auf C1 : t1 fA (1013 ) 1016 2 ∗ (1013 )2 = 1016 2 ∗ 1026 = 1016 = 2 ∗ 1010 = Algorithmus B auf C2 : t2 = = = = = fB (1013 ) 6 ∗ 109 50 ∗ 1013 ∗ log(1013 ) 6 ∗ 109 50 ∗ 104 ∗ 13 ∗ log(10) 6 325 ∗ 104 ∗ log2 (10) 3 4 325 ∗ 10 ln(10) ∗ 3 ln(2) Damit folgen die Rechenzeiten t1 = 20000000000 Sekunden (t1 ≈ 5555555.56 Stunden) und t2 ≈ 3598755 Sekunden (t2 ≈ 999.7 Stunden). 1 b) Um die Frage zu beantworten für welche Werte von n Algorithmus A schneller ist, und für welche Werte Algorithmus B schneller ist, untersuchen wir zunächst die Frage, für welchen Wert von n die Wahl des Algorithmus gleichgültig ist. Also für welches n gilt: 2n2 = 50n log(n) ⇒ n = 25 log(n) n = 25 ⇒ log(n) Da sich diese Gleichung nicht nach n auflösen lässt, suchen wir uns eine Zahl n̂, die als bequeme ” Ausgangsbasis“ für die Abschätzung von n dienen soll. Da wir stets den Logarithmus zur Basis 2 betrachten, sei für den Anfang n̂ = 28 = 256. Wir überprüfen: 256 n̂ = = 32 > 25 log(n̂) 8 Ein Test mit n̂ = 27 ergibt 128 7 ≈ 18.3 < 25. Der wahre Wert für n muss also zwischen 128 und 256 liegen. Der Idee der binären Suche folgend, nehmen wir nun den Mittelwert aus dem Intervall 192 [128, 256], sei also n̂ = 192. Wir überprüfen log(192) ≈ 25.3. Da das Ergebnis schon sehr nahe an dem gewünschten Wert von 25 liegt, testen wir die nächst kleineren ganzen Zahlen vor 192 und finden, dass für den gesuchten Wert von n gelten muss: 189 < n < 190 . Für n ≥ 190 ist also die Laufzeit von Algorithmus B kleiner als die von Algorithmus A (Anmerkung: strenggenommen müsste man dies jetzt noch beweisen, z.B. per Induktion). Für n ≤ 189 ist Algorithmus A vorzuziehen (kann notfalls per nachrechnen“ für jeden Wert ” bewiesen werden). Aufgabe 2: a) Der unten angegebene Algorithmus Binary-Add bekommt als Eingabe zwei Arrays A[1 . . . n] und B[1 . . . n], deren Elemente A[i], B[i] aus der Menge {0, 1} sind. Die repräsentierten Binärzahlen seien dabei von links nach rechts zu lesen. A[1], B[1] bzw. C[1] stellen also jeweils das LSB (least significant bit) und A[n], B[n] bzw. C[n + 1] das MSB (most significant bit) dar. Die Binärzahlen werden addiert und das Ergebnis in einem Array C[1 . . . n + 1] zurückgegeben. Algorithmus 1 : addieren zweier Binärzahlen Binary-Add (A[1 . . . n], B[1 . . . n]): 1 C[1] ← 0 2 for i ← 1 to n 3 do sum ← A[i] + B[i] + C[i] 4 if sum ≤ 1 5 then C[i] ← sum 6 C[i + 1] ← 0 8 else if sum = 2 9 then C[i] ← 0 10 C[i + 1] ← 1 11 else C[i] ← 1 12 C[i + 1] ← 1 13 return C times 1 n+1 n n t1 t1 t2 t3 t3 t4 t4 1 2 b) Um die Korrektheit des Algorithmus zu zeigen formulieren wir eine geeignete Schleifeninvariante und beweisen, dass diese • vor dem ersten Schleifendurchlauf erfüllt ist (Induktionsanfang) • mit der Annahme, dass sie für einen Schleifendurchlauf mit Index i − 1 gilt (Induktionsannahme), auch für einen Durchlauf mit Index i gilt (Inducktionsschritt) • nach Beendigung der Schleife etwas über die Korrektheit des Algorithmus aussagt (Korrektheit) Invariante: Vor dem Schleifendurchlauf mit Index i enthält C[1, . . . , i] das Ergebnis der binären Addition von A[1, . . . , i − 1] und B[1, . . . , i − 1]. Induktionsanfang: Der kleinste Index der Schleife ist i = 1, nämlich wenn length(A) = length(B) = n > 0. Zuvor ist C[1] = 0 (was ein korrektes Ergebnis für die nicht durchgeführte“ binäre Addi” tion ist). Induktionsschritt: Mit der Annahme, dass die Invariante für einen Schleifendurchlauf i − 1 gilt (Induktionsvorraussetzung, in der Initialisierung gezeigt), zeigen wir, dass sie auch für einen Schleifendurchlauf i gilt: if - und else- Verzweigungen in den Zeilen 4 - 11 addieren korrekt die i-ten Werte A[i] und B[i], wie nachfolgend beschrieben: Fall 1: sum = A[i] + B[i] + C[i] ≤ 1 ist genau dann der Fall, wenn (A[i] = B[i] = C[i] = 0), oder wenn (A[i] = 1 ∧ B[i] = 0 ∧ C[i] = 0), oder wenn (A[i] = 0 ∧ B[i] = 1 ∧ C[i] = 0), oder wenn (A[i] = 0 ∧ B[i] = 0 ∧ C[i] = 1). Bei keiner dieser vier Kombinationen liefert die binäre Addition einen Übertrag, weshalb C[i + 1] = 0 wird. In C[i] wird korrekt das Ergebnis der binären Addition (sum) gespeichert. Fall 2: sum = A[i] + B[i] + C[i] = 2 ist genau dann der Fall, wenn (A[i] = 1 ∧ B[i] = 1 ∧ C[i] = 0), oder wenn (A[i] = 1 ∧ B[i] = 0 ∧ C[i] = 1), oder wenn (A[i] = 0 ∧ B[i] = 1 ∧ C[i] = 1). Bei jeder dieser drei Kombinationen liefert die binäre Addition einen Übertrag, weshalb C[i + 1] = 1 wird. In C[i] wird korrekt das Ergebnis der binären Addition (sum = 0) gespeichert. Fall 3: Gilt weder Fall 1 noch Fall 2, so muss gelten (A[i] = 1 ∧ B[i] = 1 ∧ C[i] = 1). Bei dieser Kombination liefert die binäre Addition einen Übertrag, weshalb C[i + 1] = 1 wird. In C[i] wird korrekt das Ergebnis der binären Addition (sum = 1) gespeichert. Die Fälle 1-3 behandeln alle möglichen Kombinationen von (A[i], B[i], C[i]). Nach einem Schleifendurchlauf i (und damit vor Schleifendurchlauf i + 1) steht in C[1, . . . , i + 1] das Ergebnis der binären Addition von A[1, . . . , i] und B[1, . . . , i]. Korrektheit: Vor dem Durchlauf mit i = n + 1 ist C[1, . . . , n + 1] das korrekte Ergebnis der binären Addition von A[1, . . . , n] und B[1, . . . , n]. 3 c) Im Folgenden werden wir die Laufzeit T (n) des Binary-Add Algorithmus untersuchen, um eine Aussage über seine Worst-Case Laufzeit treffen zu können. Der obige Pseudocode des Binary-Add Algorithmus enthält bereits eine Auflistung der Anzahl der Ausführungen (times) jeder einzelnen Zeile 1 . Für die Anzahl der Ausführungen t1 , t3 , t4 gilt t1 + t3 + t4 = n, da aufgrund der if - und else-Verzweigungen, stets einer der zugehörigen Codeabschnitte ausgeführt werden muss. Ebenso sehen wir, dass t2 ≤ n gelten muss, denn die Zeile wird nur dann ausgeführt, wenn die erste if -Bedingung falsch ist. Damit können wir zusammenfassen: T (n) = 1 + (n + 1) + n + n + 2(t1 + t3 + t4 ) + t2 + 1 = 3n + 3 + 2n + t2 ≤ 5n + 3 + n (ersetze t2 durch n) = 6n + 3 Um zu zeigen, dass T (n) = O(n) ist, betrachten wir den Grenzwert des Quotienten n → ∞. Es gilt: 6n + 3 3 = lim 6 + = 6 lim n→∞ n→∞ n n Und da 0≤6<∞ 6n+3 n für ist die Lauftzeit von Binary-Add also in O(n) womit wir eine obere Schranke für die Worst-Case Laufzeit nachgewiesen haben. Aufgabe 3: a) Algorithmus 2 berechnet aus den Koeffizienten a0 , . . . , an−1 und der rellen Zahl x den Wert Pn−1 i i=0 ai x . Algorithmus 2 : Summe über ai xi Summe (a0 . . . an−1 , x): 1 result ← 0 2 for i ← 0 to n − 1 3 do x potenz ← 1 4 for k ← 1 to i 5 do x potenz ← x potenz ∗ x 6 result ← result + a[i] ∗ x potenz 7 return result times 1 n+1 n n 2 (n + 1) n 2 (n + 1) − 1 n 1 Laufzeitanalyse: Bevor gezeigt wird, dass Summe in Θ(n2 ) liegt, wollen wir kurz darauf eingehen, wie sich die Anzahl der Aufrufe in Zeile 4 ergibt. Der Rumpf der äußeren for-Schleife (Zeile 2) wird genau n mal durchlaufen. Wir können dies auch Pn−1 schreiben als i=0 1 = n, was das Zählen“ etwas deutlicher werden lässt. ” Sei nun für einen Augenblick der beliebig, aber fest gewählt. Dann wird der innere Pi Wert von i P i+1 Schleifenkopf (Zeile 4) i + 1 = ( k=1 1) + 1 = k=1 1 mal ausgeführt. Wollen wir nun wissen, wie oft Zeile 4 insgesamt durchlaufen wird, so betrachten wir: n−1 i+1 XX i=0 k=1 1= n−1 X i+1= i=0 n X i=1 i= n (n + 1) 2 1 Wie in der Vorlesung vereinbart, sind die Kosten einer jeden Pseudocodezeile genau 1. Sie werden deshalb nicht explizit aufgelistet. 4 Bestimmen wir nun die Laufzeit T (n): T (n) n n 1 + (n + 1) + n + ( (n + 1)) + ( (n + 1) − 1) + n + 1 2 2 n2 n n2 n = 1+n+1+n+ + + + −1+n+1 2 2 2 2 2 = n + 4n + 2 = Wir bestimmen wieder den Grenzwert des Quotienten T (n) n2 für n → ∞: n2 (1 + n4 + n2 + 4n + 2 = lim n→∞ n→∞ n2 n2 lim 2 n2 ) =1 Und da 0 < 1 < ∞ folgt: T (n) = Θ(n2 ). b) Algorithmus 3 stellt eine Implementierung des Horner-Schemas dar. Algorithmus 3 : Horner-Schema Horner (a0 . . . an−1 , x): 1 result ← an−1 2 for i ← n − 2 downto 0 3 do result ← result ∗ x + ai 4 return result times 1 n n−1 1 Laufzeitanalyse: Die Laufzeit lässt sich in diesem Fall sehr einfach bestimmen. Es gilt: T (n) = 1 + n + (n − 1) + 1 = 2n + 1 Auch hier bestimmen wir den Grenzwert des Quotienten T (n) n für n → ∞: n(2 + n1 ) 2n + 1 = lim =2<∞ n→∞ n→∞ n n 0 < lim Damit gilt T (n) = Θ(n). Alternativ lässt sich das Horner-Schema auch sehr einfach rekursiv implementieren. Algorithmus 4 zeigt eine solche Lösung. Der Aufruf sei Horner rek(a0 . . . an−1 , x). Algorithmus 4 : Horner-Schema (rekursiv) Horner rek (ai . . . aj , x): 1 if i = j 2 then return ai 3 else Horner rek(ai+1 . . . aj , x) ∗ x + ai 5 times 1 1 T (n − 1) Laufzeitanalyse: Um zu beweisen, dass der Algorithmus in Θ(n) liegt, müssen wir die folgende Rekursionsgleichung lösen: ( 1 + 1, wenn n = 1 T (n) = T (n − 1) + 1 sonst Wir lösen die Rekursionsgleichung: T (n) Und da 0 < limn→∞ n+1 n = = .. . = = T (n − 1) + 1 T (n − 2) + 2 T (1) + (n − 1) 2+n−1 = 1 < ∞ folgt: T (n) = Θ(n). Aufgabe 4: a) Im Folgenden repräsentiere i ↔ j den Vertauschungsoperator zweier Element i und j (dies lässt sich alternativ auch durch 3 Zeilen Pseudocode ausdrücken. Algorithmus 5 : Bubble-Sort Bubbble-Sort(A[1 . . . n]): 1 j←n 2 while j ≥ 2 3 do for i ← 1 to j − 1 4 do if A[i] > A[i + 1] 5 then A[i] ↔ A[i + 1] 6 j ←j−1 times 1 n n 2 (n + 1) − 1 n 2 (n + 1) − 2 t1 ≤ n2 (n + 1) − 2 n−1 Laufzeitanalyse: Wie schon in den obigen Aufgaben bestimmen wir die Laufzeit. T (n) n n 1 + n + ( (n + 1) − 1) + ( (n + 1) − 2) + t1 + (n − 1) 2 2 n n2 n n2 + − 1) + ( + − 2) + t1 + n − 1 = 1+n+( 2 2 2 2 = n2 + 3n + t1 − 3 = An dieser Stelle sehen wir, dass die Laufzeit von der Anzahl der Ausführungen der Zeile 5, also von t1 abhängt. Wir wissen aber, dass 0 ≤ t1 ≤ n2 (n + 1) − 2 gelten muss. Damit können wir sowohl eine untere auch eine obere Laufzeitschranke bestimmen: untere Schranke: T (n) = n2 + 3n + t1 − 3 ≥ n2 + 3n − 3 6 Es gilt: 0 < limn→∞ n2 +3n−3 n2 = 1 ≤ ∞ und damit T (n) = Ω(n2 ). obere Schranke: T (n) Es gilt: 0 ≤ limn→∞ 3 2 7 2 n + 2 n−5 n2 = 3 2 = n2 + 3n + t1 − 3 n ≤ n2 + 3n + (n + 1) − 2 − 3 2 3 2 7 n + n−5 = 2 2 < ∞ und damit auch T (n) = O(n2 ). Und da T (n) = Ω(n2 ) und T (n) = O(n2 ) folgt T (n) = Θ(n2 ). b) Wir modifizieren nun Bubble-Sort wie in der Aufgabenstellung beschrieben. Dafür führen wir eine neue Variable δ ein, welche uns sagt, für welches Paar“ (p, p + 1) der letzte notwendige ” Compare-Exchange Schritt durchgeführt werden musste. Algorithmus 6 : modifizierter Bubble-Sort Mod-Bubbble-Sort (A[1 . . . n]): 1 j←n 2 while j ≥ 2 3 δ←0 4 do for i = 1 to j − 1 5 do if A[i] > A[i + 1] 6 then A[i] ↔ A[i + 1] 7 δ←i 8 j←δ c) Wir betrachten nun Best-Case Eingaben für beide Algorithmen. Warum diese Eingaben Best-Case Eingaben sind, wird in Aufgabenteil d) beantwortet. Bubble-Sort: Ein aufsteigend sortiertes Array A[1, . . . , n], so dass A[p] < A[q] mit 1 ≤ p < q ≤ n. Mod-Bubble-Sort: Ein aufsteigend sortiertes Array A[1, . . . , n], so dass A[p] < A[q] mit 1 ≤ p < q ≤ n. d) Betrachten wir nun die Laufzeiten beider Algorithmen für Best-Case Eingaben. Bubble-Sort: Wir haben bereits gezeigt, dass Θ(n2 ) für die Laufzeit dieses Algorithmus gilt. Damit haben wir sowohl eine untere, als auch eine obere Schranke bewiesen. Wir können also lediglich erwarten, dass bei einer Best-Case Eingabe niederwertige Terme oder Konstanten, welche in der asymptotischen Notation verschwinden, klein sind. Bei gegebener Implementierung müssen beide for-Schleifen stets durchlaufen werden. Der Vergleich in Zeile 3 muss ebenfalls jedesmal durchgeführt werden. Bei Best-Case Eingabe ist dieser aber stets falsch, weshalb (und das ist der einzige Zeitgewinn) die Vertauschungoperation in Zeile 7 4 niemals ausgeführt werden muss (t1 = 0). Mod-Bubble-Sort: In diesem Fall können wir mit der Eingabe eines sortierten Arrays (wie in c) beschrieben) die Laufzeit auf Θ(n) reduzieren. Ist das Eingabe-Array sortiert, wird die äußere Schleife (Zeile 2) zunächst für j = n durchlaufen. Die innere Schleife (Zeile 4) wird nun für i = 1 bis i = j − 1 = n − 1 ihren Rumpf ausführen. Da aber die if -Abfrage (Zeile 5) in einem sortieren Array niemals wahr wird, werden die Zeilen 6 und 7 niemals ausgeführt. Zeile 8 weist nun j den Wert δ = 0 zu. Damit ist die Ausführbedingung für die äußere Schleife nicht mehr gegeben und der Algorithmus terminiert. Fassen wir dies zusammen, so benötigt der Algorithmus konstante Zeit Θ(1) für die Zuweisungen in den Zeilen 1 und 3, ebenso Θ(1) für Zeile 2 (dass wir sie zwei mal ausführen verschwindet“ in der Θ-Notation), Θ(n) ” für die innere Schleife und schlussendlich nochmals Θ(1) für die Zuweisung in Zeile 8. Die gesamte Laufzeit ist folglich in Θ(n). e) Es folgen die Worst-Case Eingaben für beide Algorithmen. Bubble-Sort: Ein absteigend sortiertes Array A[1, . . . , n], so dass A[p] > A[q] mit 1 ≤ p < q ≤ n. Mod-Bubble-Sort: Ein absteigend sortiertes Array A[1, . . . , n], so dass A[p] > A[q] mit 1 ≤ p < q ≤ n. f) Betrachten wir nun die Laufzeiten beider Algorithmen für Worst-Case Eingaben. Bubble-Sort: Wie wir uns bereits in Aufgabeteil d) überlegt haben, müssen wir bei dieser Implementierung von Bubble-Sort beide Schleifen vollständig durchlaufen. Da aber das Eingabe-Array absteigend sortiert ist, wird nun in jedem Durchlauf j der äußeren Schleife lediglich das aktuell erste Element durch die innere Schleife an seine richtige Position gebracht. Dafür sind genau j − 1 Vertauschungen nötig, wobei Zeile 5 jedesmal ausgeführt werden muss. Es gilt weiterhin Θ(n2 ) für die Laufzeit, doch für times t1 gilt auch t1 = n2 (n + 1) − 2. Mod-Bubble-Sort: Die Modifikation bringt bei einer Worst-Case Eingabe keinerlei Verbesserung gegenüber dem normalen Bubble-Sort. Da, wie beim normalen Bubble-Sort, in jedem Durchlauf j der äußeren Schleife das aktuell erste Element durch die innere Schleife an seine richtige Position gebracht werden muss, sind auch hier wieder j − 1 Vertauschungen nötig. Der Wert von δ ist dadurch δ = j − 1 und der Wert, der j in Zeile 8 zugewiesen wird j = δ = j − 1 ist eine einfache Dekrementierung von j. Damit kann der Algorithmus (bis auf das δ) mit Bubblesort gleichgesetzt werden, weshalb sich auch die Argumentation für Laufzeit von Θ(n2 ) übertragen lässt. g) Für den Korrektheitsbeweis werden Invarianten sowohl für die innere, als auch für die äußere Schleife benötigt. Zudem nutzen wir einen Hilfssatz, welcher zunächst zu beweisen ist. Lemma 1: Es gibt keine Inversion bei zwei Elementen, die rechts von n − k + 1 stehen, wenn die letzte Vertauschung in Runde l die Elemente (n − k, n − k + 1) mit k > l behandelt hat. Beweis Lemma 1: Wir stellen zunächst fest, dass die innere for-Schleife des Algorithmus in Runde l (dies entspricht einem Schleifendurchlauf der äußeren Schleife mit Index j, sodass l = n−j +1) 8 alle Paare (p, p + 1) mit p < j, bei denen Inversion auftritt, behandelt. Nehmen wir nun an, es gibt ein Paar (n − z, n − z + 1) mit z < k, das eine Inversion hat, wobei das Paar (n − k, n − k + 1) mit k > l das letzte Paar ist, dass in Runde l durch eine Vertauschung behandelt wurde. Da wir bereits festgestellt haben, dass der Algorithmus bei einem Durchlauf in Runde l alle Inversionen korrekt auflöst, ist die Annahme nur möglich, wenn für das Paar (n − z, n − z + 1) gilt, dass z < l (das Paar also außerhalb des Bereichs liegt, der in der aktuellen Runde behandelt wird). Dann muss dieses Paar aber in einer Runde r < l behandelt worden sein, denn mindestens in der Runde l = 1 werden alle Paare (p, p + 1) mit p < j = n (also alle Paare (1, 2) . . . (n − 1, n)) einmal auf Inversion überprüft. Folglich muss unsere Annahme falsch sein und alle Paare hinter dem letzt behandelten Paar in Runde l müssen inversionsfrei sein. Invariante für die innere Schleife: Vor dem Durchlauf mit Index i ist A[i] ein größtes Element in A[1, . . . , i] und δ (mit 0 ≤ δ < i) ist gleich dem Wert p des letzten Paares (p, p + 1), für das ein C/E -Schritt ausgeführt wurde. Induktionsanfang: Vor dem ersten Durchlauf wird Index i auf 1 gesetzt und A[1] ist ein größtes Element in A[1, . . . , 1]. Da noch kein C/E -Schritt ausgeführt wurde, ist δ trivialerweise gleich Null. Induktionsschritt: Mit der Annahme (IV), dass die Invariante für einen Schleifendurchlauf i − 1 gilt (in der Initialisierung gezeigt), zeigen wir, dass sie auch für einen Schleifendurchlauf i gilt: Vor dem Durchlauf mit Index i steht in A[i] ein größtes Element aus A[1, . . . , i] und δ ist gleich dem Wert p des letzten Paares (p, p + 1), für das ein C/E -Schritt ausgeführt wurde. Beim Durchlauf mit Index i aktualisiert die if -Abfrage (Zeile 5, 6) so, dass der Wert in A[i + 1] ein größtes Element von A[1, . . . , i + 1] ist. Wird dabei ein C/E -Schritt auf einem Paar (p, p + 1) ausgeführt, so wird δ = p (Zeile 7). Andernfalls bleibt der Wert von δ = 0. Damit ist vor dem Schleifendurchlauf mit Index i + 1 das Element A[i + 1] ein größtes Element in A[1, . . . , i + 1] und δ (mit 0 ≤ δ < i + 1) ist gleich dem Wert p des letzten Paares (p, p + 1), für das ein C/E -Schritt ausgeführt wurde. Korrektheit: Vor dem Durchlauf mit Index i = j ist A[j] ein größtes Element in A[1, . . . , j] und δ (mit 0 ≤ δ < j) ist gleich dem Wert p des letzten Paares (p, p + 1), für das ein C/E -Schritt ausgeführt wurde. Invariante für die äußere Schleife: Vor dem Durchlauf der Schleife mit Index j ist A[j+1, . . . , n] aufsteigend sortiert und alle Elemente in A[1, . . . , j] sind kleiner oder gleich den Elementen in A[j + 1, . . . , n]. Induktionsanfang: Vor dem Durchlauf mit j = n ist A[n + 1, . . . , n] leer und die Aussage gilt trivialerweise. Induktionsschritt: Mit der Annahme (IV), dass die Invariante für einen Schleifendurchlauf j gilt, müssen wir zeigen, dass sie auch für einen Schleifendurchlauf j = δ (mit 0 ≤ δ < j) gilt. Nach Terminierung der inneren Schleife ist A[j] ein größtes Element in A[1, . . . , j] und δ (mit 0 ≤ δ < j) ist gleich dem Wert p des letzten Paares (p, p + 1), für das ein C/E -Schritt ausgeführt wurde. Nach Lemma 1 gilt für alle Paare, die größer dem Paar (δ, δ + 1) sind, Inversionsfreiheit. Das Paar (δ, δ + 1) selbst ist auch inversionsfrei, da auf ihm ein C/E -Schritt ausgeführt wurde. Daraus folgt, dass alle Elemente aus A[1, . . . , δ − 1] kleiner gleich den Elementen aus A[δ, . . . , j] sind, und dass A[δ, . . . , n] aufsteigend sortiert ist. Zeile 8 setzt j = δ. Vor dem Durchlauf mit j = δ gilt also, dass das Array A[δ+1, . . . , n] aufsteigend sortiert ist und alle Elemente in A[1, . . . , δ] kleiner oder gleich den Elementen in A[δ+1, . . . , n] sind. 9 Korrektheit: Vor dem Durchlauf mit j = 1 ist A[2, . . . , n] aufsteigend sortiert und alle Elemente in A[1, . . . , 1] sind kleiner oder gleich den Elementen in A[2, . . . , n]. Daraus folgt, dass A[1, . . . , n] aufsteigend sortiert ist. Im Fall j = 0 ist A[1, . . . , n] aufsteigend sortiert und alle Elemente in A[0, . . . , 0] sind trivialerweise kleiner gleich den Elementen in A[1, . . . , n]. 10