¨Ubungen zur Vorlesung Datenstrukturen und Algorithmen SS 2007

Werbung
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
Herunterladen