Algorithmen und Programme

Werbung
Kapitel 6
Algorithmen und Programme
Ene, mene, miste
was rappelt in der Kiste,
ene, mene, meck
und Du bist weg.
Schneller Ausschlussalgorithmus
Für die Formulierung von Lösungsverfahren, die auf einem Computer realisiert werden sollen, bedient man sich des Algorithmus“. Der euklidische Algorithmus ist ein herausragendes
”
Beispiel. Hier skizzieren wir, worauf es bei Algorithmen ankommt, und geben erste Beispiele,
im Laufe der nächsten Kapitel werden wir weitere Beispiele kennenlernen. Für die Analyse von
Algorithmen ist der Begriff Komplexität“ von Bedeutung.1
”
6.1
Algorithmen
Ein Computer ist ein Werkzeug zur Verarbeitung und
Speicherung von Information. Um ihn zu nutzen, ist er
mit Verarbeitungsvorschriften zu füttern“. Wir formulie”
ren solche Vorschriften in der Regel unter dem Stichwort
Algorithmus.
Ein Algorithmus2 für eine vorgegebene bestimmte Art von Aufgaben ist eine endliche
Abfolge von wohldefinierten, ausführbaren Vorschriften, die bei Abarbeitung, ausgehend von
einem Eingangszustand (Input) nach einer
endlichen Anzahl von Verarbeitungsschritten
einen Ausgangszustand (Output) bestimmen,
der als Lösung der durch den Eingangszustand
charakterisierten Aufgabe angesehen werden
kann.
Abbildung 6.1: AL–Khwarizmi
1
Die Begriffe Algorithmen, Programme, Programmiersprachen, . . .“ Informatikern zu erläutern, bedeutet
”
Eulen nach Athen tragen“. Wir tun dies, um sie in den mathematischen Kontext einzuordnen.
2
Die Bezeichnung leitet sich aus dem Namen Al–Khwarizmi (Al–Khwarizmi,780? — 850?), einem der bedeutensten Mathematiker des anfangenden Mittelalters, ab.
88
Algorithmen sind unabhängig von einer konkreten Programmiersprache und einem konkreten Computertyp, auf
denen sie ausgeführt werden. Algorithmen stehen im Zentrum der Informatik, der Numerischen
und der Diskreten Mathematik. Ein Algorithmus, der einen gewissen Kultstatus“ beanspru”
chen kann, ist der euklidische Algorithmus, den wir schon kennnengelernt haben, wir nutzen
einen Algorithmus zur Bestimmung des PageRank einer Web–Seite, wenn wir die Suchmaschine
google nutzen. Wir finden Algorithmen auch sonst im Alltag vor, wenn wir etwa einen Kuchen
backen: Backrezepte sind, wenn sie gut aufgeschrieben sind, Algorithmen für den Prozess des
Kuchenbackens.
Beispiel 6.1.1 Betrachte folgende Liste von Anweisungen:
EIN: Natürliche Zahl n .
step 1 k := 1, a := n .
step 2 Ist a (
= 1, dann gehe zu AUS.
3a + 1 falls a ungerade
step 3 a :=
a/2
falls a gerade
step 4 k := k + 1, gehe zu step 2.
AUS:
k: Länge der erzeugten Zahlenfolge.
Die Rechenschritte erklären sich selbst: ausgehend von n wird eine Folge von natürlichen Zahlen
erzeugt, eine so genannte Collatz/Uhlam/Warring-Folge.
Ist dies ein Algorithmus? NEIN, denn es ist nicht sichergestellt, dass die Abfrage
Ist a = 1, dann gehe zu AUS“
”
irgendwann zur Beendigung führt.
ABER: Bisher hat man keine natürliche Zahl gefunden, bei der die obige Liste von Anweisungen
nicht endet.
Unterschiedliche Algorithmen können entworfen werden zur Lösung ein und derselben Aufgabe. Leistungsunterschiede lassen sich herausarbeiten, wenn man ihren Aufbau und ihre Wirkungsweise analysiert. Fragestellungen dafür sind:
• Entwurf von Algorithmen: Wie soll ein Algorithmus zur Lösung einer bestimmten
Aufgabe aussehen?
• Berechenbarkeit: Gibt es Aufgaben, für die kein Algorithmus existiert?
• Komplexität: Wie lässt sich der Aufwand, der betrieben werden muss, um eine Problemklasse von Aufgaben zu lösen, bestimmen/abschätzen?
• Korrektheit: Wie lässt sich nachweisen, ob ein vorliegender Algorithmus die Aufgabe
korrekt löst?
• Robustheit/Zuverlässigkeit: Wie groß ist die Problemklasse von Aufgaben, die der
Algorithmus löst?
• Genauigkeit: Was ist die Qualität der Lösung, wenn numerisches Rechnen nötig ist?
89
Hauptziel der Analyse ist die Effizienzuntersuchung und die Entwicklung effizienterer Algorithmen. Diese Analyse sollte aber rechnerunabhängig durchgeführt werden. Dazu benötigt
man ein geeignetes Rechnermodell. Solche Modelle stehen zur Verfügung! Wir wollen hier nicht
darauf eingehen, unsere Analyseuntersuchungen stützen wir auf die Ermittlung des Rechenaufwands, ausgedrückt durch die Anzahl von elementaren Operationen. Hierbei kann man drei
Ansätze unterscheiden:
– Worst-case-Komplexität: Dies ist eine obere Schranke für den Aufwand in Abhängigkeit
vom Input.
– Mittlere Komplexität: Dies ist eine obere Schranke für den Aufwand in Abhängigkeit
vom Input bei gewissen Annahmen über das Auftreten des Inputs in der Problemklasse.
– Untere Komplexität: Hierunter versteht man die Ermittlung unterer Schranken für den
zu betreibenden Aufwand.
Diese Ansätze können rechnerunabhängig und a-priori erfolgen, d.h. ohne den Algorithmus zu
testen. Unter einer a-posteriori–Analyse versteht man das Testen des Algorithmus an Aufgaben
mit (hinreichend) großem Input.
6.2
Programme und Programmiersprachen
Die konkrete Ausführung eines Algorithmus nennt man einen Prozess. Die Einheit, die den
Prozess ausführt, ist ein Prozessor. Beim Kuchenbacken ist der Algorithmus das Rezept, der
Prozess die Abarbeitung des Rezepts, der Prozessor der Koch. Hier denken wir natürlich an
den Prozessor Computer“. Um eine Analyse des Ablaufs eines Algorithmus auf diesem Pro”
zessor vornehmen zu können, ist ein geeignetes Modell für den Computer (Maschinenmodell)
bereitzuhalten. Die Informatik studiert u.a. die Turing-Maschine und die Random-AccessMaschine (RAM), welche in gewissem Sinne sogar äquivalent sind. Die Analyse von Algorithmen auf einem abstrakten Niveau ist eine Disziplin der Informatik und/oder mathematischen
Informatik.
Die Hardware-Komponenten eines Computers sind
• Zentraleinheit (CPU)
• Speicher (Memory)
• Ein- Ausgabegeräte (Input, Output Devices)
Die Merkmale, die einen Computer bezüglich Hardware kennzeichnen sind Geschwindigkeit,
Speichergröße, Zuverlässigkeit, Kosten, Vernetzbarkeit.
Die Ausführung eines Algorithmus auf einem Prozessor setzt voraus, dass der Computer den
Algorithmus interpretieren können muss, d.h. er muss
• verstehen, was jeder Abarbeitungsschritt bedeutet,
• die jeweilige Operation ausüben können.
Dies leisten die Programmiersprachen. Die Algorithmen werden damit in Programmen aufgeschrieben; die einzelnen Schritte heißen nun (Programm–)Anweisung, Befehl. Bei einfachen Programmiersprachen (Maschinensprachen) kann jede Anweisung direkt vom Computer interpretiert werden. Da nur elementare Operationen damit erfaßt werden, muß man sehr
90
lange Programme schreiben. Zur Vereinfachung der Programmierung wurden höhere Programmiersprachen entwickelt. Programme in solchen Programmiersprachen können nicht direkt
durch den Computer interpretiert werden, sie werden durch Übersetzungsprogramme in die
Maschinensprache überführt. Der Übergang von Maschinensprachen zu höheren Programmiersprachen ist fließend. Es gibt eine ganze Hierarchie von Programmiersprachen:
• Basic, Fortran
• Algol
• Pascal, C, C++
• Java, Python, . . .
Auf Computern kann man Programme auf Vorrat“ für bestimmte Aufgaben ablegen. Diese
”
Sammlung von Programmen nennt man Software. Man unterscheidet
• Anwendungssoftware (Textverarbeitungssoftware, Statistiksoftware, . . . )
• Systemsoftware (Betriebssystem, Editor, Compiler, . . . )
Besonderen Stellenwert nehmen Software-Pakete ein wie Maple, Mathematica, Derive,
Matlab, Cinderella, die alle eine spezielle Ausrichtung haben: symbolisches Rechnen die
ersten drei, numerisches Rechnen Matlab und geometrisches Rechnen das letzte.
6.3
Rekursion
Ein Objekt wird als rekursiv bezeichnet, wenn es sich selbst als Teil enthält oder mit Hilfe
von sich selbst definiert ist. Rekursion kommt nicht nur in der Mathematik vor, sondern auch
im täglichen Leben (ein Bild im Spiegel im Spiegel . . . ). Rekursion kommt speziell in mathematischen Definitionen vor. Ein Beispiel haben wir schon kennengelernt: in der Definition der
natürlichen Zahlen kommt die zur Definition anstehende Menge N selbst vor. Ein anderes Beispiel ist die Fakultät einer natürlichen Zahl. Ihre rekursive Definition sieht so aus:
(
1
falls n = 1
n! :=
n · (n − 1)! falls n 6= 1
Es ist nicht überraschend, dass Rekursion sehr oft im Zusammenhang mit Objekten greift, die
mit natürlichen Zahlen im Zusammenhang stehen, da ja die natürlichen Zahlen selbst rekursiv
”
definiert sind“.
Das Wesentliche an der Rekursion ist die Möglichkeit, eine unendliche Menge von Objekten
durch eine endliche Aussage zu definieren oder eine unendliche Anzahl von Berechnungsschritten
durch ein endliches Programm zu beschreiben. Allerdings ist Vorsicht geboten, denn rekursive
Anweisungen bergen die Gefahr nicht abbrechender Ausführung; der Terminierung ist also besonderes Augenmerk zu schenken.
Hier führen wir zwei Beispiele an, die keine Hintergrundtheorie benötigen. Später kommen
wir zu einem weiteren Beispiel, nämlich zur rekursiven Behandlung des Problems des größten
gemeinsamen Teilers.
Die Türme von Hanoi3
3
Vermutlich wurde das Spiel 1883 vom französischen Mathematiker Édouard Lucas erfunden. Er dachte sich
dazu die Geschichte aus, dass indische Mönche im großen Tempel zu Benares, im Mittelpunkt der Welt, einen
Turm aus 64 goldenen Scheiben versetzen müssten, und wenn ihnen das gelungen sei, wäre das Ende der Welt
gekommen.
91
Wir betrachten drei Pfeiler i, j, k, auf die runde Scheiben mit unterschiedlichem Durchmesser
aufgesteckt werden können. Das Problem lautet: Es sind n Scheiben, die auf dem Pfeiler i
mit nach oben abnehmendem Durchmesser aufgesteckt sind, unter Zuhilfenahme des Pfeilers k
durch sukzessive Bewegung jeweils einer Scheibe auf den Pfeiler j umzuschichten. Dabei dürfen
die Pfeiler i, j, k verwendet werden aber immer unter der Maßgabe, dass niemals eine Scheibe
mit größerem Durchmesser auf einer mit einem kleinerem Durchmesser zu liegen kommt.
Man kann dieses Problem folgendermaßen lösen:
Man schichtet die obersten n − 1 Scheiben vom Pfeiler i auf den Pfeiler k unter
Zuhilfenahme von Pfeiler j den Regeln entsprechend; dann bringt man die auf dem
Pfeiler i verbliebene einzige (anfangs unterste) Scheibe auf den Pfeiler j . Nun ist der
Pfeiler i frei und man kann die n − 1 Scheiben vom Pfeiler k auf den Pfeiler j mit
Hilfe des Pfeilers i umschichten.
Es ist klar das rekursive Vorgehen zu erkennen: zur Lösung des Problems der Größe n bedienen
wir uns der Lösung der Größe n − 1 .
Wir benötigen die Bewegungsarten
bewege(m,von,über,nach), bringe(von,nach) .
Hierbei bedeutet bewege(l,a,b,c), dass die l obersten Scheiben vom Pfeiler a nach Pfeiler c den Regeln entsprechend unter Nutzung von b als Hilfspfeiler umzuschichten sind. Mit
bringe(a,b) wird die oberste Scheibe vom Pfeiler a auf den Pfeiler b gelegt. Die rekursive
Lösung für bewege(n,i,j,k) lautet damit:
Solange n > 0
bewege(n-1,i,j,k), bringe(i,j), bewege(n-1,k,i,j).
Beim Lösen der Aufgabe für n Scheiben, wird
Z(n) := 2n − 1 mal eine Scheibe umgelegt
Dies zeigt man induktiv. Der Induktionsbeginn ist trivial, der Induktionsschluss sieht so aus:
Z(n) = 1 + 2Z(n − 1) = 1 + 2(2n−1 − 1) = 2n − 1
Der Aufwand ist also enorm: für n = 64 müssen 264 − 1 ∼ 1021 Scheiben umgelegt werden.4
Allerdings sind wir ja nicht sicher, ob es nicht einen schnelleren Algorithmus gibt. Dies ist aber
nicht der Fall! (Man kann genauer hinsehen: Die kleinste Scheibe S1 wird bei jedem zweiten Zug
bewegt, die größte Scheibe Sn wird nur einmal bewegt, die Scheibe Sm wird genau 2n−m mal
bewegt.)
Die Ackermann-Funktion Als Beispiel für eine rekursive Funktionsdefinition komplexerer
Art betrachten wir das Beispiel der so genannten Ackermann-Funktion A(m, n) . Die Definition
lautet:


falls m = 0
n + 1
A(m, n) := A(m − 1, 1)
falls m 6= 0, n = 0 , m, n ∈ N0 .


A(m − 1, A(m, n − 1)) falls m 6= 0, n 6= 0
Die Ackermann-Funktion wächst sehr stark:
2
A(0, n) > n , A(1, n) > n + 1 , A(2, n) > 2n , A(3, n) > 2n , A(4, 3) > 22 , A(5, 4) > 1010000
4
Damit ist eine praktische Umsetzung der Lösung nur für kleine n möglich. Die Laufzeit des Algorithmus unter
der Annahme, dass eine Scheibe pro Sekunde verschoben wird, ist ca. 600 Milliarden Jahre bei 64 Scheiben. Das
Ende der Welt verzögert sich also noch.
92
Der Aufwand, um A(m, n) auszurechnen, wächst auch entsprechend. Beispielsweise erfordert die
Berechnung von A(1, 3) bereits folgende Rechenschritte:
A(1, 3) = A(0, A(1, 2)) = A(0, A(0, A(1, 1))) = A(0, A(0, A(0, A(1, 0))))
= A(0, A(0, A(0, A(0, 1)))) = A(0, A(0, A(0, 2))) = A(0, A(0, 3)) = A(0, 4) = 5
Es ist nicht sehr einfach einzusehen, dass die Rekursion terminiert; es ist so!
Satz 6.3.1 Für alle m, n ∈ N terminiert der Aufruf A(m, n) nach endlich vielen Schritten.
Beweis:
Der Beweis erfolgt durch zwei ineinander geschachtelte Induktionen über m (äußere Induktion)
und, bei festem m, über n (innere Induktion). Induktionsanfang: m = 0
Es folgt unabhängig von n nur ein Aufruf.
Induktionsvoraussetzung: Die Behauptung sei richtig für alle k mit 0 ≤ k ≤ m, und für alle
n ∈ N.
Induktionsschluss: m + 1
Induktion über n
n = 0: Hier wird für A(m + 1, 0) der Wert A(m, 1) zurückgegeben. Hierfür terminiert
der Aufruf nach Induktionsvoraussetzung der äußeren Induktion. Induktionsvoraussetzung: Der Aufruf A(m + 1, l) terminiert für (festes) m und alle l ≤ n.
Induktionsschluss: n + 1
Der Aufruf von A(m + 1, n) erzeugt den Aufruf von A(m, A(m + 1, n − 1)). Nach innerer Induktionsvoraussetzung terminiert der Aufruf A(m + 1, n − 1) und liefert eine
Zahl k. Dies erzeugt den Aufruf A(m, k), der nach äußerer Induktionsvoraussetzung
terminiert.
6.4
Ordnung, Listen und Sortieren
Bei den natürlichen Zahlen 1,2,3,. . . – und nicht nur dort – verwenden wir das Ungleichungszeichen “≤“. Es hat die Eigenschaften (x, y, z ∈ N)
x ≤ x;
x ≤ y und y ≤ x =⇒ y = x ;
x ≤ y und y ≤ z =⇒ x ≤ z ;
x ≤ y oder y ≤ x .
Wir nehmen dies zum Anlaß für
Definition 6.4.1 Sei X eine Menge. Eine Relation O ⊂ X × X heißt Halbordnung auf X,
falls die Relation reflexiv, antisymmetrisch und transitiv ist.
Ist zusätzlich noch
Für alle x, y ∈ X gilt (x, y) ∈ O oder (y, x) ∈ O
erfüllt, dann heißt O eine Ordnung auf X.
O
Meist schreibt man bei Vorliegen einer Halbordnung O statt (x, y) ∈ O auch x ≤ y oder kurz
x ≤ y , wenn der Zusammenhang klar ist.
93
Beispiel 6.4.2 Ist X eine Menge, dann ist in P OT (X) eine Halbordnung O definiert durch
(A, B) ∈ O : ⇐⇒ A ≤ B : ⇐⇒ A ⊂ B .
Beachte, dass nur in trivialen Fällen eine Ordnung vorliegt.
Beispiel 6.4.3 Sei A ein (endliches) Alphabet und seien An die Wörter der Länge n über dem
Alphabet A . Sei in A eine Ordnung ≤ gegeben.
Wir setzen für a = a1 . . . an , b = b1 . . . bn ∈ An :
a ≤ b : ⇐⇒ a = b oder ak ≤ bk für das kleinste k mit ak 6= bk .
lex
Dann ist ≤ eine Halbordnung in An . Man nennt sie die lexikographische Ordnung.
lex
Als Anwendung ordne man
0002, 0008, 0013, 0029, 0132, 1324
als Worte über dem in natürlicher Weise angeordneten Alphabet A := {0, 1, 2, . . . , 9} .
Beispiel 6.4.4 Auf N wird eine Halbordnung O definiert durch
(a, b) ∈ O : ⇐⇒ a|b .
Dies ist leicht zu verifizieren. Beachte, dass dies keine Ordnung ist, denn es gibt unvergleichli”
che“ Elemente, etwa 3, 4 .
Eine Liste besteht aus einer Sammlung von wohlbestimmten und wohlunterscheidbaren Objekten und ihrer Anordnung nach einem Prinzip; die leere Liste ist zugelassen.
Die Anordnung kann nach dem chronologischen Prinzip, nach einem alphabetischen Prinzip oder
allgemein mit einer Ordnung erfolgen. Kennt man alle Objekte der Liste, so kennt man die Liste;
Hat die Liste nur ganz wenige Elemente, so kann man sie einfach alle innerhalb einer eckigen
Klammer – damit machen wir den Unterschied zu Mengen klar – hinschreiben, durch Kommata
getrennt, auf die Reihenfolge kommt es hierbei offenbar an.
Maple - Illustration 6.1
Maple kennt den Datentyp Liste. Eine Liste ist eine in eckige
Klammern eingeschlossene Folge von Ausdrücken, Objekten.
Die Zahl der Einträge in einer
Liste erhält man mit dem Befehl nops. Das k-te Element einer Liste kann man sich durch
Anhängen des Index k an den Listennamen besorgen.
> L1:= [2,3,rot,Hahn,rot] ;
L1:= [2,3,rot,Hahn,rot]
> nops(L1);
5
> L1[4];
Hahn
Sei M eine endliche Menge mit n Elementen und versehen mit einer Ordnung ≤ . Sortieren
heißt, die Elemente von M so anzuordnen, dass sie bzgl. der Ordnung ≤ eine aufsteigende
Elementfolge bilden. Sortierverfahren werden benötigt etwa bei: Einordnen von Schlüsseln im
Werkzeugkasten, Ordnen der erhaltenen Karten beim Skatspiel, Sortieren von Dateien der Größe
nach. Gesichtspunkte für die Leistungsfähigkeit eines Sortierverfahrens sind:
94
Schnelligkeit. Wieviele Rechenoperationen (Vergleiche, Umstellen in einer Liste) in Abhängigkeit von n sind nötig? Dieser Aufwand wird Laufzeitkomplexität des Verfahrens genannt.
Speicherplatz. Im allgemeinen kann man sich die Elemente der Menge abgelegt in Fächern
vorstellen. Beim Sortieren kann es sinnvoll sein, Zusatzfächer zu benutzen. Der Bedarf an
Fächern in Abhängigkeit von n ist die Speicherplatzkomplexität des Verfahrens.
Sei nun eine Menge M = {a1 , . . . , an } vorgegeben. Wir denken uns die Elemente a1 , . . . , an
jeweils einzeln in einer Liste (Feld von Fächern) abgelegt. Wir sortieren diese Liste, indem wir
die Objekte in den Fächern irgendwie solange austauschen, bis sie angeordnet in den Fächern
liegen.
Sortieren durch Auswählen (Selection–sort).
Hier geht man folgendermaßen vor:
• Finde das kleinste Element und tausche es gegen das an der ersten Stelle befindliche Element (1. Schleife).
• Fahre in dieser Weise jeweils auf dem Rest des Feldes, das noch nicht sortiert ist fort (i–te
Schleife).
Man stellt leicht fest, dass in der i–ten Schleife n − i Vergleiche und eventuell ein Austausch
anfallen: Wegen
n−1
X
i=1
(n − i) =
n−1
X
j=1
1
1
j = ((1 + · · · + n − 1) + (n − 1 + · · · + 1)) = n(n − 1)
2
2
(6.1)
gilt für die Komplexität: Es fallen etwa n2 /2 Vergleiche und etwa n Austausche an. Auf den
Aufwand“ −n/2 bei den Vergleichen und −1 beim Austauschen kann man für große n verzich”
ten; etwa“ bedeutet diese Vernachlässigung, wir schreiben dafür meist ∼. Hierzu ein Beispiel,
”
wobei hier die Elemente die Buchstaben des Alphabets in ihrer alphabetischen Ordnung sind.
Anwendung von Selection–sort auf unser Beispiel EXAMPLE ergibt die Sequenz (a).
EXAMPLE
AXEMPLE
AEXMPLE
AEEMPLX
AEELPMX
AEELMPX
(a) Selection–sort
EXAMPLE
EXAMPLE
AEXMPLE
AEMXPLE
AEMPXLE
AELMPXE
AEELMPX
(b) Insert–sort
EXAMPLE
EAXMPLE
AEXMPLE
AEMXPLE
AEMPXLE
AEMPLXE
AEMLPXE
AELMPXE
AELMPEX
AELMEPX
AELEMPX
AEELMPX
EXAMPLE
EEAMPLX
EEALPMX
EEALMPX
EEAL
EEA
AEE
A
E
E
AEELMPX
(c) Bubble–sort
(d) Quick–sort
M
L
L
L
M
M
M
PX
P X
P X
P
X
Sortieren durch Einfügen (Insert–sort). Betrachte die Listenelemente der Reihe nach und
füge jedes an seinem richtigen Platz zwischen den bereits betrachteten ein, wobei diese sortiert
bleiben. Das gerade bestimmte Element wird eingefügt, indem die größeren Elemente um eine
Position nach rechts geschoben werden und das betrachtete Element auf dem frei gewordenen
95
Platz eingefügt wird.
Anwendung von Insert–sort auf unser Beispiel EXAMPLE ergibt die Sequenz (b). Man stellt
fest, dass für die Laufzeitkomplexität gilt: ∼ n2 /2 Vergleiche , ∼ n2 /4 Austausche .
Sortieren durch Austausch (Bubble–sort). Durchlaufe immer wieder das Feld und vertausche jedesmal, wenn es notwendig ist, benachbarte Elemente; wenn beim Durchlauf kein
Austausch mehr nötig ist, ist das Feld sortiert. Anwendung von Bubble–sort auf unser Beispiel
EXAMPLE ergibt die Sequenz (c). Man stellt fest, dass für die Laufzeitkomplexität gilt:
∼ n2 /2 Vergleiche , ∼ n2 /2 Austausche .
Sortieren nach Quick–sort. Dies ist der wohl am meisten angewendete Sortieralgorithmus.
Seine Idee geht auf C.A.R. Hoare (1960) zurück. Es ist ein Vorgehen, das vom Typ Teile und
Herrsche (divide et impera, divide and conquer) ist und auf einem Zerlegen des Feldes in zwei
Teile und anschließendem Sortieren der Teile unabhängig voneinander beruht. Auf die Teile
kann nun diese Idee wieder angewendet werden: Das Verfahren ist rekursiv, d.h. es ruft sich
selbst (auf kleinerer Stufe) wieder auf. Wir kommmen im nächsten Kapitel auf das Prinzip
Rekursivität“ zurück.
”
Eine entscheidende Bedeutung kommt der Zerlegung eines Feldes zu. Es soll (zweckmäßigerweise) so erfolgen, dass gilt: Wird das Feld mit Hilfe des Elements ar zerlegt, so soll dies bedeuten:
(1) ar befindet sich an seinem endgültigen Platz;
(2) für alle j < r gilt aj ≤ ar ;
(3) für alle j > r gilt aj ≥ ar .
Wir nach diesen Vorgaben zerlegt, dann ist das ganze Feld sortiert, wenn die beiden Teile sortiert
sind. ar nennt man das Pivot-Element.
Bei jedem rekursiven Schritt wird eine solche Zerlegung benötigt. Wie findet man eine solche
Zerlegung? Hier ist die Realisierung:
• Wähle irgendein ar .
• Durchsuche das Feld von links, bis ein Element gefunden ist, das nicht kleiner als ar ist,
und durchsuche das Feld von rechts, bis ein Element gefunden ist, das nicht größer als ar
ist. Tausche die so gefundenen Elemente.
• Wiederhole den obigen Suchprozess solange, bis sich die Suche von links und rechts bei
einem Element trifft. Nun ist das Element ar mit dem Element zu tauschen, bei dem sich
die Suche von links und rechts getroffen hat.
Ist das Feld nun zerlegt (Start), das Startfeld ist also nun a1 , . . . , ar , . . . , an , wird das Sortierverfahren auf die Teile a1 , . . . , ar−1 und ar+1 , . . . , an angewendet; als trennende Elemente
können nun etwa die Elemente ar−1 und an verwendet werden. Anwendung von Quick–sort auf
unser Beispiel EXAMPLE ergibt die Sequenz (d) (M ist beim Start das trennende Element).
Das Beste, was bei Quick–sort passieren könnte, ist, dass durch jede Zerlegung das Feld
genau halbiert wird. Dann würde die Anzahl Cn der von Quick–sort benötigten Vergleiche der
rekurrenten Beziehung vom Typ Teile und Herrsche“ genügen (n gerade!): Cn = 2C n2 + n .
”
Dabei ist 2C n2 der Aufwand für das Sortieren der zwei halbierten Felder und n der Aufwand
für die Zerlegung. Man kann zeigen, dass notwendigerweise Cn = n log2 n gilt; zum Logarithmus
siehe unten. Es gilt: 2n ln n ∼ 1.38n log 2 n . Die Verifikation, dass diese Darstellung von Cn der
Rekursion (??) genügt ist mit der Funktionalgleichung des Logarithmus einfach:
n
2C n2 + n = n log2 ( ) + n = n(log2 n − 1) + n = n log2 n = Cn .
2
Für den allgemeinen Fall zeigt eine etwas aufwendigere Analyse Cn = 2n ln n .
96
Machen wir einen kleinen Einschub zur Logarithmusfunktion.
Eine allgemeine Herangehensweise, Aufwandsrekursionen der obigen Art, zu analysieren, wird
unter der Überschrift Master-Theorem der Komplexität“ abgehandelt; siehe Abschnitt 6.6.
”
Eine wichtige Begriffsbildung ist die Laufzeitkomplexität im Mittel eines Verfahrens.
Damit ist hier gemeint, wieviele Rechenschritte ein Sortierverfahren benötigt, wenn es auf ein
zufällig“ vorsortiertes Feld angewendet wird.
”
6.5
Landausymbole und Komplexität
Landau-Symbole5 werden in der Mathematik und in der Informatik verwendet, um das asymptotische Verhalten von Funktionen und Folgen zu beschreiben. In der Informatik werden sie bei der
Analyse von Algorithmen verwendet und geben ein Maß für die Anzahl der Elementarschritte in
Abhängigkeit von der Größe der Eingangsvariablen an. Die Komplexitätstheorie verwendet sie,
um verschiedene Probleme danach zu vergleichen, wie schwierig“ oder aufwändig sie zu lösen
”
sind.
Die folgende Definition ist ein Spezialfall der allgemeinen Landau-Symbole.
Definition 6.5.1 Seien f, g : N −→ R Funktionen.
(a) Wir schreiben f (n) = O(g(n)), wenn es N ∈ N und eine Konstante c ≥ 0 gibt mit
|f (n)| ≤ c|g(n)| falls n ∈ N, n ≥ N .
(b)
Wir schreiben f (n) = o(g(n)), wenn es für alle c > 0 ein N ∈ N gibt mit
|f (x)| ≤ c|g(x)| falls n ∈ N, n ≥ N .
Im ersten Fall sagen wir f (n) gleich Groß-O von g(n)“, im zweiten Fall f (n) gleich Klein-o
”
”
von g(n)“.
Die Tatsache, dass in der Definition von Groß-O nur das Verhalten für große natürliche Zahlen
eine Rolle spielt, drückt man oft so aus: f ist groß-O von g für n −→ ∞ .
Beispiel 6.5.2
n−2 = O(2n−2 ) , n−2 = o(2n−1 ) .
e−n = o(n−25 ) .
n! = O(nn ) .
n3 = O(n3 ) .
Pn
2
i=1 i = O(n ) .
√
n! = 2 π n (ne−1 )n (1 + O(n−1 )) . (Stirlingsche Formel)
Beispiel 6.5.3 Die Fibonacci-Zahlen sind induktiv definiert durch
f0 := f1 := 1 , fn+1 := fn + fn−1 , n ∈ N .
Dann gilt fn = O(2n ), denn es gilt fn ≤ 2fn−1 , n ∈ N .
5
Diese Symbole wurden vom Mathematiker E.G. Landau (1877-1938) eingeführt.
97
Wenn wir sagen, dass ein Algorithmus einen Aufwand von O(g) hat, dann meinen wir damit Folgendes: Wenn der Algorithmus auf unterschiedlichen Computern mit den gleichen Datensätzen läuft, und diese die Größe n haben, dann werden die resultierenden Laufzeiten immer
nicht größer sein als eine Konstante mal g(n) .
Kommen wir zu einigen konkreten Beispielen.
Türme von Hanoi
Diese Aufgabe ist ja durchaus mit den Algorithmen für Aufgaben auf einem Rechner zu vergleichen, wenn wir etwa die Lösung einem Roboter überlassen wollen. Den Aufwand haben wir
schon untersucht. Alerdings werden wir feststellen, dass die Rekursionsgleichung
Z(n) = 2Z(n − 1) + 1
nicht unter das so genannte Master-Theorem, das wir noch anführen werden, passt.
Matrixmultiplikation
Matrix-Multiplikation ist eine Standardaufgabe beim wissenschaftlichen Rechnen. Betrachte
zwei Matrizen in Rn,n , die wir miteinander multiplizieren wollen. Klar, der Mindestaufwand
(für Additionen und Multiplikationen), der getrieben werden muss, ist von der Ordnung O(n2 ),
da alle n2 Einträge im Produkt berechnet werden müssen. Die naive“ Umsetzung der Matrix”
multiplikation ist im Aufwand von der Ordnung O(n3 ) = O(nlog2 8 ), da bei der Berechnung eines
Eintrags im Produkt n Multiplikationen und n − 1 Additionen anfallen.
Auf V. Strassen6 geht ein Berechnungsverfahren zurück, das nach der Methode divide and
”
conquer“ arbeitet. Es ist inspiriert durch die Beobachtung von C. F. Gauss, dass die Multiplikation zweier komplexer Zahlen durch drei Multiplikationen und vier Additionen realisiert werden
kann.
Seien A, B ∈ Rn,n zwei Matrizen und sei n = 2k . (Sind die Matrizen nicht von der Größe
n = 2k, so fülle sie mit Nullzeilen und Nullspalten so auf, dass die Größe einer Zweierpotenz
erreicht wird.) Wir wollen C mit C = AB berechnen. Dazu zerlegen wir die Matrizen A, B und
C in gleich große Block-Matrizen
C11 C12
B11 B12
A11 A12
,C=
,B=
A=
C21 C22
B21 B22
A21 A22
mit
Aij , Bij , Cij ∈ Rn/2,n/2 , i, j = 1, 2 .
Dann haben wir
C11 = A11 B11 +A12 B21 , C12 = A11 B12 +A12 B22 , C21 = A21 B11 +A22 B21 , C22 = A21 B12 +A22 B22 .
Damit haben wir den Aufwand noch nicht reduziert, denn wir benötigen 8 Multiplikationen,
um die Cij -Matrizen zu berechnen, genauso, wie bei der Standard-Matrixmultiplikation. Wir
können aber eine bedeutende Beobachtung machen. Wir definieren neue Matrizen
M1
M2
M3
M4
6
:=
:=
:=
:=
(A11 + A22 )(B11 + B22 ) M5 := (A11 + A12 )B22
(A21 + A22 )B11
M6 := (A21 − A11 )(B11 + B12 )
A11 (B12 − B22 )
M7 := (A12 − A22 )(B21 + B22 )
A22 (B21 − B11 )
Volker Strassen, 1969
98
welche wir nutzen können, die Cij -Matrizen damit auszudrücken:
C11 = M1 + M4 − M5 + M7 C12 = M3 + M5
C22 = M1 − M2 + M3 + M6 C21 = M2 + M4
Damit brauchen wir nur 7 Multiplikationen, wenngleich die Anzahl der Additionen erhöht wurde. Wir iterieren diesen Zerlegungsprozess bis die Untermatrizen zu Skalaren (reellen Zahlen)
degenerieren; wir benötigen n-Zerlegungsschritte. Sei T (m) die Anzahl der Multiplikationen bei
der Lösung eines Problems der Größe m. Dann haben wir
T (m) = 7 T (m/2) .
Da T (1) = 1 gilt, ist einfach zu verifizieren, dass T (2k ) = 7k und daher T (n) = nlog2 7 folgt. Also
haben wir das Ergebnis, dass wir bei der Realisierung der iterativen Zerlegung zur Multiplikation
von A, B ∈ Rn,n den Aufwand
O(nlog2 7 ) ≈ O(n2.71 )
treiben müssen.
In der Zwischenzeit wurde dieses Ergebnis durch weitere Überlegungen schon auf O(n2.41 )
verbessert.
Berechnung des größten gemeinsamen Teilers
Der euklidische Algorithmus ist ein Beispiel für einen sehr effizienten Algorithmus zur Berechnung des größten gemeinsamen Teilers zweier zahlen a, b ∈ Z, verglichen etwa mit dere Berechnung auf Grund einer Primfaktorzerlegung der beteiligten Zahlen. Zur Analyse dieses Algorithmus können wir folgendermaßen vorgehen. Wir nehmen dabei a ≥ b > 0 an und beziehen uns
auf die Darstellung der Reste im Ablauf des Algorithmus:
r0 := a, r1 := b, ri−1 = qi+1 ri + ri+1 , i = 2, . . . , k, rk = qk+1 rk+1
mit rk+1 = ggT(a, b) . Wegen qi+1 ≥ 1 folgt ri−1 ≥ ri + ri+1 > 2ri+1 , also ri+1 < 21 ri−1 . Es gilt
also r2 < 21 a, r4 < 12 r2 , und in der Konsequenz
r2l < 2−l a, l = l = 1, . . . .
Also muss spätestens, wenn 2−l a ≤ 1 ist, r2l = 0 gelten. Dies bedeutet log2 a ≤ l . Wenn r2l0
der letzte Rest 6= 0 mit geradem Index ist, so muss also l0 < log2 a gelten. Für die Anzahl d der
Divisionen gilt somit (wenn wir die letzte Division nicht zählen)
d ≤ 2l0 ≤ ⌊2 log 2 a⌋ .
Diese Schranke wird im Allgemeinen nicht erreicht.
Die Größe der Eingabedaten können wir durch die Anzahl der Binärstellen von a messen.
Wenn wir die maximale Anzahl der Divisionen mit d(n) bezeichnen, sehen wir
d(n) ≤ 2n d.h. d(n) = O(n) .
(6.2)
Man kann nun so argumentieren, dass der euklidische Algorithmus zur Berechnung des
größten gemeinsamen Teilers eines Bruches zweier aufeinanderfolgender Fibonacci–Zahlen besonders langsam ist; es ist der worst case für den euklidischen Algorithmus gegeben.
99
Quick-Sort
Die Laufzeit des Algorithmus hängt im wesentlichen von der Wahl des Pivotelementes ab.
Im worst case wird das Pivotelement stets so gewählt, dass es das größte oder das kleinste
Element der Liste ist. Dies ist etwa der Fall, wenn als Pivotelement stets das Element am Ende
der Liste gewählt wird und die zu sortierende Liste bereits sortiert vorliegt. Die zu untersuchende
Liste wird dann in jedem Rekursionsschritt nur um eins kleiner und die Zeitkomplexität wird
beschrieben durch O(n2 ) .
Im best case wird das Pivotelement stets so gewählt, dass die beiden entstehenden Teillisten
etwa gleich groß sind. Wir haben dies in der Analyse in Abschnitt unterstellt und dann die
asymptotische Laufzeit des Algorithmus mit O(n · log2 (n)) angegeben. Diese Zeitkomplexität
gilt
ebenso für den average case, bei dem die Längen der Teillisten jeweils durchschnittlich
n−1
1 Pn−1
i=0 i = 2 betragen.
n
Für die Wahl des Pivotelementes sind in der Literatur verschiedene Ansätze beschrieben
worden. Die Wahrscheinlichkeit des Eintreffens des worst case ist bei diesen unterschiedlich
groß.
6.6
Das Master-Theorem
Das Master-Theorem stellt ein Hilfsmittel dar, aus rekursiv gegebene Aufwandsgleichungen den
Aufwand für ein Problem explizit anzugeben. In konkreten Fällen haben wir dies ohne theoretischen Überbau schon getan.
Als allgemeine Form einer rekursiven Aufwandsanalyse betrachten wir:
n
T (n) = a T ( ) + f (n)
b
(6.3)
Hierbei steht T (n) für die zu untersuchende Laufzeitfunktion, während a und b Konstanten sind.
Ferner bezeichnet f (n) eine von T (n) unabhängige und nicht negative Funktion.
Interpretation der Rekurrenz für T (n):
a = Anzahl der Unterprobleme in der Rekursion
1/b = Teil des Originalproblems, welches wiederum durch alle Unterprobleme repräsentiert wird
f (n) = Kosten (Aufwand), die durch die Zerlegung des Problems und der Kombination der
Teillösungen entstehen
Wir erweitern die Groß-O– Betrachtungsweise.
Definition 6.6.1 Sei g : N −→ [0, ∞) .
(a) O(g) := {f : N −→ R|es gibt c ≥ 0, N ∈ N mit 0 ≤ f (n) ≤ cg(n) für alle n ≥ N } .
(b)
Ω(g) := {f : N −→ R|es gibt c ≥ 0, N ∈ N mit f (n) ≥ cg(n) für alle n ≥ N } .
(c)
Θ(g) := O(g) ∩ Ω(g) .
Satz 6.6.2 (Master-Theorem) Betrachte die Rekursionsgleichung (6.3), wobei die Funktion
T : N −→ R monoton nicht fallend sei, d.h. T (n) ≤ T (n + 1) für alle n ∈ N . Es gelte:
(a) a ≥ 1, b > 1 .
100
(b)
f : N −→ [0, ∞) .
Dann gilt bei einer Betrachtung der Rekursionsgleichung (6.3) und der zugehörigen Laufzeitfunktion T für n ∈ N, welche eine Potenz von b sind:
(1) Ist f (n) = O(nlogb a−ε ) für ein ε > 0, dann ist T (n) = Θ(nlogb a ) .
(2) Ist f (n) = Θ(nlogb a ), dann ist T (n) = Θ(nlogb a ln n) .
(3) Ist f (n) = Ω(nlogb a+ε ) für ein ε > 0 und gibt es c ∈ (0, 1) und N ∈ N mit f (n/b) ≤ ac f (n)
für alle n ≥ N, dann ist T (n) = Θ(f (n)) .
Beweis:
Wir skizzieren nur den Beweis zum 1. Fall, und zwar für f (n) := nc mit d := logb a > c .
Dies ist keine wesentlich Einschränkung auf Grund der Voraussetzung. Ohne Beschränkung der
Allgemeinheit können wir auch annehmen: T (1) = 1 .
Wir stellen uns nun das Rekursionsschema als Weihnachtsbaum mit l Astreihen; jede Astreihe
ist mit der Anzahl ai an Kugeln bestückt, in Übereinstimmung mit der Anzahl der Aufgaben,
die auf dem Level i ∈ {0, . . . , l} zu lösen sind. Offenbar ist l = logb n die Anzahl der Levels und
auf jedem Level sind ai := ai Unteraufgaben zu lösen. Die Größe einer Aufgabe auf dem Level
i ist (n/bi ) . Also fällt auf diesem Level der Aufwand
i
a i
a
ai (n/bi )c = nc ci = nc c
b
b
an. Also ist der Gesamtaufwand unter Beachtung von
l
X
i=0
n
c
a i
bc
=n
c
l X
a i
i=0
bc
=n
c1
a
bc
<1
l+1
− bac
= Θ(nc ) .
1 − bac
Bemerkung 6.6.3 In jedem Fall von Satz 6.6.2 haben wir polynomiales Verhalten in f . Die
Voraussetzungen der drei Fälle lassen sich im wesentlichen mit f (n) = nc einteilen in die drei
Fälle
a
a
a
<
1
,
=
1
,
> 1.
bc
bc
bc
Wie uns der Beweis zu Satz 6.6.2 lehrt, korrespondiert dies zur Tasache, dass der Aufwand auf
den unterschiedlichen Levels fällt, gleich bleibt oder ansteigt.
Beachte, dass die Fallunterscheidungen im Satz 6.6.2 nicht vollständig sind: es gibt Beispiele,
die nicht erfasst werden; siehe (4) im folgender Beispielaufzählung.
Beispiel 6.6.4
(1) Betrachte
T (n) = 8T (n/2) + 1000n2
Hier ist der 1. Fall des Master-Theorems zuständig mit a = 8, b = 2, f (n) = 1000n2 und
log2 8 = 3 . Klar, f (n) = O(n3−ε ) für jedes ε ∈ [0, 1) . Also erhalten wir
T (n) = O(n3 ) .
101
(2) Betrachte
T (n) = 4T (n/4) + 1000n
Hier ist der 2. Fall des Master-Theorems zuständig mit a = 4, b = 4, f (n) = 1000n und
log4 4 = 1 . Klar, f (n) = Θ(n1 ) . Also erhalten wir
T (n) = Θ(n1 ) ln n .
(3) Betrachte
T (n) = 2T (n/2) + n2
Hier ist der 3. Fall des Master-Theorems zuständig mit a = 2, b = 2, f (n) = n2 und log2 2 =
1 . Klar, f (n) = Ω(n1+ε ) für ε = 1 . Ferner ist f (n/b) ≤ ac f (n) für alle n ∈ N mit c = 12 .
Also erhalten wir
T (n) = O(n2 ) .
(4) Betrachte
T (n) = 8T (n/2) + n3 ln n
Hier könnte der 3. Fall des Master-Theorems zuständig sein mit a = 8, b = 2, f (n) = n3 ln n
und log2 8 = 3 . Aber man kann zeigen, dass die Bedingung, die im 3. Fall an f (n) gestellt
ist, nicht erfüllt ist. Es ist eher eine Verallgemeinerung des 2. Falls. Man kann beweisen:
T (n) = Θ(n3 ln2 n)
Es gibt Varianten des Master-Theorems, die die zwischen den drei Fällen des Satzes 6.6.2
liegenden Aufgaben behandeln, z.B. die Aufgabe (4) im obigen Beispiel.
Bisher haben wir implizit Rekursionsgleichungen behandelt für Laufzeitwerten T (n) für natürliche
Zahlen, die Potenzen von b sind. Realistischere Formen der Rekursionsgleichung sind7
oder
n
T (n) = a T (⌈ ⌉) + f (n)
b
(6.4)
n
T (n) = a T (⌊ ⌋) + f (n)
b
(6.5)
n
n
T (n) = a T (⌈ ⌉) + (a − a′ ) T (⌊ ⌋) + f (n)
b
b
(6.6)
oder sogar (mit a′ ≥ 1)
Ohne Beweis halten wir fest, dass die Aussagen des Master-Theorems hier angepasst werden
können ohne wesentlichen Verlust an Schärfe. Als Ergebnis haben wir damit, dass bei der Rekursionsgleichung (6.3) nicht rücksicht genommen werden muss auf Potenzen von b, da wir ja
die Version (6.4) oder (6.5) betrachten können.
7
Zur Erinnerung: ⌈z⌉ := min{u ∈ Z|u ≥ z} , ⌊z⌋ := max{u ∈ Z|u ≤ z} .
102
6.7
Anhang: g-adische Entwicklung und Computerzahlen
Die Dezimalbruchschreibweise für reelle Zahlen ist wohlbekannt; wir haben sie mitunter auch
schon verwendet. Etwa:
3, 20123 · · · = 3 +
0
1
2
3
2
+
+
+
+
+ ···
10 100 1000 10000 100000
Allgemein nennt man, wenn z eine natürliche Zahl und (an )n∈N eine Folge von Ziffern an ∈
{0, 1, . . . , 9} ist, die Darstellung
z.a1 a2 a3 · · · := z ·
∞
X
aj
10j
j=1
einen unendlichen Dezimalbruch oder eine Dezimalbruchentwicklung der dargestellten
Zahl. Dabei ist schon berücksichtigt, dass in der Tat eine Zahl dargestellt wird, da die angeführte
Reihe wegen an · 10−n ≤ 9 · 10−n die geometrische Reihe als Majorante besitzt.
Es kann Mehrdeutigkeiten bei der Darstellung geben, etwa 1 = 1, 000 · · · = 0, 999 . . . . Die
Mehrdeutigkeit lässt sich dadurch beseitigen, dass man entweder “an = 9 bis auf endlich viele
n“ oder “an = 0 bis auf endlich viele n“ verbietet. Stellt man in einer Dezimalbruchentwicklung
m
P
z.a1 a2 . . . auch noch z durch Potenzen von 10 dar, d.h. z =
bj 10j , so resultiert schließlich
j=0
ein Dezimalbruch bm . . . b0 .a1 a2 . . . . Bricht man eine Dezimalbruchentwicklung ab, erhält man
eine Näherung für die reelle Zahl.
Nun kann man das Spiel statt mit g = 10 allgemein mit g ∈ N\{1} betreiben und man kommt
zur g–adischen Entwicklung xg einer reellen Zahl. Wir halten fest:
Satz 6.7.1 Sei g ∈ N , g ≥ 2 . Jede Zahl x ∈ R hat eine g–adische Entwicklung der Form
x = xg = bm . . . b0 .a1 a2 . . . mit bi , ai ∈ {0, . . . , g − 1} .
Speziell für g = 2 und natürliche Zahlen x ergibt dies die sogenannte Dualdarstellung von
natürlichen Zahlen; etwa
18 = 24 + 21 , d.h. (18)2 = 10010 .
Eine Zahl x kann eine endliche Anzahl von Ziffern haben bezüglich einer Basis und eine
unendliche Anzahl von Ziffern in einer anderen Basis. Zum Beispiel gilt für x = 1/3:
(x)10 = +0.3 , (x)3 = +0.1 .
Für x := 103 3/4 haben wir
(x)10 = +[103.75] , (x)2 = +[1100111.11] , (x)16 = +[67.C] .
Auf Rechenanlagen steht nur endlicher Speicherplatz für die Darstellung von Zahlen zur
Verfügung, also auch nur eine endliche Anzahl von Zahlen und für jede Zahl hat man sich
mit einem rationalen Näherungswert zu begnügen. Bei der Festkomma–Darstellung wird
jede Zahl durch das Vorzeichen, s′ Ziffern vor dem Komma und t′ Ziffern nach dem Komma
ersetzt. Anstelle der reellen Zahlengeraden wird ein äquidistant geteiltes endliches Punktraster
verwendet. Für wissenschaftlich–technische Rechnungen ist die Festkomma–Darstellung nicht
sehr geeignet, da beispielsweise physikalische Konstanten über viele 10–er Potenzen streuen,
z.B.:
103
m0 = 9.1110 10 −28 g
L = 6.02 10 23 Mol −1
h = 6.62 10 −34 Watt sec2
c = 3.00 10 10 cm sec−1
:
:
:
:
Ruhemasse des Elektrons
Loschmidt–Zahl
Planksches Wirkungsquantum
Lichtgeschwindigkeit
Passender für diese Zwecke ist die Gleitkomma–Darstellung. Das System der Gleitkommazahlen wird charakterisiert durch 4 Parameter (Maschinenkonstanten):
• Basis g
• Mantissenstellenzahl t
• Exponentengrenzen b, B ∈ Z(b < 0 < B) ;
Exponentenbereich [b, B] := {z ∈ Z|b ≤ z ≤ B} .
Jede Gleitkommazahl hat nach Wahl dieser Parameter die Form
x = sign(x) 0.a1 . . . at g e mit 0 ≤ ai < g, a1 6= 0, e ∈ [b, B] ,
mit dem Vorzeichen sign(x), der Mantisse 0.a1 . . . at und dem Exponenten e . (Die Bedingung a1 6= 0 erzwingt eine Normierung). Sie kann so abgespeichert werden:
x=
b
±
a1
···
at
e
(Beachte, dass die Speicherung von a1 = 1 bei g = 2 gar nicht nötig ist.)
Beispiel 6.7.2 Sei t = 5, b = −5, B = 5, x = 27.5
1. g = 10 : x = +0.27500 10 2
2. g = 2 : x = +0.110111 2 5
Nicht darstellbar!
3. g = 16 : x = +0.1B800 16 2
Im Hexadezimalsystem (g = 16) schreibt man für die Ziffern 10, 11, 12, 13, 14, 15 die Buchstaben A, B, C, D, E, F .
Wir stellen fest, dass die Null unter den normalisierten Gleitkommazahlen nicht vorkommt.
Wir fügen sie hinzu – die Darstellung ist rechnerspezifisch – und nennen diese Zahlen dann
Maschinenzahlen:
x−1 , . . . x−t ∈ {0, 1, . . . , g − 1}, x−1 6= 0,
e
F := F(g, t, emin , emax ) := σ 0.x−1 . . . x−t g |
∪{0} .
e ∈ Z, emin ≤ e ≤ emax , σ ∈ {+, −}
Es ist einfach, diese Zahlen zu zählen:
#F(g, t, emin , emax ) = 2(g − 1)gt−1 (emax − emin + 1)
In F(g, t, emin , emax ) sind größte und kleinste Zahl gegeben durch
xmin := +[10 . . . 0] gemin −t = gemin −1 , xmax := +[δ . . . δ] gemax −t = gemax (1−g−t ) wobei δ = g−1 .
Offenbar gilt für alle Computerzahlen x 6= 0 g−emin −1 ≤ |x| ≤ gemax . Gilt |x| < g−emin −1 , so
wird im Allgemeinen x durch Null ersetzt. Zahlen x mit |x| > gemax können nicht verarbeitet
werden. Treten diese Fälle auf, so spricht man von Exponentenüberlauf. In jedem Intervall
[ge−1 , ge ], emin ≤ e ≤ emax , finden wir µ = gt − gt−1 + 1 gleichförmig verteilte Zahlen:
F ∩ [ge−1 , ge ] = {ge−1 , ge−1 + ge−t , . . . , ge−1 + µge−t } .
Beachte, dass der Zuwachs ge−t anwächst, wenn e von emin auf emax anwächst.
104
(6.7)
Beispiel 6.7.3
1. g = 2, t = 3, b = −1, B = 2. Es gibt 33 Maschinenzahlen.
2. g = 10, t = 10, B = −b = 99.
Die kleinste positive Maschinenzahl verschieden von Null ist
m = +0.100000000010 −99 ,
die größte positive Maschinenzahl ist
M = +0.999999999910 99 .
Nachbar“ von m : m′ = +0.1000000001 10 −99
”
Nachbar“ von M : M ′ = +0.9999999998 10 99
”
Also: m − m′ = 10 −109 , M − M ′ = 10 89
Das Beispiel zeigt uns, dass betragsmäßig kleine Gleitkommazahlen dichter liegen als betragsmäßig
große.
Lemma 6.7.4 Betrachte das System F(g, t, emin , emax ) . Sei x eine reelle Zahl mit xmin ≤ |x| ≤
xmax . Dann gilt:
1
|z − x|
≤ g−t+1 .
(6.8)
min
z∈F
|x|
2
Beweis:
Ohne Einschränkungen können wir annehmen: x ist positiv. Wir können annehmen, dass mit
einem e mit emin ≤ e ≤ emax gilt: x ∈ [ge−1 , ge ] . Aus (6.7) erhalten wir, dass eine Zahl z ∈
[ge−1 , ge ] mit |z − x| ≤ 21 ge−t existiert. Wegen ge−1 ≤ x ist die Behauptung bewiesen.
Die Zahl eps := 12 g−t+1 heißt Maschinengenauigkeit. Beachte, sie hängt nur von der
Mantissenlänge t ab.
Auf einem Computer sind üblicherweise zwei Formate von Fließkommazahlen verfügbar: einfache und doppelte Genauigkeit. Im IEEE-Standard mit einfacher Geneauigkeit haben wir
eps = 2−23 ≈ 10−7 .
Nun hat man ein Verfahren anzugeben, das eine Zahl, die keine Maschinenzahl ist, durch eine
Maschinenzahl approximiert. Offenbar ist Rundung ein geeignetes Verfahren, ein solches x̃ zu
konstruieren.
Wir wissen, dass sich jedes x ∈ R g–adisch so darstellen läßt:
x = sign(x)0.a1 . . . at at+1 . . . g e
mit 0 ≤ ai < g, e ∈ Z und a1 6= 0 . Dazu erklären wir die Rundung rd(x) durch
g
sign(x)0.a1 . . . at g e
, falls at+1 < 2
rd(x) :=
sign(x)(0.a1 . . . at + g−t ) g e , sonst
Beispiel 6.7.5 Sei g = 10, t = 4, B = −b = 99 . Wir haben:
rd(0.31796 10 45) = 0.3180 10 45 , rd(0.0012345 10 −99) = 0.1235 10 −100 .
105
Die obigen Beispiele zeigen, dass Rundung nicht immer zu einer Maschinenzahl führt (Exponentenüberlauf, Exponentenunterlauf). Rechenanlagen geben bei Exponentenüberlauf eine Fehlermeldung und beenden die Rechnung. Bei Exponentenunterlauf ist die Vorgehensweise nicht
einheitlich, meist wird die Rechnung mit Null fortgesetzt.
Folgerung 6.7.6 Für jedes x ∈ R gilt:
(a) |x − rd(x)| ≤ 12 g1−t |x| .
(b) rd(x) = x(1 + ǫ) mit |ǫ| ≤ 21 g1−t .
Beweis:
Sei x = sign(x)0.a1 . . . at at+1 . . . g e.
Zu (a) : Es gilt |x − rd(x)| ≤ 12 ge−t nach Konstruktion von rd(x). Also
1
1
1
|x − rd(x)||x|−1 ≤ ge−t
≤ g1−t .
e
2
0.a1 · g
2
Zu (b) : Setze ǫ :=
|x − rd(x)|
für x 6= 0, für x = 0 ist nichts zu beweisen.
|x|
Beachte: Wir finden in Folgerung 6.7.6 wieder die Maschinengenauigkeit eps := 12 g1−t .
Wegen Rundung werden die arithmetischen Operationen +, −, ·, / nicht exakt ausgeführt.
Dies hat zur Folge, dass die Ersatzoperationen ⊕, ⊖, ⊙, ⊘ definiert durch
x ⊕ y := rd(x + y) , x ⊖ y := rd(x − y), x ⊙ y := rd(x · y) , x ⊘ y := rd(x/y)
nicht den üblichen Rechenregeln genügen. Wir sehen dabei davon ab, dass Exponentenüberlauf
und Exponentenunterlauf auftreten kann.
Beispiel 6.7.7 Sei g = 10, t = 4 .
0.1000 10 1 ⊕ 0.400 10 −4 = rd(1.0004) = 1 = 0.1000 10 1
Aus x ⊕ y = x folgt also nicht notwendigerweise y = 0.
Sei g = 10, t = 8 . Es seien
a = 0.233712.58 10 −4 , b = 0.33678429 10 2 , c = −0.33677811 10 2 .
Es gilt:
A : = a ⊕ (b ⊕ c)
= a ⊕ (0, 61800000 10 −3
B : = (a ⊕ b) ⊕ c
= (0.00000023 10 2 + 0.33678429 10 2) ⊕ c
= rd(0.02337126 10 3 + 0.6180000 10 −3)
= rd(0.33678452 10 2 − 0.33677811 10 2)
= rd(0.64137126 10 −3)
= 0.64100000 10 −3
= 0, 64137126 10 −3
Exaktes Resultat: C := a + b + c = 0.641371258 10 −3
Also A = rd(C) , B 6= rd(C) und der Genauigkeitsverlust bei C beträgt 5 Dezimalen! Der
Genauigkeitsverlust lässt sich gut verstehen: Es werden zwei Zahlen subtrahiert, die annähernd
gleich sind.
106
Eine positive Aussage zur Wirkung der Ersatzoperationen ist
Folgerung 6.7.8 Sei ⊗ eine der Operationen {⊕, ⊖, ⊙, ⊘} und × ihre entsprechende Operation
in {+, −, ·, /}. Dann gilt für alle x, y ∈ R :
x ⊗ y = (x × y)(1 + ǫ) mit |ǫ| ≤ eps.
Beweis:
Folgerung 6.7.6 mit der Definition von ⊗.
Probleme der Computer-Arithmetik sind:
Überlauf Siehe oben.
Unterlauf Siehe oben.
Auslöschung Addition etwa gleich großer Zahlen mit entgegengesetztem Vorzeichen führt zu
einer starken Verringerung der Tahl der gültigen Ziffern.
Rechenregeln Selbst in Fällen, wo weder Überlauf noch Unterlauf eintritt, gelten die Rechenregeln der reellen Zahlen nicht mehr.
Beispiel 6.7.9 Wenn man zwei Gleitkommazahlen addiert, die ähnlichen Absolutwert haben,
aber entgegengesetzes Vorzeichen, kommt es zum Auslöschen signifikanter Stellen und das Ergebnis wird ungenau. Etwa ergibt die Auswertung von ((1 + x) − 1)/x für x = 1e − 15 den Wert
1.1102, und dies ist ziemlich ungenau (11% Fehler).
6.8
1.)
Übungen
S
Sei A ein (endliches) Alphabet, sei A∗ := {()} ∪ n∈N An die Menge der Wörter (beliebiger Länge) über dem Alphabet A .
Für zwei Worte u = (u1 , . . . , uk ) ∈ Ak , v = (v1 , . . . , vl ) ∈ Al setzen wir:
uv := (u1 , . . . , uk , v1 , . . . , vl ) ∈ Ak+l .
Wir definieren für u, v ∈ A∗ :
u ≤ v : ⇐⇒ Es gibt z ∈ A∗ mit uz = v .
(a) Zeige: ≤ ist eine Halbordnung in A∗ .
(b) Ist ≤ stets eine Ordnung in A∗ ?
(c) Gibt es in A∗ ein Wort w, so dass gilt:
w ≤ u für alle u ∈ A∗ .
2.)
Ist die Halbordnung O eine Ordnung auf der Menge A, dann ist auch die zugehörige
lexikographische Halbordnung auf A × A eine Ordnung.
3.)
Sei O eine Halbordnung auf der Menge X und sei M eine teilmenge von X . Unter einer
O
oberen Schranke von M versteht man ein x ∈ X mit y ≤ x für alle y ∈ M . Wenn die
Menge aller oberen Schranken von M ein kleinstes Element (bezüglich der Halbordnung
O) besitzt, dann heißt dieses kleinste Element kleinste obere Schranke.
Sei M := {2, 3, . . . , 10} ⊂ N versehen mit der Teilbarkeitsrelation.
107
(a)
Man gebe eine mindestens zweielementige Teilmenge von M an, welche eine kleinste
obere Schranke besitzt.
(b) Man gebe eine mindestens zweielementige Teilmenge von M an, welche keine obere
Schranke besitzt.
(c)
Was ist die kleinste obere Schranke von {a, b} ⊂ M ?
4.)
Sei X eine Menge. Was ist die kleinste obere Schranke (siehe obige Aufgabe) einer Teilmenge von POT(X) bezüglich der Inklusion?
5.)
Zeige: Für alle x, y, z > 1 gilt xlogy z = z logy x .
6.)
Zeige: Für alle x, y > 1 gilt logx n = Θ(log2 n) .
7.)
Betrachte mit a ≥ 1, b > 1, f : N −→ N die Rekursionsgleichung
n
T (1) := 1 , T (n) = a T (⌈ ⌉) + f (n), n ≥ 2 .
b
Zeige, dass diese Vorgabe die Laufzeitfunktion T : N −→ N eindeutig bestimmt.
8.)
Betrachte die Rekursionsgleichung
√
n
T (1) := 1 , T (n) = 3 T ( ) + n n + 1, n ≥ 2 .
2
Zeige: T (n) = Θ(nlog2 3 ) .
9.)
Betrachte die Rekursionsgleichung
n
T (1) := 1 , T (n) = T ( ) + 1, n ≥ 2 .
4
Welches asymptotische Verhalten zeigt T (n)?
10.) Betrachte die Rekursionsgleichung
n
T (1) := 1 , T (n) = 7 T ( ) + n2 , n ≥ 2 .
3
Welches asymptotische Verhalten zeigt T (n)?
108
Herunterladen