Grundlagen der Informatik - Benutzer-Homepage

Werbung
Grundlagen der Informatik
SS 2003
Mitschrift von Malte Ried
29. November 2003
2
Vorwort
Über das Script
Es handelt sich hierbei um eine Mitschrift aus dem Fach “Grundlagen der Informatik” des
Sommer-Semesters 2003 bei Professor Geisse. Es enthält (fast) nur die Tafelanschriften von
Professor Geisse. Ich gebe natürlich keine Gewähr auf den Inhalt.
Sollte jemand Fehler finden (ganz egal ob Rechtschreib-, Grammatik- oder Formatierungsfehler, aber auch inhaltliche Fehler) möge er mir bitte eine Beschreibung der Fehler zusenden.
Hier will ich mich auch noch bei folgenden Personen bedanken, die bei der Entstehung dieses
Scriptes mitgholfen haben:
Klaus Schulwitz
Mario Klapper
erste Korrekturlesung
zweite Korrekturlesung
Fehlerberichte stammen von folgenden Personen:
Michael Paul
Matthias Peter
Jörg Schwalb
Vielen Dank!
“Es gibt keinen Löffel.”
Neo
3
4
Inhaltsverzeichnis
Vorwort
3
Organisatorisches
9
Klausur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
9
Klausurzulassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
9
Bücher . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
9
1 Grundbegriffe und Arbeitsgebiete der Informatik
11
1.1
Was ist Informatik? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
11
1.2
Was ist Information? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
12
1.3
Was ist Verarbeitung von Information? . . . . . . . . . . . . . . . . . . . . . . .
14
1.4
Was sind Digitalrechner . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
15
1.5
Arbeitsgebiete der Informatik . . . . . . . . . . . . . . . . . . . . . . . . . . . .
16
2 Zahlendarstellungen und Codes
19
2.1
Stellenwertsysteme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
19
2.2
Dualzahlen, Oktalzahlen, Hexadezimalzahlen . . . . . . . . . . . . . . . . . . .
19
2.3
ASCII und Unicode
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
20
2.4
Darstellung von ganzen Zahlen . . . . . . . . . . . . . . . . . . . . . . . . . . .
21
2.5
Festkommadarstellung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
22
2.6
Fließkommadarstellung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
22
2.7
Codes zur Datenkomprimierung . . . . . . . . . . . . . . . . . . . . . . . . . . .
23
2.8
Codes zur Fehlererkennung und -korrektur . . . . . . . . . . . . . . . . . . . . .
24
3 Algorithmen und Datenstrukturen
27
3.1
Algorithmen und Programme . . . . . . . . . . . . . . . . . . . . . . . . . . . .
27
3.2
Kontrollelemente von Algorithmen . . . . . . . . . . . . . . . . . . . . . . . . .
29
3.2.1
Folge . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
29
3.2.2
Auswahl . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
30
5
6
INHALTSVERZEICHNIS
3.2.3
3.3
3.4
3.5
3.6
3.7
Wiederholung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
31
Strukturierung von Algorithmen durch Blöcke . . . . . . . . . . . . . . . . . . .
34
3.3.1
Vorzeitiges Verlassen von Blöcken . . . . . . . . . . . . . . . . . . . . . .
37
Rekursion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
37
3.4.1
Verwaltung der Rekursion durch das Programmiersystem . . . . . . . .
38
3.4.2
Wo man Rekursion besser vermeidet . . . . . . . . . . . . . . . . . . . .
38
3.4.3
Wo man Rekursion mit Vorteil anwendet . . . . . . . . . . . . . . . . .
40
Wie ‘gut’ ist ein Algorithmus? . . . . . . . . . . . . . . . . . . . . . . . . . . . .
42
3.5.1
Die O-Notation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
43
3.5.2
Häufig vorkommende O(. . .)-Ausdrücke . . . . . . . . . . . . . . . . . .
43
Spezifikation und Korrektheit von Algorithmen . . . . . . . . . . . . . . . . . .
46
3.6.1
Zusicherungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
46
3.6.2
Schleifeninvariante . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
47
Datentypen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
48
3.7.1
Taxonomie der Datentypen . . . . . . . . . . . . . . . . . . . . . . . . .
49
3.7.2
Einfache Datentypen . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
50
3.7.3
Strukturierte Datentypen . . . . . . . . . . . . . . . . . . . . . . . . . .
50
3.7.4
Zeiger-Datentypen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
51
3.7.5
Zur Lebensdauer von Daten . . . . . . . . . . . . . . . . . . . . . . . . .
55
4 Programmiersprachen
59
4.1
Syntax vs. Semantik . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
59
4.2
Lexikalische Struktur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
59
4.3
Syntaktische Struktur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
60
4.4
Syntax-Diagramme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
61
A Einschübe
63
A.1 Kreuzprodukt von Mengen
. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
63
A.2 ‘Kardinalität’ einer Menge . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
63
A.3 n-Tupel über einer Menge . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
63
A.4 Logarithmen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
64
A.5 Addition im Dualsystem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
64
A.6 Wie groß ist fib(n)?
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
64
A.7 Priorität, Assoziativität und Reihenfolge . . . . . . . . . . . . . . . . . . . . . .
65
B Übungen
67
B.1 1. Übung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
67
B.2 2. Übung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
68
INHALTSVERZEICHNIS
7
B.3 3. Übung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
68
B.4 4. Übung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
69
B.5 5. Übung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
70
B.6 6. Übung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
71
B.7 7. Übung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
72
C Lösungen
73
C.1 1. Übung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
73
C.2 2. Übung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
75
C.3 3. Übung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
77
C.4 4. Übung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
81
C.5 5. Übung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
85
C.6 6. Übung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
88
C.7 7.Übung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
91
8
INHALTSVERZEICHNIS
Organisatorisches
Klausur
Dauert 90 Minuten, es ist alles erlaubt (außer dem Nachbarn). Da es sich um eine Prüfungsleistung handelt, muss man sich anmelden.
Klausurzulassung
Auf die beiden geforderten Hausübungen wird verzichtet. ABER: Rechnen Sie jederzeit damit,
Ihre Lösungen zu den Übungsaufgaben an der Tafel vortragen zu müssen.
Bücher
1. Ch. Horn, I. O. Kerner, P. Forlig: Lehr- und Übungsbuch - Informatik, Band 1 Grundlagen und Überblick. Fachbuchverlag Leipzig 2001
2. P. Reichenberg: Was ist Informatik - Eine algemeinverständliche Einführung Hanser
Verlag München 1991
3. Fl. Bauer, G. Goos: Informatik - Eine einführende Übersicht Band 1+2, Springer Verlag
Berlin 1991/92
9
10
INHALTSVERZEICHNIS
Kapitel 1
Grundbegriffe und
Arbeitsgebiete der Informatik
1.1
Was ist Informatik?
Informatik ist die Wissenschaft von der Informationsverarbeitung in Natur und Technik, insbesondere der automatischen Verarbeitung mit Hilfe von Digitalrechnern.
Bemerkung:
(a) Informatik ist NICHT die Wissenschaft von Computern, so wenig wie Astronomie die
Wissenschaft von Teleskopen ist.
(b) Informatik ist eine Wissenschaft - keine Bastelecke für Software-Spieler.
Vorgehensweise einer Ingenieuerwissenschaft:



1. Problemstellung





2. Analyse


Prinzip: “Teile und Herrsche”
3. Teillösungen finden




4. Synthese





5. Lösen
(c) Wir müssen klären:
• Was ist Information?
• Was ist Verarbeitung von Information?
• Was sind Digitalrechner?
(d) Auch in der Natur gibt es Informationsverarbeitung
• Vererbung /genetischer Code
(3 Nucleinbasen kodieren eine Aminosäure)
• Schwänzeltanz der Bienen
(Richtung und Entfernung zur Futterquelle)
11
12
KAPITEL 1. GRUNDBEGRIFFE UND ARBEITSGEBIETE DER INFORMATIK
• Nervennetze in Mensch und Tier
(Sehr komplexe Informationsverarbeitung, wird zu einem großen Teil nicht verstanden)
1.2
Was ist Information?
Der Begriff der Information ist eng verknüpft mit dem der Nachricht und dem der Zeichenkette.
Gegeben sei ein ‘Alphabet’ X mit den ‘Buchstaben’ x1 , x2 , ..., xs : X = {x1 , x2 , ..., xs }.
Bemerkung: Die Buchstaben xi werden auch Zeichen oder Symbole genannt.
Eine “Zeichenkette der Länge n über X” ist eine Folge von n Zeichen aus X (ein “n-Tupel
über X” (siehe A.3, Seite 63)).
Die Menge aller n-Tupel über X ist das n-fache Kreuzprodukt X ⊗ X ⊗ ... ⊗ X , bezeichnet
|
{z
}
n−mal
als X n (siehe A.1, Seite 63).
Wird eine Zeichenkette übermittelt, spricht man von einer Nachricht. Eine Nachricht wird zur
“Information”, durch die Art und Weise, wie mit ihr beim Sender / Empfänger umgegangen
wird.
Wir hätten gern ein “Maß” für die Information: Ein Maß für die Information ist die Länge
der kürzesten Beschreibung, die eine Nachricht benötigt, welche dieselbe Bedeutung für den
Empfänger besitzt wie die ursprünglich vorgegebene Information beim Sender.
Dazu zählen wir die Menge aller Zeichenketten der Länge n über X:
|X n | = |X ⊗ X ⊗ ... ⊗ X| = |x| · |x| · ... · |x| = |x|n = sn
|
{z
} |
{z
}
n−mal
n−mal
Wir müssen also eine Nachricht aus sn Nachrichten kürzestmöglich auswählen. Wie geht das?
Beispiel:
“Zahlenrätsel”: Gegeben ist eine Zahl zwischen 1 und 128. Sie muss durch Fragen der Art “Ist
die Zahl größer als . . .” erraten werden. Die beste Strategie ist dabei die “Binäre Suche”:
> 64
nein @ ja
@
R
> 32
> 96
nein @ ja nein @ ja
@
R
@
R
..
..
..
..
.
.
.
.
Wie viele Fragen benötigt man schlimmstenfalls? Nach ld 128 = 7 ist die Suche abgeschlossen
(Logarithmen siehe A.4, Seite 64).
Man kann also durch ld sn = n·ld s Fragen eine von sn Nachrichten auswählen und betrachtet
deshalb n·lds als den Informationsgehalt einer Nachricht der Länge n über dem Alphabet X
mit s Zeichen (Shanon, ≈ 1950)
Genau genommen gilt diese Beschreibung nur dann, wenn wir nichts über die Häufigkeit
einzelner Zeichen wissen und deshalb Gleichverteilung annehmen.
1.2. WAS IST INFORMATION?
13
Beispiel: Im Zahlenrätsel werden in 90% aller Fälle Zahlen <64 zum Raten ausgewählt. Dann
ist es eine bessere Strategie (die also im Mittel mit weniger Fragen auskommt), die ‘Ratepunkte’ nach unten zu verschieben, abhängig von der Häufigkeit der zu ratenden Zahlen.
Idee: Die Zunahme der Information verringert die Unbestimmtheit beim Empfänger.
Also:
Große Wahrscheinlichkeit des Auftretens ↔ wenig Information
Kleine Wahrscheinlichkeit des Auftretens ↔ viel Information.
Beispiel:
“Morgen geht die Sonne auf” ↔ wenig neues
“Morgen gewinnen Sie im Lotto” ↔ Donnerwetter!
Deshalb: Informationsgehalt eines Zeichens xi mit Wahrscheinlichkeit der Auftretens p(xi ) ist
H(Xi ) =ld p(x1 i ) = −ld·p(xi ).
“Mittlerer Informationsgehalt” eines Zeichens (“Entropie der Nachrichtenquelle”) ist dann
Hs =
s
X
s
X
p(xi ) ·
H(xi )
=−
p(xi ) · ld(p(xi ))
| {z }
| {z }
i=1
i=1
Häufigkeit Informationsgehalt
und der mittlerer Informationsgehalt einer Zeichenkette der Länge n über dem Alphabet X
mit s Zeichen:
s
X
Hs,n = −n
p(xi ) · ldp(xi )
i=1
Bemerkung: Diese Formulierung stimmt mit unserer vorherigen überein, wenn die Zeichen
gleich häufig sind. Dann ist p(xi ) = 1s unabhängig von xi und damit
Hs,n = −n
s
X
1
i=1
s
· ld
1
1
= −n · S · = n · lds
s
s
Man misst den Informationsgehalt in ‘Bit’ (Abk. für ‘Binary Digit’). Was ist ein Bit? Alphabet
= {0, 1} gleiche Wahrscheinlichkeit p(0) = 12 , p(1) = 12
⇒ Informationsgehalt eines Zeichens ist
H
= 1 · ld2 = 1 · 1 = 1 bit!
2
,
1
|{z}
|{z}
Anzahl
Länge
der
Buchstaben Zeichenkette
im Alphabet
In der praktischen Anwendung sind Alphabete mit 256 Zeichen wichtig. Bei Gleichverteilung
ist der Informationsgehalt eines Zeichens
H256,1 = 1 · ld 256 = 8 bit
und kann also auch als eine Zeichenkette von 8 Zeichen aus dem Alphabet {0, 1} dargestellt
werden: das nennt man 1 Byte.
Vielfache davon sind KByte (1024 Bytes), MByte (1024 KBytes), GByte (1024 MBytes).
Bemerkung: Manchmal auch: 1kByte = 1000 Byte, 1MByte = 1000 kByte, etc. → Quelle für
Konfusion.
14
KAPITEL 1. GRUNDBEGRIFFE UND ARBEITSGEBIETE DER INFORMATIK
k in dezimal entspricht 1000 = 103 , 210 ≈ 103
K in binär entspricht 1024 = 210 , 232 ≈ 1010
1.3
Was ist Verarbeitung von Information?
Information wird durch Algorithmen verarbeitet.
‘Intuitive’ (= schwammige)Definition des Begriffs ‘Algorithmus’:
Eine Arbeitsvorschrift
• muss endlich in der Notation sein (endlich lange Beschreibung)
• endlich in der Abarbeitung sein (stoppt nach endlich vielen Schritten, und bietet das
Ergebnis an)
• Zuständig für eine ganze Klasse von Aufgaben (nicht nur für einen Spezialfall)
• erfasst alle Sonderfälle (kein Versagen durch Inkompetenz)
• deterministisch, d.h.: in jedem Schritt ist festgelegt:
(a) Was soll getan werden? (‘Operation’)
(b) Womit soll etwas getan werden? (‘Operanden’)
(c) Wie geht es weiter? (‘Kontrollfluss’)
Bemerkung: Man kann den letzten Punkt auch fallen lassen und gelangt zu den nicht deterministischen Algorithmen: solche mit Zufallscharakter. (Realisierung durch sog. ‘Pseudozufallszahlen’)
Beispiel für Algorithmen:
(a) Tante Emmas Backrezept für Gugelhupf
(b) Der Euklidsche Algorithmus zur Berechnung des GGT(a,b) von 2 natürlichen Zahlen a
und b
(c) Gauß’scher Algorithmus zur Auflösung linearer Gleichungssysteme
(d) Der Algorithmus in einem Schachprogramm zum Bestimmen des nächsten Zuges.
Zur Klärung der Fragen
• Gibt es Aufgaben, die ein Computer prinzipiell nicht lösen kann? (→Ja!)
• Gibt es Aufgaben, die ein Computer nicht mit vernünftigem Zeit- und Speicheraufwand
lösen kann? (→Ja!)
eignet sich der intuitive Algorithmusbegriff nicht. Präzise Formulierungen sind unter anderem:
• allgemein-rekursive Funktionen (Göde, Kleene, 1936)
• Lambda-Kalkül(Church, 1936)
• Turing-Maschine(Turing, 1936)
• Random-Access-Maschinen (1964)
• jede moderne Programmiersprache
1.4. WAS SIND DIGITALRECHNER
1.4
15
Was sind Digitalrechner
‘Digital’ im Gegensatz zu ‘Analog’: Bei Analog-Rechnern werden die Zahlen, mit denen gerechnet wird, durch zu den Zahlen proportionale physikalische Größen dargestellt.
Beispiel: −1000 . . . + 1000 entspricht −10.0V . . . + 10.0V
Bei Digitalrechnern werden die Zahlen durch Rechenelemente mit diskreten Zuständen kodiert.
Meistens sind es nur 2 Zustände, ‘0’ und ‘1’. Diese werden dann wieder durch 2 Spannungen
repräsentiert.
Beispiel: ‘0’ = 0V, ‘1’ = 5V
Eine Leitung repräsentiert damit 1 Bit; ein Bündel von 8 Leitungen ist damit 1 Byte.
Ein moderner Rechner ist ein hochkomplexes System. Die Bewältigung der Komplexität gelingt
durch sog. ‘Abstraktionsbarrieren’: Trennlinien in der Betrachtung bzw. Beschreibung und
Realisierung, die strikt eingehalten werden. Damit stellt sich ein Rechner etwa so dar:
Anwendungsprogramme
Systemnahe Programme
Betriebssystem
Software
Hardware Abstraktion / Treiber
Register - Transfermodell
Hardware
“Instruktionssatz”
der Maschine
Gatter zur Realisierung von Logik
Transistoren als Schalter
Elektronen - Transport im Kristall
Achtung: Dieses Bild stellt nicht den Aufbau eines Rechners aus Baugruppen dar! Er sieht in
etwa so aus (“Von Neumann-Architektur”)
16
KAPITEL 1. GRUNDBEGRIFFE UND ARBEITSGEBIETE DER INFORMATIK
Steuerwerk
Rechenwerk
Arbeitsspeicher
Registerwerk
Festplatte
1.5
Ein /Ausgabe
Ein /Ausgabe
Modul
Modul
Arbeitsgebiete der Informatik
Teilgebiete sind
• Theoretische Informatik
• Technische Informatik
• Praktische Informatik
• Angewandte Informatik
(a) Theoretische Informatik
(b) Technische Informatik
• Automatentheorie
• Computerarchitektur (→ ‘Architekt’)
• Algorithmentheorie
• Computerorganisation (→ ‘Statiker’)
• Theorie der Berechenbarkeit
• Register-Transfer-Modelle
• Komplexitätstheorie
• Gatterentwurf
• Theorie der formalen Sprachen
• Halbleitertechnik
• Bussysteme
• Peripherie
• Rechnernetze
1.5. ARBEITSGEBIETE DER INFORMATIK
(c) Praktische Informatik
(Achtung: ! Das ist nicht der praktische Einsatz der Informatik, das ist ‘Angewandte Informatik’)
• stellt die Arbeitsfähigkeit von Computern sicher
• Softwareentwicklung
17
(d) Angewandte Informatik
Wendet Erkenntnisse, Methoden und Programme an in
• Ingenieurwissenschaften (numerische
Verfahren)
• Naturwissenschaften (Simulation)
• Programmentwicklung und Test
• Mathematik
(Computer-Algebra-Systeme)
• Betriebssysteme
• Medizin (Diagnose-Systeme)
• Compiler
• Rechtswissenschaften
(Informationssysteme)
• Datenbanken
• Informationssysteme
• Protokolle für Rechnernetze
• Technik (Eingebettete Systeme)
18
KAPITEL 1. GRUNDBEGRIFFE UND ARBEITSGEBIETE DER INFORMATIK
Kapitel 2
Zahlendarstellungen und Codes
2.1
Stellenwertsysteme
Was meinen wir eigentlich mit ‘1234’ ?
1234 = 1 · 103 + 2 · 102 + 3 · 101 + 4 · 100
Allgemein sei b > 1, b ∈ N (die ‘Basis’) und zi ∈ {0, 1, . . .P
, b − 1}, i = 0 . . . n (die ‘Ziffern’).
n
Dann meint die Schreibweise zn zn−1 . . . z1 z0 b die Zahl z = i=0 zi · bi .
Beispiel: b = 2, Ziffern: 0,1
Zahl: 10112 = 1 · 23 + 0 · 22 + 1 · 21 + 1 · 20 = 1110
2.2
Dualzahlen, Oktalzahlen, Hexadezimalzahlen
(a) b = 2 mit den Ziffern 0,1; Dualzahlen
Beispiel: 10112 = 1 · 23 + 0 · 22 + 1 · 21 + 1 · 20 = 1110
(b) b = 8 mit den Ziffern 0, 1, . . . , 6, 7 Oktalzahlen
Beispiel: 3778 = 3 · 82 + 7 · 81 + 7 · 80 = 25510
(c) b = 16 mit den Ziffern 0, 1, . . . , 8, 9, A, B, . . . , E, F Hexadezimalzahlen
Beispiel: AF F E16 = 10 · 163 + 15 · 162 + 15 · 161 + 14 · 160 = 4504510
Bemerkung: In der Programmiersprache C wird das so notiert: 0xAF F E.
Umrechnungen in diesen drei Systemen sind besonders einfach!
Jeweils 3 Dualziffern ergeben zusammengefasst eine Oktalziffer:
101 |{z}
110 |{z}
100 2
|{z}
5
6
48
Jeweils 4 Dualziffern ergeben zusammengefasst eine Hexadezimalziffer:
1010
| {z} 1111
|{z } 1110
|{z } 2
| {z } 1111
A
F
F
19
E16
20
KAPITEL 2. ZAHLENDARSTELLUNGEN UND CODES
Warum ist das so?
Analogon: Angenommen wir hätten ein 1000er System mit den Ziffern 0 . . . 999. Dann ist
432.759.91310 = 432.759.913 1000
|
{z
}
Jeweils eine
Ziffer
Beispiel zur Anwendung: Kompakte Darstellung von Schalterstellungen:
0011
|{z } 1011
| {z } 2 = 3B16
3
B16
wobei jede ‘1’ ‘ein’ und jede ´0’ ‘aus’ darstellt.
Umrechnung von Hexadezimal nach Dezimal
rechnung von Dezimal nach Hexadezimal?
“Fortgesetztes Dividieren mit Rest”
Beispiel:
45054 : 16 =
2815 : 16 =
175 : 16 =
10 : 16
=
ist nun bekannt, aber wie funktioniert die Um-
2815 R 14 E
175 R 15 F
16
R 15 F
0
R 10 A
Stop wenn Quotient = 0, dann Aufschreiben der Reste in umgekehrter Reihenfolge!
Bemerkung: Das funktioniert natürlich auch mit anderen Basen!
Bezeichnung: Eine Hex-Stelle heißt ‘Nybble’. ⇒ 1 Byte kann durch 8 Bit und damit durch 2
Nybble dargestellt werden.
2.3
ASCII und Unicode
Mit n Dualziffern lassen sich 2n verschiedene Werte darstellen. Was sie kodieren und wie die
Kodierung erfolgt, ist damit noch nicht festgelegt!
Druckbare Zeichen (und Steuerzeichen, wie ‘Zeilenumbruch’, ‘Seitenvorschub’, ‘Klingel’, u.A.)
werden in sog. Zeichensätzen kodiert. Der gebräuchlichste ist der ASCII (“American Standard
Code for Information Interchange”). Er wird in einem Byte kodiert und belegt die unteren
128 Zeichen (der 256 möglichen Zeichen; “7-Bit-ASCII”).
Beispiel:
Zeichen
t(Leerzeichen)
(
)
∗
+
0
..
.
Code(Hex)
20
28
29
2A
2B
30
..
.
Zeichen
A
..
.
Z
a
..
.
Code(Hex)
41
..
.
5A
61
..
.
z 7A
Zeilenumbruch 0A
9 39
Seitenvorschub 0C
@ 40
Klingel 07
Bemerkung: Im PC sind die oberen 128 Zeichen des Zeichensatzes belegt mit Umlauten, Grafikzeichen, u.Ä. Das ist nicht standarisiert.
2.4. DARSTELLUNG VON GANZEN ZAHLEN
21
Um alle auf der Erde vorkommenden Sprachen und Satzzeichen kodieren zu können braucht
man mehr Bits: ‘Unicode’ wird in 16 Bits (4 Nybbles) kodiert. Dabei stimmen die Codes 0000
- 007F in den unteren 8 Bits mit ASCII überein.
2.4
Darstellung von ganzen Zahlen
Muss mann nur positive ganze Zahlen kodieren, geht es ganz einfach: Mit n Bits kann man
die Zahlen 0 . . . 2n − 1 direkt darstellen.
Beispiel: Mit 4 Bits lassen sich die Zahlen 0 . . . 15 darstellen:
0000 0
0001 1
.. ..
. .
0111
1000
1001
..
.
7
8
9
..
.
1111
15
Bemerkung: So werden in C die Typen ‘unsigned . . . ’ dargestellt.
Braucht man auch negative Zahlen, dann spendiert man die Hälfte der Codes hierfür. Aus
technischen Gründen (einfache Gatternetze) tut man das nach der sog. ‘Zweierkomplement’Methode: Um eine Zahl zu negieren, wird sie bitweise komplementiert, und dann 1 dazugezählt
(Addieren im Dualsystem siehe A.5, Seite 64).
Beispiel: Bei einer 4-Bit-Darstellung ist -5 zu kodieren:
0000 0
5 entspricht 0101
0001 1
Komplement 1010
.. ..
. .
+1 1011 = -5
0110 6
Zurück geht es mit der gleichen Vorschrift! Damit sieht die Kodierungstabelle
0111 7
wie nebenstehend aus.
1000 -8
Bemerkung: Das vorderste Bit (“MSB”, “Most significant Bit”) ist genau ge1001 -7
nommen dann gesetzt, wenn die Zahl negativ ist.
1010 -6
So werden in C die Typen “signed . . .” kodiert.
.. ..
. .
Die Null (0) wird nur einmal dargestellt, es gibt keine ‘-0’. Zur negativen Zahl
1110
-2
(−2n−1 ) gibt es kein positives Gegenstück. Additionen können durchgeführt
1111 -1
werden, ohne das Vorzeichen besonders zu berücksichtigen:
Dezimal
-2
+5
3
Dual
1110
Hierbei wird der Überlauf ignoriert!
+ 0101
0011
Eine Subtraktion wird durch die Addition des 2er-Komplements durchgeführt:
Dezimal
-1
- +6
-5
Dual
0001
Auch hierbei wird der Überlauf ignoriert!
+ 1010
1011
22
2.5
KAPITEL 2. ZAHLENDARSTELLUNGEN UND CODES
Festkommadarstellung
Aufgabe: Mit Preisen in Euro soll auf den Cent genau gerechnet werden. (z.B.: 3,55Eur +
2,05Eur = 5,60Eur)
Lösung: Multiplikation mit 100 (‘Skalierung’), dann Rechnen mit ganzen Zahlen! Interpretation des Ergebnisses durch ‘Deskalierung’ (teilen durch 100). In der Informatik werden Skalierungsfaktoren, die eine Potenz von 2 sind, bevorzugt, da man dann die Skalierung nicht
wirklich durchführen muss!
Beispiel: Ganze Zahlen sollen als 4 Bytes kodiert sein. Diese 4 Bytes werden in der ‘3.1’Darstellung wie folgt genutzt: 3 Bytes für den ganzzahligen Anteil, 1 Byte für den Nachkommaanteil, ausgedrückt in 256-tel.
Beispiel: Was bedeutet 0000 3AC0 in 3.1-Darstellung?
00 |{z}
00
|{z}
3A
|{z}
C0
|{z}
= 58, 7510
0·2562 +0·2561 +58·2560 +192·256−1
Bemerkung: Auch hier kann das 2er-Komplement zu Darstellung von negativen Zahlen verwendet werden.
2.6
Fließkommadarstellung
Forderungen beim Rechnen in den Naturwissenschaften:
(a) sehr kleine und sehr große Zahlen müssen darstellbar sein.
Elementarladung = 1, 6 · 10−19 As
Lichtgeschwindigkeit = 2 · 108 m
s
(b) Die relative Genauigkeit der Darstellung ist wichtiger als die absolute Genauigkeit.
Elementarladung ±0, 001 · 10−19 As ≈ ±0, 6%
Lichtgeschwindigkeit = ±0, 001 · 108 m
s ≈ ±0, 6%
⇒Fixkommadarstellung wäre große Verschwendung!
Idee: Die signifikanten Stellen und die Größenordnung der Zahl werden getrennt notiert.
Beispiel: 1, 6 · 10−19 → signifikante Stellen : 1,6
Größenordnung
: 10−19
Definitiver Standard: IEEE-754 legt verschiedene Fließkommadarstellungen fest. Wir betrachten exemplarisch das “short real” Format. Die Gesamtgröße einer Zahl beträgt 32 Bits, davon:
Vorzeichen
Exponent
Mantisse
(‘Signifikant’)
: 1 Bit
: 8 Bits
: 23 Bits
Bemerkung:
(a) Das Vorzeichen bezieht sich auf die Mantisse
(b) Der Exponent bezieht sich auf die Basis 2 und wird als 127 + wirklicher Exponent abgelegt. (sog. ‘Bias’-Darstellung; vermeidet ein negatives Vorzeichen für den Exponenten!)
2.7. CODES ZUR DATENKOMPRIMIERUNG
23
(c) Die Zahl wird normalisiert gespeichert, d.h. der Exponent wird so gewählt, sodass die
Folge der Dualziffern mit 1,xxx beginnt. Die ‘1’ wird nicht mitgespeichert.
Beispiel: Die Zahl −11, 929920 · 10−17 soll im “short real”-Format dargestellt werden.
−11, 929920 · 10−17 ≈ 1, 25 · 2−10 (−11, 929920 · 10−17 n-mal mit 2 multiplizieren, bis vor dem
Komma eine 1 steht (hier ist n = 20), ist die Zahl größer als 1, n mal durch 2 teilen)
⇒ Vorzeichen = 1
Exponent = 127 + (−20) = 10710 = 011010112
Mantisse = 1, 2510 = 1, 012 → 0100 . . . 00
Darstellung |1011010110100
. . . 00}
{z
32Bits
Hex
B5 A0 00 00
Achtung: Eine Zahl, die sich im Dezimalsystem exakt darstellen lässt, wird im Allgemeinen im
Binärsystem mit endlich vielen Ziffern nur angenähert, und umgekehrt. ⇒ Fließkommadarstellungen sind mit Fehlern behaftet! Deshalb ist z.B.: ein Vergleich auf exakte Übereinstimmung
zweier Fließkommagrößen x==y unsinnig. Richtig wäre z.B.: |x − y| < 10−10
Beim Rechnen mit Fließkommagrößen treten noch weitere Quellen für Fehler auf:
(a) “Exponenten-Overflow”: Der Exponent des Ereignisses fällt aus dem darstellbaren Bereich nach oben hinaus.
(b) “Exponenten-Underflow”: wie (a), aber jetzt ist der Exponent zu klein. Dann wird das
Ergebnis üblicherweise zu Null (0) gesetzt. ⇒ Verlust aller Ziffern!
(c) Beim Addieren und Subtrahieren werden zunächst durch Schieben der Mantisse die
Exponenten gleich gemacht. ⇒ Verlust einiger Ziffern der Mantisse!
2.7
Codes zur Datenkomprimierung
häufiges Vorkommen eines Symbols = wenig Information
seltenes Vorkommen eines Symbols = viel Information
Sollte sich das nicht in der Kodierung der Symbole niederschlagen?
Wir hatten festgestellt:
Idee: Häufige Symbole werden durch wenige Bits, seltene durch mehr Bits dargestellt. ⇒ im
Mittel reichen weniger Bits aus, um eine Folge von Symbolen darzustellen!
Realisierung: “Huffman-Codierung”
Beispiel:
Symbol Häufigkeit Code 1 Code 2
a
1/8
00
110
b
1/8
01
111 Jede Zeile ist ein Codewort.
c
1/2
10
0
d
1/4
11
10
Code 1 ist ein Code mit konstanter Länge. Die Zeichenkette “cadccdcb” wird mit 16 Bits
codiert.
Code 2 ist ein Huffman-Code und codiert die Zeichenkette “cadccdcb” mit 14 Bits.
Bemerkung:
(a) Wichtige Eigenschaft bei Codes mit nicht konstanter Länge ist die Fano-Bedingung
(Präfix-Freiheit). Kein Codewort für ein Zeichen bildet den Anfang eines Codes für
ein anderes Zeichen.
24
KAPITEL 2. ZAHLENDARSTELLUNGEN UND CODES
(b) Die Konstruktion eines Huffman-Codes ist nicht schwierig; man benötigt dazu einen
Binär-Baum. Für unser Beispiel sieht der Binär-Baum so aus:
{c,d,a,b}: 1/1
0
c:1/2
1
{d,a,b}: 1/2
0
1
{a,b}: 1/4
d:1/4
0
a:1/8
1
b:1/8
Das Konstruktionsprinzip: Fasse immer zwei Knoten mit der kleinsten Häufigkeit zusammen zu einem Knoten mit der Summe der Häufigkeiten.
Der Pfad von der Wurzel zu einem Blatt gibt die Codierung eines Symbols an diesem
Blatt an.
(c) Huffman-Codierung ist eine der bei JPEG und MPEG verwendeten Codierungen.
2.8
Codes zur Fehlererkennung und -korrektur
Idee: Nicht alle Codeworte werden zur Codierung eines Symbols benutzt; manche sind ‘ungültig’.
Sieht der Empfänger einer Nachricht ein solches Codewort, weiß er, dass ein Fehler vorliegt.
Beispiel: Das Paritätsbit bei 7-Bit-ASCII.
Wir wählen das achte Bit beim Übertragen von ASCII-Zeichen so, dass immer eine gerade
Anzahl von Bits gesetzt ist (“even parity”; “odd parity” geht entsprechend).
Zeichen
@
A
B
C
Hex
0x40
0x41
0x42
0x43
Binär
01000000
01000001
01000010
01000011
Parity
→11000000
→01000001
→01000010
→11000011
Sieht der Empfänger z.B.: 11001011, so weiß er, dass mindestens 1 Bit falsch ist, aber nicht
welches.
Die grundsätzliche Idee ist: ‘Abstand’ der gültigen Codeworte muss noch größer werden.
Definition: Die “Hamming-Distanz” von zwei Codeworten ist die Anzahl von Bits, in denen
sie sich unterscheiden. Die Hamming-Distanz eines Codes ist das Minimum der HammingDistanzen aller gültigen Codeworte dieses Codes.
Beispiel: Die Hamming-Distanz eines ASCII-Codes mit der Parität ist 2. Er erlaubt das Erkennen von 1-Bit-Fehlern.
2.8. CODES ZUR FEHLERERKENNUNG UND -KORREKTUR
25
Allgemein: Zum Erkennen von n-Bit-Fehlern muss der Code eine Hamming-Distanz von mindestens n + 1 haben.
n
n
n+1
= gültiges Codewort
=ungültiges Codewort
Mit einem ähnlichen Verfahren kann man sogar n-Bit-Fehler korrigieren.
n
n
d = 2n
Wo gehört dieses
= gültiges Codewort
hin?
=ungültiges Codewort
⇒ d > 2n
Die Hamming-Distanz des Codes muss also mindestens 2n+1 sein.
Beispiel: Ein Code zur Codierung mit m Nachrichtenbits soll r Prüfbits zusätzlich bekommen,
um alle 1-Bit-Fehler zu korrigieren zu können. Jedes Codewort besteht also aus n = m + r
Bits.
Es gibt 2m legale Nachrichten, von denen jede von n illegalen Codeworten mit HammingDistanz 1 ‘umgeben’ ist (Jedes Bit des Codewortes kann im Fehlerfall invertiert sein). Also
benötigt jede der 2m legalen Nachrichten n + 1 Codeworte zur Codierung. Die Gesamtanzahl
der Codeworte ist 2n . Also muss gelten: (n + 1) · 2m ≤ 2n = 2m+r ⇒ m + r + 1 ≤ 2r ⇒liefert
eine untere Grenze für r.
So benötigt man z.B. für m = 16 mindestens r = 5 Prüfbits. ((16 + 5 + 1) ≤ 25 geht, aber
(16 + 4 + 1) 6≤ 24 geht nicht.)
Bemerkung: Ein solcher Hamming-Code wird wie folgt konstruiert: Nummeriere die Bits eines
Codewortes von links nach rechts beginnend bei 1:
26
KAPITEL 2. ZAHLENDARSTELLUNGEN UND CODES
1
↑
P
2
↑
P
3
↑
D
4
↑
P
5
↑
D
6
↑
D
7
↑
D
8
↑
P
9
↑
D
10
↑
D
11
↑
D
12
↑
D
Jedes Prüfbit stellt die gerade Parität einer gewissen Menge von Bits (einschließlich sich selbst)
sicher. Jedes Datenbit kann im mehreren solcher Mengen auftauchen.
Beispiel: Das Datenbit an Position 11 = 8 + 2 + 1 ist an der Paritätsberechnung der Prüfbits
8, 2 und 1 beteiligt. Der Empfänger der Nachricht prüft die Prüfbits auf korrekte Parität und
summiert die Positionen aller fehlerhaften Prüfbits. Ist die Summe 0, so liegt kein Fehler vor,
ansonsten ist sie genau die Position des fehlerhaften Bits.
Beispiel: m = 4, r = 3, n = 7
Codewort = 1010001 Wie lautet die Nachricht?
1
2
3
4
5
6
7
1
0
1
0
0
0
1
P
P
D
P
D
D
D
An der Berechnung der
Prüfbits sind beteiligt:
1: 1357 →Falsch 
2: 2367 →OK
⇒ fehlerhaftes Bit: 1 + 4 = 5 ⇒ Nachricht ist richtig 1101

4: 4567 →Falsch
Bemerkung: Es gibt noch viele weitere Codes, die Fehlererkennung oder Fehlerkorrektur von
Fehlern erlauben. Wichtig sind:
(a) Cyclic Redundancy Check (CRC) - z.B. Festplatte, Datenübertragung
(b) Bose-Chandhuri-Hocquenglen (BCH) und speziell Reed-Dolomon-Codes (CD-ROM, DVD)
Kapitel 3
Algorithmen und
Datenstrukturen
3.1
Algorithmen und Programme
Problem
↑
Spezifizieren
→
Algorithmierung
→
Algorithmus
↑
Verifizieren
→
Programmierung
→
Programm
↑
Testen
Beispiel:
(a) Problem: Sind a und b > 0 zwei natürliche Zahlen und ist m > 0 die größte aller Zahlen,
die sowohl a als auch b teilen, so heißt m der größte gemeinsame Teiler von a und b:
m = ggT(a, b) Entwerfen Sie einen Algorithmus und dann ein Programm zum Berechnen
des ggT von zwei natürlichen Zahlen!
(b) Algorithmierung: Wenn a = b, dann ist auch der ggT (a, b) = a = b.
Sei o.B.d.A. (ohne Berücksichtigung der Algemeinheit) a > b. Wenn t > 0 irgendein
Teiler von a und b ist, dann ist er auch Teiler von c = a − b (denn dann existieren
x, y ∈ N mit a = xt und b = yt : c = a − t = xt − yt = (x − y)t). Speziell ist der ggT von
a und b ein Teiler von a und b. ⇒ Der ggT(a, b) ist ein Teiler von c.
Beispiel: ggT(115,46) = ggT(69,46) = ggT(23,46) = ggT(23,23) = 23
Bemerkung: Im Folgenden ist eine Zeile, die mit ‘//’ beginnt, ein Kommentar: dient ‘nur’
zur Erläuterung für den Menschen!
Algorithmus für ggT:
//Eingabe mit Überprüfung
1. Wiederhole die folgende Aktion
1.1. Erfrage die Werte für a und b solange bis a > 0 und b > 0 (ganze Zahlen)
//Berechnung des ggT
2. Solange a 6= b führe folgende Aktion aus
2.1. Falls a > b dann
2.1.1. a ← a − b sonst
2.1.2. b ← b − a
//jetzt ist a = b =ggT(a, b)
27
28
KAPITEL 3. ALGORITHMEN UND DATENSTRUKTUREN
//Ausgabe
3. Gib den Wert von a aus.
(c) Programmierung: z.B.: mittels Programmiersprache C
int main(void)
{
int a,b;
do
{
scanf("%d",&a);
scanf("%d",&b);
}
while(a <= 0 || b <= 0);
while(a != b)
{
if(a>b)
a = a-b;
else
b = b-a;
}
printf("%d/n", a);
return 0;
}
Bemerkung: Diese Programmiersprache nennt man ‘prozedual’. Merkmale einer prozedualen Programmiersprache sind: Zuweisungen, Schleifen, Reihenfolge der Berechnungen
ist wichtig.
Es gibt auch andere Programmierweisen (‘Paradigmata’):
(a) funktionale, mit den Merkmalen: keine Zuweisungen, Rekursion, Reihenfolge der
Berechnungen spielt fast keine Rolle .
Beispiel: ggT in Scheme (ein LISP Dialekt):
(define (ggT a b)
(if (= a b)
a
(if (> a b)
(ggT (- a b) b)
(ggT a (- b a)))))
(b) logisch, mit den Merkmalen: keine Zuweisungen, Rekursion, Reihenfolge der Berechnungen wird gar nicht angegeben.
Man formuliert “wahre Aussagen” über das Problem und lässt die Maschine einen
Weg zur Lösung selbst suchen.
Beispiel: ggT in Prolog (Programming in Logic):
ggT(A, A, A).
ggT(A, B, C) :- A > B, D is A - B, ggT(D, B
3.2. KONTROLLELEMENTE VON ALGORITHMEN
29
ggT(A, B, C) :- A < B, D is B - A, ggT(A, D, C).
Hinweise:
ggT(A, B, C)
:,
is
3.2
:
:
:
:
“Der ggT von A und B ist C”
‘wenn’
‘und’
‘bezeichnet’
Kontrollelemente von Algorithmen
Ablaufsteuerung eines Algorithmus wird spezifiziert durch die sog. ‘Kontrollelemente’:
• Folge (Sequenz)
• Auswahl (Selektion)
• Wiederholung (Iteration)
Sie bestimmen die Reihenfolge von Aktionen in Algorithmen. Visualisierung durch ‘Flussdiagramme’ bzw. ‘Struktogramme’ (= Nassi-Schneidermann-Diagramme).
Grundlegendes Element ist die ‘Verarbeitung’ (= eine Aktion bzw. die Zusammenfassung
mehrerer Aktionen zu einer).
Flussdiagramm:
↓
v
↓
Struktogramm:
v
Die eindimensionale Darstellung geschieht durch ‘Pseudocode’ (= programmiersprachenähnliche Konstrukte, aber mit freierer Notation): V
Eine einfache Verarbeitung wird mit Semikolon (;) abgeschlossen.
//. . . bezeichnet einen Kommentar (bis zum Zeilenende)
Beispiel:
//Das ist eine Zuweisung an die Variable x:
x ← 42;
3.2.1
Folge
Flussdiagramm
V1
V2
..
.
Vn
Struktogramm
V1
V2
..
.
Vn
30
KAPITEL 3. ALGORITHMEN UND DATENSTRUKTUREN
In Pseudocode:
{ V1
V2
..
.
Die Sequenz repräsentiert nach außen nur eine Verarbeitung!
→ Hilfsmittel zur hierarchischen Strukturierung von Algorithmen.
Vn }
3.2.2
Auswahl
Gibt es in drei Formen:
(a) bedingte Verarbeitung (“einarmiges if”)
Struktogramm
Flussdiagramm
B
B
B: ‘Bedingung’
f
w
f
V
w
V
In Pseudocode:
falls B dann V
Beispiel:
falls a < 0 dann a ← −a;
(b) einfache Alternative (“zweiarmiges if”)
Flussdiagramm
Struktogramm
B
w
B
f
w
f
V1
V1
V2
In Pseudocode:
falls B dann V1 sonst V2
Beispiel:
falls a > b dann a ← a − b sonst b ← b − a;
(c) mehrfache Alternative
V2
3.2. KONTROLLELEMENTE VON ALGORITHMEN
31
Struktogramm
Flussdiagramm
S
W1
V1
W2
V2
S
...
W1
Wn
Vn
Sonstige
W2
V1
V2
Wn
...
Vn
Sonstige
VS
VS
S: ‘Selektor’ (ein Ausdruck)
Wi : Werte (konstante Ausdrücke)
In Pseudocode:
falls S =
W1 : V1
W2 : V2
..
.
Wn :
Sonst:
3.2.3
Beispiel:
falls x mod 3
0: x ← x / 3;
1: x ← x +1;
2: x ← x -1;
sonst: ; //Maschine kaputt!
Bemerkung: Hinter ‘Sonst’ steht hier eine
“leere Anweisung”.
Vn
VS
Wiederholung
Gibt es in vier Formen:
(a) Wiederholung mit vorausgehender Bedingungsprüfung.
Flussdiagramm
B
Struktogramm
B
f
V
w
V
In Pseudocode:
solange B wiederhole V
Bemerkung: Diese Art Schleife heißt “abweisende Schleife”: Möglicherweise wird der
Rumpf keinmal durchlaufen.
Beispiel: Untersuche, ob eine ungerade natürliche Zahl p eine Primzahl ist. Dazu:
α) p > 2 ist eine Primzahl, falls sie durch kein t mit 1 < t < p teilbar ist, d.h. wenn
alle 1 < t < p gilt: p mod t 6= 0.
β) Da p ungerade ist, genügt es, nur ungerade t zu untersuchen.
32
KAPITEL 3. ALGORITHMEN UND DATENSTRUKTUREN
γ) Es genügt, nur solche t zu untersuchen, für die t2 ≤ p. Wäre nämlich t ein Teiler von
√
√
p mit t2 > p, d.h. t > p, dann gäbe es ein x mit p = x · t, d.h. x = pt < √pp = p,
√
also auch ein Teiler, der kleiner als p ist.
Algorithmus:
Tastatureingabe: p;
falls p > 2 und p mod 2 6= 0 dann {
// initialisiere t
t ← 3;
solange (t · t ≤ p) und (p mod t 6= 0) wiederhole {
// bisher kein t mit t teilt p gefunden
// probiere nächstes t
t ← t + 2;
}
//Hier ist (t · t > p) oder (p mod t = 0)
falls t · t > p dann {
Bildschirmausgabe: “p ist eine Primzahl”
} sonst {
Bildschirmausgabe: “p ist keine Primzahl”
}
}
Sonst { //Hier ist (p ≤ 2) oder (p mod 2 = 0)
Bildschirmausgabe: “Unzulässiges p!”
}
Bemerkung: : Beachte die strikte Verwendung von {. . .}
falls B {
...
} sonst {
...
}
Das schützt vor Fehlern beim Einfügen von Zeilen in Sequenzen:
falls x < 1 dann
x ← x + 1;
Bildschirmausgabe: “Hier wird x erhöht”; //Denkste!
y ← 2x + 3;
Bemerkung: Beim negieren von zusammengesetzten Bedingungen: Anwendung der DeMorgan’schen Regeln:
¬(α ∧ β) ⇔ (¬α ∨ ¬β)
¬(α ∨ β) ⇔ (¬α ∧ ¬β)
Beispiel: ¬((p > 2) ∧ (p ≤ 5)) ⇔ (¬(p > 2) ∨ ¬(p ≤ 5)) ⇔ ((p ≤ 2) ∨ (p > 5))
(b) Wiederholung mit Zählvariablen
In Pseudocode:
für I solange B mit S wiederhole V
I:‘Initialisierung’, B:‘Bedingung’, S:‘Schrittsteuerung’, V:‘Rumpf’
Bemerkung: Hierfür gibt es keine besonderen Symbole in Flussdiagrammen bzw. Struktogrammen. Wir können dies aber aus Bekanntem zusammensetzen:
3.2. KONTROLLELEMENTE VON ALGORITHMEN
33
I
f
B
w
V
S
Zwei wichtige Spezialfälle:
α) ‘Aufwärtszählen’
für z ← AW solange z ≤ EW mit z ← z+ SW wiederhole V
AW: ‘Anfangswert’, EW: ‘Endwert’, SW: ‘Schrittweite’
z ← AW
z ≤EW
f
w
V
z ← z+ SW
β) ‘Abwärtszähler’
für z ← AW solange z ≥ EW mit z ← z− SW wiederhole V
0 Flussdiagramm wie oben aber mit B z
34
KAPITEL 3. ALGORITHMEN UND DATENSTRUKTUREN
Flussdiagramm
Struktogramm
V
V
B
B
f
w
In Pseudocode:
wiederhole V solange B;
Bemerkung: Diese Art Schleife heißt “nicht abweisende Schleife”. Der Rumpf der Schleife
wird immer mindestens einmal durchlaufen.
Beispiel:
wiederhole{
Bildschirmausgabe: “Bitte a > 0 eingeben”;
Tastatureingabe: a;
} solange a ≤ 0;
//Hier ist a > 0
(d) Wiederholung ohne Bedingungsprüfung
Flussdiagramm
V
Struktogramm
V
In Pseudocode:
für immer wiederhole V;
Bemerkung: Das ist die sog. ‘Endlosschleife’. Anwendung z.B. in der Prozessrechentechnik:
für immer wiederhole {
Bremspedalsensor lesen;
Radsensor lesen;
Bremse ansteuern;
}
3.3
Strukturierung von Algorithmen durch Blöcke
Idee:Zusammenfassen von Aktionen bekommt einen Namen und kann durch nennen des Namens(‘Aufruf’) aktiviert werden. Vorteile sind:
3.3. STRUKTURIERUNG VON ALGORITHMEN DURCH BLÖCKE
35
(a) Gliederung eines Algorithmus durch hierarchische Dekomposition (Prinzip: “Teile und
Herrsche”)
(b) Wiederverwendung durch mehrfachen Aufruf anstelle mehrfachen Hinschreibens.
• universeller (Anzahl der Aufrufe muss nicht bekannt sein)
• weniger fehleranfällig (bei Änderungen)
(c) Kapselung unwichtiger Details (“Information Hiding”)
(d) Ermöglicht Parallelarbeit beim Programmieren durch Teilen der Aufgabe,
Modifikationen, damit es brauchbar wird:
(a) In einen Block müssen Daten hinein und Daten heraus ⇒ Blöcke haben eine sog. ‘Parameterliste’
(b) Ein Block soll bei Bedarf eigene (lokale) Daten verwalten dürfen ⇒ Blöcke haben einen
sog. ‘Deklarationsteil’
In Pseudocode:
block N(ein: x1 ∈ X1 , x2 ∈ X2 , . . . ; mit: y1 ∈ Y1 , y2 ∈ Y2 , . . . ; aus: z1 ∈ Z1 , z2 ∈ Z2 , . . .)
lokal: w1 ∈ W1 , w2 ∈ W2 , . . . ;
V
xi , yi , zi
Xi , Yi , Zi
w1
V
N
heißen “formale Parameter”
sind deren ‘Trägermengen’ (‘Typen’)
sind die lokalen Variablen mit den Typen Wi
ist eine Verarbeitung (typischerweise eine Folge)
ist der Name des Blocks
Es gibt drei verschiedene Arten von Parametern:
(a) ‘ein’-Parameter dienen zum Übermitteln von Werten in den Block hinein. Beim Aufruf
steht an der entsprechenden Stelle ein Ausdruck; dessen Wert wird übermittelt (“callby-value”)
(b) ‘aus’-Parameter dienen zum Übermitteln von Werten aus dem Block heraus. Beim Aufruf
steht an der entsprechenden Stelle ein Variablen-Name; diese Variable erhält bei der
Zuweisung an den Parameter (innerhalb des Anweisungsteil des Blockes) den Wert.
(c) ‘mit’-Parameter sind eine Kombination aus (a) und (b). Sie Übermitteln einen Wert
in den Block hinein und einen aus dem Block heraus. Übergabe wie bei (b): “call-byreference”, d.h. Wenn im Anweisungsteil der Name des Parameters genannt wird, wird
auf die im Aufruf stehende Variable zugegriffen.
36
KAPITEL 3. ALGORITHMEN UND DATENSTRUKTUREN
Beispiel: Block zur Berechnung von n!
block fakultät(ein: n ∈ N0 ; aus: ergebnis ∈ N0 )
lokal: produkt ∈ N0 , f aktor ∈ N0
{
produkt ← 1;
für f aktor ← 1 solange f aktor ≤ n mit f aktor ← f aktor + 1 wiederhole {
produkt ← produkt · f aktor;
}
ergebnis ← produkt;
}
Aufruf z.B. durch
fakultät(2 · x + 5, y);
oder auch
fakultät(x, y);
oder sogar
fakultät(x, x);
aber
fakultät(x, 3 · y);
geht nicht! (3 · y ist keine Variable sondern ein Ausdruck.)
Zweite Lösung (ohne Hilfsvariablen):
block fakultät(ein: n ∈ N0 ; aus: ergebnis ∈ N0 )
{
ergebnis ← 1;
solange n > 1 wiederhole {
ergebnis ← ergebnis · n;
n ← n − 1;
}
}
Beachte: Bei Übergabe durch Werte gibt es keinen nach außen sichtbaren Effekt bei Zuweisungen an den Parameter:
x ← 8;
fakultät(x, y);
//Hier ist der Wert von x immer noch 8!
Bemerkung:
(a) Jeder Aufruf eines Blocks verfügt über einen eigenen Satz von lokalen Variablen.
(b) Viele Programmiersprachen erlauben es Blöcke innerhalb von Blöcken zu definieren. Es
gilt dann die folgende Regel: Wird in einer Verarbeitung eine Variable benutzt, die weder
lokal noch in der Parameterliste des Blocks deklariert ist, dann wird sie im umgebenden
Block nach eben dieser Regel gesucht (sog. ‘statische’ oder ‘lexikalische’ Bindung von
Variablen).
Regeln zur Konstruktion von Blöcken:
(a) Namen so lokal wie möglich deklarieren!
(b) Möglichst keine globalen Daten verwenden!
3.4. REKURSION
37
(c) Nach Möglichkeit “call-by-value” verwenden!
(d) Blöcke sind so zu wählen, dass
• der innere Zusammenhang stark ist,
• der äußere Zusammenhang schwach ist.
(d.h. die Schnittstelle (Parameter, globale Variablen) soll klein sein)
3.3.1
Vorzeitiges Verlassen von Blöcken
Manchmal ist es sinnvoll, die Abarbeitung eines Blocks an einer bestimmten Stelle unmittelbar zu beenden, und zum Aufrufpunkt zurückzukehren (vor allem in Fehlersituationen angebracht).
Struktogramm
Flussdiagramm
N
N
‘N’ ist der Name des Blocks, dessen Abarbeitung beendet werden soll.
In Pseudocode:
beende N;
3.4
Rekursion
Definition: Eine Funktion (oder ein Algorithmus) heißt rekursiv, wenn sie (er) sich selbst als
einen ihrer (seiner) Berechnungsschritte enthält, in der Regel mit ‘verkleinerter’ Aufgabenstellung. Damit das Verfahren terminieren kann, muss es immer mind. einen Zweig in der
Berechnung geben, der den Wert elementar (d.h. nicht rekursiv) bestimmt (das ist der sog.
‘Basisfall’).
Beispiel:
n! =
1
n=0
n · (n − 1)! n > 0
block fakultät(ein: n ∈ N0 ; aus: ergebnis ∈ N0 )
{
falls n = 0 dann {
ergebnis ← 1;
} sonst {
fakultät(n − 1, ergebnis);
ergebnis ← ergebnis · n;
}
}
38
KAPITEL 3. ALGORITHMEN UND DATENSTRUKTUREN
Abarbeitungsprozess für den Aufruf ‘fakultät(3,x)’:
fakultät(3,x)
fakultät(2,x)
fakultät(1,x)
fakultät(0,x)
erg = 6
3.4.1
erg = 2
erg = 1
erg = 1
Verwaltung der Rekursion durch das Programmiersystem
Im oben aufgeführtem Beispiel existieren zu einer bestimmten Zeit 4 verschiedene Aktivierungen des gleichen Blocks ‘fakultät’: die Rekursionstiefe ist 4. Jede Aktivierung besitzt Ihren
eigenen Satz von lokalen Variablen. Wie werden diese vom System verwaltet?
Beobachtung: Eine Aktivierung auf der Tiefe n wird erst verlassen, wenn die von ihr erzeugte
Aktivierung auf der Tiefe n + 1 schon verlassen wurde! ⇒ Verwaltung der Aktivierungen
geschieht als ‘Stapel’ (‘Stack’):
Stapelzeiger
Aktivierung Tiefe 3
Aktivierung Tiefe 2 Aufruf
Aktivierung Tiefe 1
Tiefe
Tiefe
Tiefe
Tiefe
4
3
2 Verlassen
1
Tiefe 3
Tiefe 2
Tiefe 1
Das ist eine sehr effiziente und für den Programmierer automatische Verwaltung des lokalen
Speicherplatzes von Blockaktivierungen.
3.4.2
Wo man Rekursion besser vermeidet
Manchmal führt Rekursion zu ineffizienten Lösungen.
Beispiel: Fibunacci-Zahlen (0, 1, 1, 2, 3, 5, 8, 13, 21, 34, . . .) sind definiert durch

n=0
 0
1
n=1
fib (n) =

fib (n − 1) + fib (n − 2) n > 1
3.4. REKURSION
39
block fib(ein n ∈ N0 ; aus: f ∈ N0 )
lokal r ∈ N0 , s ∈ N0 ;
{
falls n = 0 dann f ← 0 sonst
falls n = 1 dann f ← 1 sonst {
fib(n − 1, r);
fib(n − 2, s);
f ← r + s;
}
}
Wie lange dauert es, fib(n) zu berechnen?
Dazu: Wie viele Aufrufe von fib sind für die Berechnung von fib(n) nötig?
n
0
1
2
3
4
5
6
Zn =Zahl der Aufrufe von fib
1
1
3
5
9
15
25
(Zn +1)
2
1
1
2
3
5
8
13
Vermutung: (Zn + 1)/2 ist fib(n + 1), d.h. Zn = 2 · fib (n + 1) − 1.
Beweis: durch vollständige Induktion:
α) n = 0
Z0 = 1 = 2 · fib (0 + 1) − 1
β) n = 1
Z1 = 1 = 2 · fib (1 + 1) − 1
γ) Sei Zn = 2 · fib (n + 1) − 1
und Zn−1 = 2 · fib (n) − 1
Dann:
Zn+1
= 1 + Zn + Zn−1
= 1 + 2 · fib (n + 1) − 1 + 2 · fib (n) − 1
= 2 · ( fib (n + 1) + fib (n)) − 1
= 2 · fib (n + 2) − 1
Anzahl der Aufrufe (siehe A.6, Seite 64) Zn ≈ 2 ·
√1
5
· Φn+1
Annahme: Schneller Rechner, 1 Aufruf = 1 nsec.
n
10
20
50
100
Zeit
0,18 µsec
22 µsec
41 sec
36000 Jahre
Das ist “exponentieller Anstieg”, völlig unbrauchbar für praktische Anwendungen!
Bessere Lösung: Wir heben zwei Folgeelemente in Variablen auf, und berechnen den nächsten
Folgewert als Summe der beiden Vorhergehenden:
40
KAPITEL 3. ALGORITHMEN UND DATENSTRUKTUREN
block fib(ein n ∈ N0 ; aus: f ∈ N0 )
lokal a ∈ N0 , b ∈ N0 , s ∈ N0 ;
{
a ← 0;
b ← 1;
solange n > 0 wiederhole{
s ← a + b;
a ← b;
b ← s;
n ← n − 1;
}
f ← a;
}
Status des Blocks beim Aufruf mit n = 6:
n a b
s
6 0 1
?
5 1 1
1
4 1 2
2
3 2 3
3
2 3 5
5
8
1 5 8
0 8 13 13
Anzahl der Durchläufe durch die Schleife: Zn = n
Beispiel: 1 Durchlauf = 10 ns ⇒ Z100 = 1µsec (!!! vgl. 36 000 Jahren bei Rekursion)
3.4.3
Wo man Rekursion mit Vorteil anwendet
Typischer Fall: Problem lässt sich in zwei oder mehr nicht überlappende Teilprobleme aufspalten und die Teillösungen können leicht kombiniert werden.
1. Beispiel: Markieren einer Strecke durch Teilungen bei
Strichmarken (“Wir basteln ein Lineal”):
1 1 1
2, 4, 8, . . .
mit verschieden hohen
3.4. REKURSION
41
block Lineal(ein: links ∈ N0 , rechts ∈ N0 , höhe ∈ N0 )
lokal: mitte ∈ N0
{
falls höhe > 0 dann {
mitte ← (links + rechts)/2;
Zeichne(mitte, höhe);
Lineal(links, mitte, höhe − 1);
Lineal(mitte, rechts, höhe − 1);
}
}
Aufruf zum Beispiel mit Lineal(0, 4, 2)
Lineal(0, 4, 2)
Zeichne(2, 2)
Lineal(0, 2, 1)
Zeichne(1, 1)
Lineal(0, 1, 0)
Lineal(1, 2, 0)
Lineal(2, 4, 1)
Zeichne(3, 1)
Lineal(2, 3, 0)
Lineal(3, 4, 0)
Darstellung durch ‘Aufrufbaum’ (es werden nur die Parameter für Lineal angegeben):
(0, 4, 2)
(0, 2, 1)
(0, 1, 0)
(2, 4, 1)
(1, 2, 0)
(2, 3, 0)
Reihenfolge beim Zeichnen der Marken:
1
(2.)
2
(1.)
3
(3.)
Wenn es im Rumpf von Lineal heißt:
Lineal(links, mitte, höhe − 1);
Zeichne(mitte, höhe);
Lineal(mitte, rechts, höhe − 1);
Dann ist die Reihenfolge beim Zeichnen:
(3, 4, 0)
42
KAPITEL 3. ALGORITHMEN UND DATENSTRUKTUREN
1
(1.)
2
3
(2.)
(3.)
2. Beispiel: “Türme von Hanoi” (Spiel mit n Scheiben und drei Stangen)
n
‘1’
‘2’
‘3’
Regeln:
• Es dürfen nur kleinere auf größere Scheiben liegen.
• Nur die oberste Scheibe eines Stapels darf bewegt werden.
Ziel: Der ganze Stapel soll zu Stange 3 bewegt werden.
Idee:
(a) n − 1 Scheiben auf ‘2’
(b) unterste Scheibe auf ‘3’
(c) n − 1 Scheiben auf ‘3’
block Hanoi(ein: n ∈ N0 , start ∈ N0 , hilf ∈ N0 , ziel ∈ N0 )
{
falls n > 0 dann{
Hanoi(n − 1, start, ziel, hilf );
Bildschirmausgabe: “Von ” start “ nach ” ziel;
Hanoi(n − 1, hilf, start, ziel);
}
}
3.5
Wie ‘gut’ ist ein Algorithmus?
Die Abarbeitung von Algorithmen benötigt ‘Ressourcen’ vor allem Zeit und Speicherplatz.
Wie klassifiziert man das? Komplikationen: Verhalten eines Algorithmus hängt ab von:
• der Problemgröße (z.B.: Matrixmultiplikation von 10 × 10 bzw. 100 × 100-Matrizen)
3.5. WIE ‘GUT’ IST EIN ALGORITHMUS?
43
• der Eingabewerte (z.B.: sortieren einer bereits sortierten Menge von Zahlen)
• der Fragestellung (z.B.: bester / schlechtester / mittlerer Fall)
• die Güte der Implementierung
• der zur Verfügung stehenden Software (Compiler) und Hardware
Wie abstrahiert man davon?
3.5.1
Die O-Notation
Definition: Eine Funktion g wird O(f ) genannt, falls es Konstanten c und n0 gibt, so dass
g(n) < c · f (n)
für alle n > n0 ist.
Beispiel: Bei der Analyse eines Algorithmus habe sich herausgestellt, dass die Laufzeit 3n2 +
7n − 1 ist wenn die Problemgröße n ist. Damit ist die Laufzeit O(n2 ).
Denn: n > 7 ist n2 > 7n, das heißt 3n2 +7n−1 < 3n2 +7n < 3n2 +n2 = 4n2 , also n0 = 7, c = 4.
Nutzen: Von maschinenspezifischen Merkmalen und der Implementierung wird abstrahiert; die
‘Güte’ wird an die Problemgröße gekoppelt.
Achtung: Diese Notation gibt nur eine obere Schranke der Komplexität, das muss nicht notwendigerweise die beste obere Schranke sein!
Beispiel: Die oben angegebene Laufzeit ist auch O(n3 ).
Deswegen will man im Allgemeinen auch zeigen, dass es für einen gegebenen Algorithmus
einen Ausdruck O(h) gibt, mit einer ‘kleineren’ Funktion h (Das heißt limn→∞ fh(n)
(n) = 0). Das
ist im Allgemeinen viel schwieriger!
Achtung: Die Konstanten c und n0 werden üblicherweise nicht angegeben und müssen nicht
unbedingt klein sein!
Beispiel: Algorithmus A habe die Laufzeit O(n2 ), Algorithmus B für das gleiche Problem
O(1, 5n ). Welcher ist ‘besser’ ?
(a) schnelle Antwort: A (Das stimmt gewiss für große n).
(b) bessere ‘Antwort’: Wie groß wird n? Wie groß sind die Konstanten?
Beispiel: cA = 1000, cB = 0, 001
n
cA · n2 cB · 1, 5n
1
103 1, 5 · 10−3
10
105 1, 8 · 10−2
20
4 · 105 3, 3
50 2, 5 · 106 6, 4 · 105
← Bis hier her ist B besser als A
100
107 4, 1 · 1014
3.5.2
Häufig vorkommende O(. . .)-Ausdrücke
1) Teile von Algorithmen, die eine konstante Anzahl von Malen durchlaufen werden, haben
konstante Laufzeit: O(1).
44
KAPITEL 3. ALGORITHMEN UND DATENSTRUKTUREN
2) Rekursive Algorithmen, die in jedem Schritt die Menge der Eingabedaten halbieren.
Tn = T n2 + 1
(Tn = Zeitbedarf für n)
für n ≥ 2, T1 = 0
Annahme: n = 2k
T2k = T2k−1 + 1 = T2k−2 + 1 + 1 = . . . = T20 + k = k
Tn = k = ld n
⇒ Laufzeit: O(log n)
3) Rekursive Algorithmen, die in jedem Schritt die Menge der Eingabedaten halbieren,
dazu aber jedes Element betrachten müssen.
für n ≥ 2, T1 = 0
Tn = T n2 + n
Tn = T n2 + n = T n4 +
n n n
n
+ n = . . . = n + + + + . . . 0 = 2n − 2
2
2
4
8
⇒ Laufzeit: O(n)
4) Rekursive Algorithmen, die die Eingabedaten in zwei Hälften aufspalten, und beide
Hälften getrennt abarbeiten (wie im Beispiel ‘Lineal’).
Tn = 2 · T n2 + 1
für n ≥ 2, T1 = 0
Annahme: n = 2k
T2k
T2k
2
T2k
⇒
2
T2k
22
2 · T2k−1 + 1
1
= T2k−1 +
2
=
=
2 · T2k−2 + 1 +
= T2k−2 +
1
2
1 1
+
2 4
..
.
T2k
2k
= T20 +
1
2k
= 2k − 1
= n−1
=
⇒ T2k
⇒ Tn
1 1 1
1
+ + + ... + k
2 4 8
2
1−
⇒ Laufzeit: O(n)
5) Ganz häufig: rekursive Algorithmen, die ihre Eingabedaten in zwei Hälften aufspalten
und davor, währenddessen und danach die Eingabedaten einmal durchlaufen.
Tn = 2 · T n2 + n
für n ≥ 2, T1 = 0
3.5. WIE ‘GUT’ IST EIN ALGORITHMUS?
45
Annahme: n = 2k
T2k
T2 k
2k
Tk
⇒ 2k
2
⇒
T20
+k
20
T2k
⇒ Tn
= 2 · T2k−1 + 2k
T2k−1
=
+1
2k−1
T2k−2
=
+1+1
2k−2
..
.
= k
= 2 · 2k
= n · ld n
⇒ Laufzeit: O(n · log n)
6) Rekursive Algorithmen, die ihre Eingabedaten jedesmal um 1 verringern, dabei aber alle
Daten betrachten müssen.
Tn = Tn−1 + n
Tn−1 + n
für n ≥ 2, T1 = 1
= Tn − 2 + n − 1 + n
= Tn−3 + n − 2 + n − 1 + n
..
.
= 1 + 2 + 3 + ... + n − 1 + n
n · (n + 1)
=
2
1 2 1
=
n + n
2
2
⇒ Laufzeit: O(n2 )
Beispiel: Multiplikation Matrix · Vektor
Allgemeiner: doppelt verschachtelte Schleifen
7) Dreifach verschachtelte Schleifen: O(n3 ), Beispiel: Multiplikation Matrix · Matrix.
√
n ld n (ld n)2
n
n · ld n
n2
10
3
9
3
30
100
100
6
36
10
600
10.000
1.000
9
81
31
9000 1.000.000
10.000
13
169
100
130.000
108
6
100.000
16
256
316 1, 6 · 10
1010
6
1.000.000
19
361 1.000
19 · 10
1012
Bei geteilten Problemen wird die Laufzeit wie folgt zusammengesetzt:
Ein Teil A mit der Laufzeit O(n) wird vor dem Teil B mit der Laufzeit O(n2 ) ausgeführt,
dann werden die Laufzeiten addiert:
O(n) + O(n2 ) = O(n2 )
Eine Schleife mit der Laufzeit O(n) wird ausgeführt; in dieser Schleife ein Teil mit der Laufzeit
O(n2 ), dann werden die Laufzeiten miteinander multipliziert:
O(n) · O(n2 ) = O(n3 )
46
3.6
KAPITEL 3. ALGORITHMEN UND DATENSTRUKTUREN
Spezifikation und Korrektheit von Algorithmen
Die Spezifikation eines Algorithmus (“Was macht dieser Algorithmus eigentlich?”) geschieht
durch Vor- und Nachbedingungen. Das sind wahre Aussagen über die Ein- bzw. Aus-Parameter
des Algorithmus.
Achtung: Die Vorbedingung ist vom Aufrufer zu erfüllen, die Nachbedingung muss vom Implementierer des Algorithmus erfüllt werden.
Beispiel: Lösung der quadratischen Gleichung ax2 + bx + c = 0
block QUGL(ein: a ∈ R, b ∈ R, c ∈ R; aus: x1 ∈ R, x2 ∈ R)
vor: a 6= 0 ∧ b2 ≥
4ac
√
√
b2 −4ac
b2 −4ac
∧ x2 = −b− 2a
nach: x1 = −b+ 2a
{. . .}
Bemerkung: Hier wurde davon ausgegangen, dass den Ein-Parametern innerhalb des Algorithmus keine neuen Werte zugewiesen werden. Im Allgemeinen (und insbesondere bei der
Verwendung von Mit-Parametern) kennzeichnet man Anfangswerte von Parametern durch
Unterstreichung.
Beispiel:
block wastutdas(ein: x ∈ Z; mit: y ∈ Z; aus: z ∈ Z)
vor: y ≥ 0;
nach: y = 0 ∧ z = x · y;
{. . .}



‘Spezifikation’
’Implementierung’
Ein Algorithmus heißt korrekt bezüglich seiner Spezifikation, wenn für jeden Aufruf, der die
Vorbedingungen erfüllt, nach dem Aufruf des Algorithmus die Nachbedingungen erfüllt sind.
3.6.1
Zusicherungen
Zum Nachweis der Korrektheit formuliert man an gewissen Stellen des Algorithmus Zusicherungen (wahre Aussagen mit den beteiligten Variablen), so dass folgende Schlusskette gilt:
P(V)
⇒ Z1 (V) c
⇒...c
⇒ Z n (V) ⇒
Q(V)
| {z }
|
{z
}
| {z }
Vorbedingung
Zusicherungen
Nachbedingung
Dabei sieht jeder Zusicherungsschritt Zn (V)c
⇒Zk+1 (V) so aus:
Zk (V) und Aktion zwischen Zk und Zk+1 ⇒ Zk+1 (V)
Beispiel: Minimum zweier Zahlen
3.6. SPEZIFIKATION UND KORREKTHEIT VON ALGORITHMEN
47
block min2(ein: a ∈ R, b ∈ R; aus: min ∈ R)
vor: wahr;
nach: (min = a ∨ min = b) ∧ (min ≤ a) ∧ (min ≤ b);
{
falls a < b dann {
!!Z1 : a < b;
min ← a;
!!Z2 : a < b ∧ min = a;
!!Z3 : min = a ∧ min ≤ a ∧ min ≤ b;
} sonst {
!!Z4 : b ≤ a;
min ← b;
!!Z5 : b ≤ a ∧ min = b;
!!Z6 : min = b ∧ min ≤ b ∧ min ≤ a;
}
!!Z7 : (min = a ∧ min ≤ a ∧ min ≤ b) ∨ (min = b ∧ min ≤ b ∧ min ≤ a);
!!Z8 : (min = a ∨ min = b) ∧ min ≤ b ∧ min ≤ a;
}
3.6.2
Schleifeninvariante
Schleifeninvarianten sind Zusicherungen in Schleifen, die beim Durchlaufen des Schleifenkörpers
erhalten bleiben. Es seien:
P(V) Zusicherung über Variablen vor Schleifeneintritt
Q(V) Zusicherung über Variablen nach Schleifenende
I(V) Schleifeninvariante
B(V) Wiederholbedingung der Schleife
A(V) Datentransformation durch den Schleifenkörper
Dann muss für die Invariante nachgewiesen werden:
1) P(V) ⇒ I(V), das heißt die Invariante muss bei Schleifeneintritt wahr sein.
2) (I(V) ∧ B(V)) ⇒ I(A(V)), das heißt die Invariante muss beim Schleifendurchlauf reproduziert werden.
3) (I(V) ∧¬ B(V)) ⇒ Q(V), das heißt beim Abbruch der Schleife muss die Zielaussage wahr
sein.
Schema “was gilt wo?”
48
KAPITEL 3. ALGORITHMEN UND DATENSTRUKTUREN
P(V)
I(V)
B(V)
w
(I(V) ∧ B(V))
f
(I(V) ∧¬B(V))
A
I(A(V))
Q(V)
Beispiel: Ganzzahlige Division mit Rest von z durch t soll Quotienten k und Rest r liefern.
block divmod(ein: z ∈ N0 , t ∈ N; aus: k ∈ N0 , r ∈ N0 )
vor: z ≥ 0 ∧ t > 0;
nach: z = k · t + r ∧ 0 ≤ r < t;
{
k ← 0;
r ← z;
solange r ≤ t wiederhole {
r ← r − t;
k ← k + 1;
}
}
P(V)
B(V)
Q(V)
probieren: I(V)
z ≥0∧t>0∧k =0∧r =z
r≥t
z = k · t + r ∧ 0 ≤ r ∧r < 1
|
{z
}
Das könnte I(V) sein (wegen I(V) ∧¬ B(V)) ⇒ Q(V))
z =k·t+r∧0≤r
1) P(V): (z ≤ 0 ∧ t > 0 ∧ k = 0 ∧ r = z) ⇒ (z = k · t + r ∧ 0 ≤ r): I(V)
2) I(V) ∧ B(V): z = k · t + r ∧ 0 ≤ r ∧ r ≥ t
z0 = z
t0 = t
I(A(V))
r0 = r − t
k0 = k + 1
⇒ z 0 = z = k · t + r = (k 0 − 1) · t0 + (r0 + t0 ) = k 0 · t0 + r0
und r0 = r − t ≥ t − t = 0 das heißt r0 ≥ 0.
Also: z 0 = k 0 · t0 + r0 ∧ r0 ≥ 0 : I(V0 )
A(V)
3) I(V) ∧¬ B(V): z = k · t + r ∧ 0 ≤ r ∧ r < t : Q(V)
3.7
Datentypen
Kontrollstrukturen: “Was wird wann gemacht?”
Datenstrukturen: “Womit wird etwas gemacht?”
3.7. DATENTYPEN
3.7.1
49
Taxonomie der Datentypen
Datentypen
Idealisierte
Datentypen
Einfache
Datentypen
Ordinale
Datentypen
Boolean
Konkrete
Datentypen
Abstrakte
Datentypen
Strukturierte
Datentypen
Zeiger
Datentypen
Fließkomma Feld / ArrayBund / Record
Datentypen Datentypen Datentypen
Aufzählungstypen
Integer Character
Idealisierte Datentypen sind die aus der Mathematik bekannten Mengen N, Z, R, C, usw. mit
ihren Operatoren. √
Sie sind in endlichen Maschinen nicht, bzw. nur symbolisch darstellbar.
Beispiel: Die Zahl 2 hat keine endliche Dezimal- bzw. Binärrepräsentation. Sie kann aber in
einem Computer-Algebrasystem als zum Beispiel 2ˆ( 21 ) dargestellt werden. Beachte: 1.41421356
√
ist nicht 2! (Es handelt sich hierbei nur um eine Näherung!)
Konkrete Datentypen sind die von einem Rechensystem (in Hard- und Software) bereitgestellten Datentypen. Das sind im Allgemeinen eine Reihe von vordefinierten Datentypen sowie
die Möglichkeit neue Datentypen vom Benutzer (das heißt vom Programmierer) zu definieren
(durch so genannte Datentypkonstruktoren).
Beispiel: Fest eingebaute Typen: integer. Benutzerdefinierter Typ: feld[0. . .15] von integer.
Abstrakte Datentypen (ADTs) verbergen ihren inneren Aufbau vor dem Benutzer. Eine Instanz
eines ADTs (‘Objekt’) besteht aus Instanzen von herkömmlichen Datentypen oder Datenstrukturen und ADTs sowie Funktionen bzw. Prozeduren, die auf diesen operieren. Nach außen ist
im Allgemeinen nur ein Teil der Funktionen sichtbar: das so genannte ‘Interface’. Realisierung
von ADTs geschieht durch Klassen in objekt-orientierten Programmiersprachen.
Beispiel: Stapelspeicher (‘Stack’)
Nach außen sichtbar: push(), pop(), top()
im Inneren: mit Feld, Stapelzeiger ist dann der aktuelle Feld-Index.
push()
sp
17
50
42
pop()
top()
Implementierung
Interface
50
KAPITEL 3. ALGORITHMEN UND DATENSTRUKTUREN
3.7.2
Einfache Datentypen
a) Boolean: dient zur Darstellung von Wahrheitswerten; zwei mögliche Werte (wahr und
falsch); Operationen: und, oder, nicht, Vergleiche.
b) Integer: dienen zur Darstellung ganzer Zahlen mit und ohne Vorzeichen, typischerweise
mit unterschiedlichem Darstellungsbereich; Operationen: Grundrechenarten und Vergleiche.
c) Character: dienen zur Darstellung des Zeichensatzes eines Rechners; Operationen: Vergleiche, Umwandlung Character ↔ Integer.
d) Aufzählungstypen: dienen zur Kodierung einer kleinen Menge von Zuständen, die vom
Benutzer explizit benannt werden; Operationen: Vergleiche, Umwandlung Aufzählungstyp ↔ Integer.
Beispiel: (Blau, Rot, Gelb)
Das passiert üblicherweise mit der Vergabe eines Namens für diesen Typ:
typ Farbe=(Blau, Rot, Gelb);
Eine Variable von diesem Typ wird deklariert mit:
lokal: f ∈ Farbe;
e) Fließkommadatentypen dienen zur näherungsweisen Darstellung reeller Größen; unterscheiden sich durch den Darstellungsbereich und die Darstellungsgenauigkeit; Operationen: Grundrechenarten, Vergleiche
3.7.3
Strukturierte Datentypen
a) Felder sind Aggregationen von Daten des gleichen Typs (des ‘Basistyps’). Die Auswahl
eines Datenelements geschieht durch einen ganzzahligen Index.
Beispiel: typ Vektor = feld[1. . .10] von integer;
typ Matrix = feld[1. . .3] von feld[1. . .3] von real;
Achtung: Vektor und Matrix sind Typnamen, nicht Variablennamen!
Variablen dieses Types müssen erst deklariert werden, zum Beispiel:
lokal: m ∈ Matrix, v ∈ Vektor;
Zugriff auf Feldelemente durch zum Beispiel:
v[8] ← 42;
v[i + 1] ← v[i];
m[2][3] ← m[v[8] − 40][i + 2];
Achtung: Der Zugriff auf ein Feldelement außerhalb des deklarierten Indexbereiches ist
ein fataler Programmierfehler, den der Compiler im Allgemeinen nicht erkennen kann!
b) Verbunde sind Aggregationen von Daten, möglicherweise unterschiedlicher Typen (der
‘Komponententyp’). Die Auswahl einer Komponente geschieht durch Angabe des Komponentennamens.
Beispiel: der Deklaration eines Verbundes:
typ Datum = verbund{
tag ∈ integer;
monat ∈ integer;
jahr ∈ integer;
};
3.7. DATENTYPEN
51
Deklaration einer Variable zum Beispiel:
lokal: d ∈ Datum;
Zugriff auf zum Beispiel den Monat:
d.monat ← 6;
c) Typisch ist eine geschachtelte Definition von Datentypen:
typ String = feld[1 . . . 10] von char;
Datum = . . . (wie oben);
Adresse = verbund {
strasse, ort ∈ String;
hausnr, plz ∈ integer;
}
Person = verbund {
name, vorname ∈ String;
geburtstag ∈ Datum;
wohnort ∈ Adresse;
}
Kartei = feld[1 . . . 1000] von Person;
Deklaration einer Variable zum Beispiel:
lokal: kartei ∈ Kartei;
Zugriff auf den Monat des Geburtsdatums der 42. Person:
m ← kartei[42] .geburtstag .monat;
| {z }
Person
|
{z
}
Datum
|
{z
}
Integer
Zugriff auf den ersten Buchstaben des Nachnamens:
c ← kartei[42] .name[1];
| {z }
Person
|
{z
}
String
|
{z
Character
}
Bemerkung: Groß- und Kleinschreibung von Namen erfolgt nach dem Schema:
Typen → Großschreibung
Variablen und Komponenten → Kleinschreibung
3.7.4
Zeiger-Datentypen
Definition: Ein Zeigervariable ist eine Variable, die als Wert die Adresse einer anderen Variablen eines bestimmten Typs (des ‘Basistypes’) enthält.
Beispiel:
..
.
x
(Typ: Zeiger auf real)
Deklaration: x ∈ˆreal;
y
(Typ: real)
52
KAPITEL 3. ALGORITHMEN UND DATENSTRUKTUREN
Operationen:
a) erzeugen eines Zeigers (‘Referenzierung’)
Beispiel: x ←ˆy;
b) zugriff auf die verwiesenen Variable (‘Dereferenzierung’)
Beispiel: xˆ ← 2 · xˆ + 3;
Beachte Eine Zuweisung an xˆ (das heißt xˆ← . . .) überschreibt den verwiesenen Wert: eine
Zuweisung an x (das heißt x ← . . .) überschreibt den Zeigerwert!
Beispiel:
4.4
3.3
x
y
4.4
5.5
x ←ˆy;
xˆ← 4.4;
x ←ˆz;
xˆ← 4.4;
z
Bemerkung: Die Menge der Werte einer Zeigervariablen enthält einen speziellen Zeiger (‘nil’),
der garantiert auf kein gültiges Datenobjekt zeigt. Darstellung:
x
1. Anwendungsbeispiel: Decodierung eines Huffman-Codes
Dekodierbaum:
1
0
c
0
0
a
Datensruktur:
1
d
1
b
3.7. DATENTYPEN
53
wurzel
*
*
c
*
a
d
b
typ Bit=(0,1);
Knoten=verbund{
links ∈ˆKnoten;
rechts ∈ˆKnoten;
symbol ∈ char;
}
block decode(ein: wurzel ∈ˆKnoten, code ∈ feld[1 . . . 100] von Bit, nbits ∈ integer; aus: s ∈ char)
lokal: i ∈ integer, p ∈ˆKnoten;
{
p ← wurzel;
falls p = nil dann Abbruch(“Aufruf fehlerhaft: kein Baum!”);
für i ← 1 solange i ≤ nbits mit i ← i + 1 wiederhole{
falls code[i] = 0 dann {
p ← pˆ.links;
}sonst{
p ← pˆ.rechts;
}
falls p = nil dann Abbruch(“Code nicht decodierbar!”);
}
s ← pˆ.symbol;
falls s =‘*’ dann Abbruch(“Code nicht decodierbar!”);
}
2. Anwendungsbeispiel (Rekursion in Algorithmen und Daten): Arithmetische Ausdrücke und
Grundrechenarten. Diese Ausdrücke lassen sich als Binärbäume schreiben.
Beispiel: (1+2)*(7+4*5)=((1+2)*(7+(4*5))) (“vollständig geklammerter Ausdruck”, siehe A.7,
Seite 65)
54
KAPITEL 3. ALGORITHMEN UND DATENSTRUKTUREN
*
+
+
1
2
7
*
4
5
Ein innerer Knoten enthält folgende Informationen: Operation, linker Teilbaum, rechter Teilbaum.
Ein Blatt enthält folgende Information: Zahlenwert.
Bei der Bearbeitung eines Knotens müssen wir wissen, ob es sich um einen inneren Knoten
oder um ein Blatt handelt: ein ‘Flag’ hierfür ist notwendig!
typ Knoten = verbund{
blatt ∈ boolean;
op ∈ char;
links ∈ˆKnoten;
rechts ∈ˆKnoten;
zahl ∈ float;
};
Beispiel:
//wahr ⇔ Knoten ist Blatt
//Opperationen +,-+*,/
//linker Teilbaum
//rechter Teilbaum
//fals blatt = wahr
Ausdruck = ˆKnoten;
3.7. DATENTYPEN
55
wurzel
f
*
?
f
+
f
+
?
?
w
?
?
?
1
w
?
?
?
2
f
*
w
?
?
?
7
?
w
?
?
?
4
w
?
?
?
5
Aufgabe: Gegeben sei ein solcher Baum. Es soll die vollständig geklammerte Form des Ausdrucks ausgegeben werden.
block AusdrAusg(ein: p ∈ Ausdruck)
{
falls pˆ.blatt dann {
Bildschirmausgabe(pˆ.zahl);
} sonst {
Bildschirmausgabe(“(”);
AusdrAusg(pˆ.links);
Bildschirmausgabe(pˆ.op);
AusdrAusg(pˆ.rechts);
Bildschirmausgabe(“)”);
}
}
Bemerkung: Beachte strukturelle Ähnlichkeit mit den “Türmen von Hanoi”!
3.7.5
Zur Lebensdauer von Daten
a) lokale Variablen (innerhalb von Blöcken definiert)
Es git eine Instanz jedes Datums pro Aktivierung des entsprechenden Blocks. Die Le-
56
KAPITEL 3. ALGORITHMEN UND DATENSTRUKTUREN
bensdauer (das heißt die Zeit, in der der Speicherplatz zur Verfügung steht) ist an die
Lebensdauer der Aktivierung gekoppelt: Wenn der Block verlassen wird, wird die Aktivierung beendet und der Speicherplatz der lokalen Daten automatisch zurückgegeben.
Die Daten ‘leben’ auf dem Stack.
Beispiel:
block binomkoeff(ein: n ∈ integer, k ∈ integer; aus: nük ∈ integer)
lokal: erg ∈ integer;
{
falls k = 0 dann nük ← 1 sonst {
binomkoeff(n, k − 1, erg);
nük ← (erg · (n − k + 1)) /k;
}
}
block main()
lokal: total ∈ integer;
{
binomkoeff(5, 2, total);
}
Hierfür sieht der Stack wie folgt aus (wobei der Stack nach oben wächst):
binomkoeff
binomkoeff
binomkoeff
n
k
nük
erg
n
k
nük
erg
n
k
nük
erg
:5
:0
:
:?
:5
:1
:
:1
:5
:2
:
:5
main total :10
b) globale Daten (außerhalb aller Blöcke definiert)
Es gibt genau eine Instanz jedes Datums. Die Lebensdauer ist die gesamte Programmlaufzeit. Die Daten ‘leben’ im so genannten “statischem Datensegment”.
Beispiel:
3.7. DATENTYPEN
57
global: ventilIstAuf ∈ boolean;
block öffneVentil()
{
..
.
ventilIstAuf ← wahr;
..
.
}
Bemerkung: Üblicherweise erlauben die Programmiersprachen zusammen mit den Definitionen eine Initialisierung globaler Daten, zum Beispiel in C: int glob = 5;
c) Die Möglichkeiten von a) und b) reichen nicht aus, wenn zur Laufzeit eine vorher unbekannte Menge Daten in eine Datenstruktur aufgenommen werden soll. Daher gibt
es zusätzlich die ‘Halde’(‘Heap’): Von dort kann der Programmierer explizit Speicherplatz anfordern, dessen Lebenszeit er selbst bestimmt; das heißt dieser Speicherplatz
muss auch explizit an die Halde zurückgegeben werden. Dazu dienen bei uns ‘new’ und
‘dispose’.
Beispiel: Erzeugen eines Blatts in einem Baum für arithmetische Ausdrücke.
block leseBlatt(aus erg ∈ Ausdruck)
{
new(erg); //Erzeugt Knoten und lässt erg darauf zeigen
ergˆ.blatt ← wahr;
Tastatureingabe(ergˆ.zahl);
}
Bemerkung:
(a) die verwiesene Variable ergˆ hat selbst keinen Namen und es kann nur über den
Zeiger auf sie zugegriffen werden.
(b) dispose(erg) gibt den Knoten an die Halde zurück. Danach ist erg ungültig und
darf nicht mehr derefferenziert werden!!!
(c) Typische Anordnung von Programm, Stack und Heap im Adressraum des ‘Prozesses’ (= Programm in Ausführung):
58
KAPITEL 3. ALGORITHMEN UND DATENSTRUKTUREN
Max
Stack
Heap
Statische Daten
Programm-Code
0
Kapitel 4
Programmiersprachen
Prgrammiersprachen dienen zur Formulierung von Algorithmen, so dass diese von einer Maschine abgearbeitet werden können.
4.1
Syntax vs. Semantik
Syntax einer Programmiersprache
Semantik einer Programmiersprache
: deren formaler Aufbau
: was bezeichnen bzw. bewirken die Konstrukte
einer Programmiersprache.
Beispiel: Zuweisung
Syntax
: linke Seite ← rechte Seite;
Semantik : Die rechte Seite wird ausgewertet und der Wert an der Speicherstelle abgelegt, die die linke Seite bezeichnet.
Beachte:
a) Die Semantik wird hier nur informal angegeben. Wir haben ‘Flussdiagramme’ und ‘Zusicherungen’ benutzt.
b) Fehlerhafte Programme, die auf Missverständnissen der Semantik einer Programmiersprache beruhen, sind schwer zu korrigieren.
Beispiel: Welche Bedeutung hat
if(g(a++, a) && h(a, --a))...
(in C/C++) genau? (Das ist gaaanz falsch! Man weiss nicht, was in welcher Reihenfolge
ausgeführt wird.)
4.2
Lexikalische Struktur
Hierunter versteht man die Zusammensetzung der Grundsymbole einer Sprache (‘Token’) aus
Einzelzeichen.
Beispiel:
59
60
KAPITEL 4. PROGRAMMIERSPRACHEN
a) Zahlen bestehen aus beliebig vielen (aber mindestens einer) Ziffer(n).
b) Bezeichner fangen mit einem Buchstaben an, und enthalten nur Buchstaben und Ziffern.
c) Der Operator “kleiner oder gleich” wird aus den Zeichen ‘<’ und ‘=’ gebildet, die ohne
Zwischenraum hintereinander geschrieben werden.
Formale Beschreibung durch “Reguläre Ausdrücke”:
ZIFFER
ZAHL
BUCHSTABE
BEZEICHNER
KLGLEICH
=
=
=
=
=
[‘0’ - ‘9’]
{ZIFFER}+
[‘A’ - ‘Z’ ‘a’ - ‘z’]
{BUCHSTABE}({ZIFFER} | {BUCHSTABE})*
‘<’‘=’
Bemerkung:
• [‘0’ - ‘9’] ist kurz für ‘0’ | ‘1’ | ‘2’ | . . .
• {x} wird ersetzt durch die Definition von x
• + bedeutet beliebig oft, aber mindestens einmal
• * bedeutet beliebig oft, auch keinmal
Lexikalische Analyse eines Programms durch “Endliche Automaten”.
Ziffer
Buchstabe
Start
2
1
Buchstabe
3
anderes Zeichen
(als Ziffer oder Buchstabe)
kein Buchstabe
4.3
Syntaktische Struktur
Hierunter versteht man die Zusammensetzung von Grundsymbolen der Sprache zu Ausdrücken,
Anweisungen und letztlich zu einem vollständigem Program.
Beispiel:
a) Ein Ausdruck besteht aus einem Ausdruck gefolgt von einem Operator, gefolgt von einem
Ausdruck; oder aber einer einzelnen Zahl.
b) Eine abweisende Schleife besteht aus dem Schlüsselwort ‘solange’, einem boolschen Ausdruck, dem Schlüsselwort ‘wiederhole’ und einer Anweisung.
Formale Beschreibung durch “Kontextfreie Grammatik”.
4.4. SYNTAX-DIAGRAMME
61
exp : (exp op exp) | ZAHL ;
op : PLUS | MINUS | . . . | KLGLEICH | . . . ;
abw. Schleife : SOLANGE exp WIEDERHOLE anweisung;
Bemerkung:
exp Expression (Ausdruck)
op
Operator
:
wird gelesen als “besteht aus”
|
oder
;
Ende
Syntaktische Analyse eines Programms geschieht durch den sogenannten ‘Parser’ (=endlicher
Automat mit Stack, ein sogenannter ‘Kellerautomat’).
Bemerkung: “Reguläre Ausdrücke” sind die Teilmenge der “Kontextfreien Grammatik”. Damit
ist die lexikalische Analyse ein Spezialfall der syntaktischen Analyse. Die Aufteilung geschieht
aus praktischen Erwägungen.
4.4
Syntax-Diagramme
Sowohl lexikalische als auch syntaktische Struktur einer Programmiersprache lassen sich durch
sogenannte “Syntax-Diagramme” darstellen:
exp
exp
op
exp
ZAHL
op
+
..
.
¡=
abw. Schleife
SOLANGE
zahl
exp
ziffer
WIEDERHOLE
ziffer
0
1
..
.
9
anweisung
62
KAPITEL 4. PROGRAMMIERSPRACHEN
Anhang A
Einschübe
A.1
Kreuzprodukt von Mengen
Seien A und B endliche Mengen:
A = {a1 , a2 , ..., an }
B = {b1 , b2 , ..., bn }
Dann ist das Kreuzprodukt A ⊗ B die Menge aller geordneten Paare (a, b) bei dem a ∈ A und
b ∈ B ist.
A ⊗ B = {(a, b)|a ∈ A ∧ b ∈ B}
Beispiel:
A = {1, 2, 3}
B = {x, y}
A ⊗ B = {(1, x), (1, y), (2, x), (2, y), (3, x), (3, y)}
Bemerkung:
a) |A ⊗ B| = |A| · |B|
b) Ist (A ⊗ B) ⊗ C = A ⊗ (B ⊗ C)?
Ja, weil ((a, b), c) = (a, (b, c)) = (a, b, c).
A.2
‘Kardinalität’ einer Menge
Die ‘Kardinalität’ einer Menge ist die Anzahl der Elemente der Menge.
A.3
n-Tupel über einer Menge
Ein n-Tupel aus N0 ist eine geordnete Menge von Elementen aus N0 .
(K1 , K2 , . . . , Kn ) 6= (K2 , K1 , . . . , Kn )K1 ∈ N0 , K2 ∈ N0 , . . . , Kn ∈ N0
63
64
ANHANG A. EINSCHÜBE
Die Menge aller n-Tupel über N0 :{(K1 , K2 , . . . , Kn )|K1 ∈ N0 , K2 ∈ N0 , . . . , Kn ∈ N0 } =
N0 ⊗ N0 ⊗ . . . ⊗ N0
|
{z
}
n−mal
A.4
Logarithmen
Logarithmen sind die Umkehrung der Exponentation.
Sie kennen ‘Zehnerlogarithmen’:
y = 10x ⇔ log1 0y = log1 010x = x
Entsprechend ‘Zweierlogarithmen’:
y = 2x ⇔ log2 y = log2 2x = x
Für log2 schreibt man ld (sprich: “Logarithmus dualis”)
Sei y1 = 2x1 und y2 = 2x2 , d. h. ldy1 = x1 und ldy2 = x2 . Dann ist y1 · y2 = 2x1 · 2x2 = 2x1 +x2 ,
d.h. ld(y1 · y2 ) = x1 + x2 =ldy1 +ldy2
y1
y2
=
2x1
2x2
= 2x1 −x2 , d.h. ld
y1
y2
= ld 2x1 −x2 = x1 − x2 = ld y1 − ldy2
Nun sei n ∈ N. Dann ist y n = (2x )n = 2n·x , d.h. ldy n = n · x = n·ldy.
A.5
Addition im Dualsystem
Ziffern 0,1 ⇒ Folgende Kombinationen sind möglich:
0+0
0+1
1+0
1+1
=
=
=
=
0
1
1
10 (d.h. Summe 0, Übertrag 1)
⇒ Erkenntnis: Der Übertrag von der vorherigen Stelle ist ebenfalls zu berücksichtigen! Deshalb
gibt es 8 Fälle:
yi xi üi üi+1 Si
0
0
0
0
0
Beispiel:
0
0
1
0
1
Dual Dezimal
0
1
0
0
1
0101101
45
0
1
1
1
0
+
0011001
+
25
1
0
0
0
1
1000110
70
1
0
1
1
0
1
1
0
1
0
1
1
1
1
1
A.6
Wie groß ist fib(n)?
Ansatz:
fib (n) = a · bn
A.7. PRIORITÄT, ASSOZIATIVITÄT UND REIHENFOLGE
fib (n − 1) = a · bn−1
fib (n − 2) = a · bn−2
a · bn = a · bn−1 + a · bn−2
b2 = b + 1
65
| : (abn−2 )
Bemerkung: Das ist die Gleichung für den “Goldenen Schnitt”
b
1
1
b
=
⇒ b2 = b + 1
b
b+1
⇒ p1/2 =
1
2
±
1
2
√
5
Wenn abn1 und abn2 beides Lösungen des Problems sind, dann ist es die Linearkombination
αabn1 + βabn2 auch:
αabn1
+ αabn2
n
a(αb1 + βbn2 )
αabn−1
1
αabn−1
2
a(αbn−1
+
βbn−1
)
1
2
=
=
=
+
+
+
βabn−2
1
βabn−2
2
a(αbn−2
+
βbn−2
)
1
2
Deshalb ist
n
n
1 1√
1 1√
+
5
+B·
−
5
2 2
2 2
= A+B
1 1√
1 1√
= A·
+
5 +B·
−
5
2 2
2 2
fib (n)
fib (0)
fib (1)
= A·
A und B werden aus den Anfangsbedingungen fib(0) = 0 und fib(1) = 1 bestimmt.
⇒ A = √15 , B = − √15
Definition:
1 1 √
+ · 5
≈ 1, 618 . . . “Goldener Schnitt”
2 2
1 1 √
Φ−1 = − + · 5
2 2
1
1 1 √
= −
− · 5 =
Φ
2 2
n
1
1
−1
1
fib (n) = √ · Φn − √ ·
≈ √ · Φn
Φ
5
5
5
Φ
A.7
=
Priorität, Assoziativität und Reihenfolge
a) 2+3*4 soll ausgewertet werden wie (2+(3*4): ‘*’ hat Priorität über ‘+’
b) 4-1-2 soll ausgewertet werden wie (4-1)-2: ‘-’ ist linksassoziativ
c) Die Auswertereihenfolge hat hiermit nichts zu tun!
Im Ausdruck (f(3)+g(4))*h(5) kann jede der drei Funktionen als erste, zweite oder dritte
aufgerufen werden. Das kann fatal sein, falls die Funktion Seiteneffekte (Zuweisungen an
globale Variablen, Ausgaben auf den Bildschirm, Aktionen in der Realität) haben!
66
ANHANG A. EINSCHÜBE
Anhang B
Übungen
B.1
1. Übung
1) Es seien A = {a, b, c} und B = {2, 4, 6, 8}.
a) Zählen Sie die Elemente aus A ⊗ B auf!
b) Wie viele Elemente hat An ⊗ B m ?
c) Beschreiben Sie die Elemente von A ⊗ B 2 ⊗ A! Wie viele gibt es?
2) Wenn beim Zahlenrätsel die maximal zu erratende Zahl n keine Potenz von 2 ist, dann
ist ldn keine ganze Zahl. Wie groß ist dann die Zahl der Fragen, die maximal nötig ist,
um die Zahl du erraten?
Behandeln Sie das Beispiel n = 10.
3) Berechnen Sie:
a) log10 1000
d) log5 25
g) ld 1024 + ld 32
b) log10 1016
e) log7 49−2
h) ld (512 + 512)
c) log10 10−16
1
f) ld 128
4) Eine Nachrichtenquelle sendet Zeichen aus dem Alphabet X = {a, b, c, d} mit den Wahrscheinlichkeiten p(a) = p(b) = 81 , p(c) = 12 .
a) Wie groß ist p(d)?
b) Berechnen Sie die Entropie der Quelle!
c) Wie groß ist der Informationsgehalt der Nachricht “acca”?
d) Wie groß ist der mittlere Informationsgehalt einer Nachricht aus 1000 Zeichen?
e) Angenommen, alle Zeichen wären gleich wahrscheinlich. Wie groß ist dann der
Informationsgehalt einer Nachricht aus 1000 Zeichen? Schlussfolgerung?
5) Angenommen, Ihr neuer Computer besitzt einen Hauptspeicher von 512 MByte.
a) Wie viele Bytes, wie viele Bits sind das?
b) Welche Informationsmenge ist nötig, um ein Byte aus dem Hauptspeicher auszuwählen? (Das ist die sog. ‘Adressbreite’.)
67
68
ANHANG B. ÜBUNGEN
6)
a) Beschreiben Sie einen Algorithmus zum Berechnen des Rauminhaltes und der Oberfläche eines Quaders.
b) Beschreiben Sie einen Algorithmus zum Berechnen der Summe von zwei mehrstelligen Dezimalzahlen.
c) Vergleichen Sie die zu a) und b) benötigten Darstellungsmittel!
B.2
2. Übung
1) Stellen Sie die folgenden Zahlen im Dezimalsystem dar:
202(5) 33(7) 3AB(16) 202(8)
2) Stellen Sie die folgenden Dezimalzahlen im Dual-, Oktal- und Hexadezimalsystem dar:
0, 1, 9, 10, 22, 64, 255, 4321
3) Nehmen Sie an, die Hexadezimalzahl 1234(16) repräsentiert 16 Schalter. Wie viele der
Schalter sind in Stellung ‘an’ ?
4) Wie lautet die Folge von Nybbles, um den druckbaren Text ‘Affe’ darzustellen?
5)
a) Wenn man 14 Bits zur Darstellung von vorzeichenlosen Zahlen benutzt, was ist die
kleinste, was ist die größte darstellbare Zahl, ausgedrückt sowohl im Hexadezimalsystem als auch im Dezimalsystem?
b) Wie a), aber jetzt zur Darstellung von vorzeichenbehafteten Zahlen in Zweierkomplementdarstellung!
6) Geben Sie die Hexadezimaldarstellung von -55 an, wenn 10 Bits zur Verfügung stehen!
7) Geben Sie die Hexadezimaldarstellung der Dezimalzahl 205.125 an, wenn vier Bytes in
“2.2”-Festkommadarstellung benutzt werden sollen.
8) Welche Schwierigkeit tritt beim Multiplizieren und Dividieren von Festkommazahlen
auf?
9) Stellen Sie die folgenden Dezimalzahlen im “short real”-Format für Fließkommazahlen
dar:
a) 2.345 · 105 b) −1.111 · 10−11
c) 33 · 1022
d) −33 · 1022
e) 33 · 10−22
f) −33 · 10−22
10) Welche Dezimaldarstellung hat die “short real”-Zahl B7AAF F EE?
Welche Dezimaldarstellung hat die “short real”-Zahl EEF F AAB7?
B.3
3. Übung
1) Das folgende Alphabet mit 8 Symbolen und zugeordneten Häufigkeiten wurde entworfen,
um Rock-Songs aus den Fünfzigern effizient zu kodieren:
2
16
A
NA
37
37
1
3
BOOM 37 SHA
37
2
10
GET
YIP
37
37
2
1
JOB
WAH 37
37
B.4. 4. ÜBUNG
69
Konstruieren Sie eine entsprechenden Huffman-Code und benutzen Sie diesen, um die
folgende Nachricht zu codieren:
Get a job
Sha na na na na na na na na
Get a job
Sha na na na na na na na na
Wha yip yip yip yip yip yip yip yip
Sha boom
Wie viele Bits werden benötigt? Was wäre die entsprechende Zahl bei einem Code fester
Länge?
2) Für alle folgenden Aufgabenteile sollen die Symbole einer Nachrichtenquelle durch je
4 Bits codiert sein. Konstruieren Sie Fehlererkennungs- bzw. Fehlerkorrekturcodes für
folgende Fälle:
a) Fehlererkennung aller 1-Bit-Fehler
b) Fehlerkorrektur aller 1-Bit-Fehler
c) Fehlererkennung aller 2-Bit-Fehler (einschließlich aller 1-Bit-Fehler)
Geben Sie zu jedem Aufgabenteil den Code, seine Hamming-Distanz und je ein Beispiel
für eine erfolgreiche bzw. erfolglose Erkennung bzw. Korrektur eines fehlerhaft übertragenen Codewortes an. Versuchen Sie, mit so wenig wie möglichen Bits auszukommen!
B.4
4. Übung
1) Entwickeln Sie einen Block
SuccPrim(ein: P rim ∈ N; aus: N P rim ∈ N)
der zu einer gegebenen Primzahl Prim die nächstgrößere Primzahl NPrim ermittelt.
2) Entwerfen Sie einen Block
ggT3(ein: u ∈ N, v ∈ N, w ∈ N; aus: g ∈ N)
der den größten gemeinsamen Teiler g von drei Zahlen u,v und w berechnet.
Hinweis: Entwickeln Sie Ihr Verständnis des Rechenprozesses am Beispiel u = 30, v =
105, w = 70.
3) Entwickeln Sie einen Block, der mittels einer Schleife die folgendermaßen definierte Funktion f für ein übergebenes Argument n berechnet:
1
n=1
f (n) =
f (ndiv2) + 1 n > 1
Dabei ist die Operation ‘div’ die ganzzahlige Division(mit Rest). Was berechnet die
Funktion f?
4) Die Binomenalkoeffizienten nk , k = 0 . . . n, sind als Koeffizienten der Expansion
n
(a + b) =
n X
n
k=0
k
ak bn−k
70
ANHANG B. ÜBUNGEN
ganzzahlig. Das sieht man ihrer Definition
n · (n − 1) · . . . · (n − k + 1)
n
n!
=
=
k
k!(n − k)!
1 · 2 · ... · k
nicht an. Die Berechnung von n · (n − 1) · . . . · (n − k + 1) führtaußerdem
schnell zuneinem
n
n
Überlauf. Entwickeln Sie einen
Algorithmus,
der
gemäß
→
0
1 → . . . → k den
Binomenalkoeffizienten nk bei gegebenem n und k berechnet, nur ganzzahlig rechnet
und die o.g. Schwierigkeiten vermeidet!
5) In dieser Aufgabe soll bn , b ∈ N, n ∈ N0 , auf zwei Arten berechnet werden:
a) Naiv: durch fortgesetztes Multiplizieren. Benutzen Sie die Identität
n
ab =
a
(ab)bn−1
n=0
n>0
und starten Sie mit a = 1.
b) Effizient: abwechselnd quadrieren und multiplizieren.

 a
n
a(b2 ) 2
abn =

n−1
(ab)b
B.5
n=0
n > 0, n gerade
n > 0, n ungerade
5. Übung
1) Schreiben Sie einen Block, der die Aufgabe 3) der 4. Übung rekursiv löst. Wie groß ist
die maximale Rekursionstiefe?
2) Entwerfen Sie einen Block, der die Aufgabe 4) der 4. Übung rekursiv löst. Wie groß ist
die maximale Rekursionstiefe?
3) Geben Sie rekursive Lösungen für die Aufgabenstellungen 5a) und 5b) der Übung 4 an!
Vergleichen Sie für alle vier Lösungen den Rechenzeitbedarf und den Speicherplatzbedarf!
Hinweise:
a)
n
b =
1
b · bn−1
n=0
n>0
b)

 1
n
(b2 ) 2
bn =

n−1
b·b
n=0
n > 0, n gerade
n > 0, n ungerade
c) Benutzen Sie zur Aufwandsabschätzung die O-Notation in Abhängigkeit vom Exponenten n!
B.6. 6. ÜBUNG
71
4) Wir ändern den Rumpf des Block ‘Lineal’ aus der Vorlesung so, dass er nun lautet:
mitte ← (links + rechts)/2;
Lineal(links, mitte, höhe − 1);
Lineal(mitte, rechts, höhe − 1);
Zeichne(mitte, höhe);
Geben Sie für den Aufruf “Lineal(0, 8, 3)” die Reihenfolge der Blockaktivierungen und
die Reihenfolge der gezeichneten Markierungen an! Wie groß ist die maximale Rekursionstiefe?
5) Geben Sie für die “Türme von Hanoi” den Aufrufbaum für den Aufruf
Hanoi(3, 1, 2, 3)
an. Markieren Sie den Durchlauf durch den Baum, der bei der Abarbeitung des Blocks
genommen wird und geben Sie dabei die Ausgabe des Programms an!
Wie groß sit der Rechenzeitbedarf?
Wie groß ist der Speicherplatzbedarf?
B.6
6. Übung
1) Geben Sie für das folgende Programmstück eine sinnvolle Nachbedingung Q an:
!!P: a > 0 ∧ b = 0;
solange a 6= b wiederhole {
a ← a + b;
b ← b + a;
}
!!Q: . . .;
2) Weisen Sie die Korrektheit der folgenden Programmteile nach:
a)
!!P: x > y
y ← x − y;
y ← x + y;
!!Q: x < y;
b)
!!P: wahr;
falls x < 0 dann {
y ← −x;
} sonst {
y ← x;
}
!!Q: y = |x|;
c)
!!P: y ≥ 0;
solange y 6= 0 wiederhole {
y ← y − 1;
}
!!Q: y = 0;
72
ANHANG B. ÜBUNGEN
d)
!!P: y + z = 10;
solange y 6= 0 wiederhole {
z ← z + 1;
y ← y − 1;
}
!!Q: z = 10 ∧ y = 0;
3) Weisen Sie die Korrektheit Ihrer Lösungen der Aufgaben 5a) und 5b) der 4. Übung nach!
4) Entwerfen Sie einen Block
block FindeGröße(ein: a ∈ feld[1 . . . n] von integer; aus: g ∈ integer, m ∈ boolean)
der aus einem Feld a von Ganzzahlen die größte herausfindet und zurückgibt. Gleichzeitig soll über das Flag m signalisiert werden,ob dieser größte Wert mehr als einmal im
Feld vorkam.
B.7
7. Übung
1) Nehmen Sie an, dass arithmetische Ausdrücke über Zahlen und den Grundrechenarten
als Binärbäume dargestellt werden. Schreiben Sie einen Block, der einen solchen Baum
als ‘Ein-Parameter’ akzeptiert und in einem ‘Aus-Parameter’ den numerischen Wert des
Ausdrucks zurückliefert.
2) Erweitern Sie die Darstellung und die Auswertung arithmetischer Ausdrücke um
a) die binäre Exponentation, das heißt Ausdrücke der Form e1 ˆe2 , die ee12 darstellen
sollen.
b) das unäre Minus, das heißt Ausdrücke der Form −e1 , die e1 negieren sollen.
3) Entwerfen Sie einen Block, der das Einlesen eines vollständig geklammerten arithmetischen Ausdrucks über Zahlen und den Grundrechenarten von der Tastatur erlaubt und
diesen in einem Binärbaum ablegt. Nehmen Sie dazu die Existenz eines Blocks ‘NächstesZeichen’ an, der beim Aufruf NächstesZeichen(c); das nächste von der Tastatur gelesene
Zeichen in der Variablen c (vom Typ char) ablegt.
4) Zeichnen Sie Syntay-Diagramme für alle Konstrukte unseres Pseudocodes. Geben Sie zu
jedem Diagramm ein Beispiel für ein korrektes Stück Code.
Anhang C
Lösungen
C.1
1. Übung
1) A = {a, b, c} , B = {2, 4, 6, 8}
a) A ⊗B = {(a, 2), (a, 4), (a, 6), (a, 8), (b, 2), (b, 4), (b, 6), (b, 8), (c, 2), (c, 4), (c, 6), (c, 8)}
b) |An ⊗ B m | = (|A| · |A| · . . . · |A|) · (|B| · |B| · . . . · |B|) = |A|n · |B|m = 3n · 4m
An ⊗ B m = (A ⊗ A ⊗ . . . ⊗ A) ⊗ (B ⊗ B ⊗ . . . ⊗ B )
|
{z
}
|
{z
}
n−mal
2
m−mal
2
c) A ⊗ B ⊗ A ⇒ |A| · |B | · |A| = 3 · 16 · 3 = 144
A ⊗ B 2 ⊗ A = {(x, y, z, w)|x ∈ A ∧ y ∈ B ∧ z ∈ B ∧ w ∈ A}
z.B.: (a, 2, 2, a) oder (a, 2, 4, b)
2) ld10 = lglg10
2 = 3, 32 Aufrunden! Also: minimale Anzahl der Fragen ist: dld10e
Hinweis:
dxe (‘ceiling’): kleinste ganze Zahl, die größer oder gleich x ist.
bxc (‘floor’): größte ganze Zahl, die kleiner oder gleich x ist.
3)
a) log10 1000 = 3
d) log5 25 = 2
g) ld 1024 + ld 32 = 15
b) log10 1016 = 16
e) log7 49−2 = −4
h) ld (512 + 512) = 10
4) X = {a, b, c, d} , p(a) = p(b) = 18 , p(c) =
a) p(d) = 1 −
1
2
−
1
8
−
1
8
=
1
2
c) log10 10−16 = −16
1
= −7
f) ld 128
, s = |X| = 4
1
4
Ps
b) H4,4 = −4 · i=0 p(Xi ) · ld(p(Xi )) = 74
H4 = −( 18 ·ld( 18 )+ 18 ·ld( 18 )+ 12 ·ld( 12 )+ 14 ·ld( 14 ))
H4 = −( 18 + (− 18 ) + (− 12 ) + (− 24 )
H4 = 83 + 38 + 12 + 24 = 74 Bit
73
ld p(a)
ld p(c) = ld p(b)
ld p(d)
=
=
=
−3
−1
−2
74
ANHANG C. LÖSUNGEN
c)
H4,4
= −4 ·
s
X
p(xi ) · ld p(xi )
i=0
=
d)
7
4
7
= −ld p(xi )
1
a : H(a) = −ld ( ) = 3
8
1
c : H(c) = −ld ( ) = 1
2
H(acca) = 2 · H(a) + 2 · H(c) = 8Bit
H(xi )
· 1000 = 1750 Bit
e) Informationsgehalt eines Zeichens bei p(a) = . . . = p(d) = 14 :
Hs = −4 ·
1
1
· ld = 2Bit
4
4
Für 1000 Zeichen:
Hs,n = n · ld s = 1000 · 2 = 2000
Schlussfolgerung: 2000 Bit > 1750 Bit
5)
a) 512 MByte = 29 · 220 Byte = 229 Byte = 229 · 23 Bits = 232 Bits.
b) ld (229 Byte) = 29 Bit
6)
a) Ein Quader ist bestimmt durch 3 Kantenlängen, a, b, c
Volumen: V = a · b · c, Fläche: F = 2(ab + bc + ac)
Ein Algorithmus wäre z.B.:
1.
2.
3.
4.
5.
b)
Eingabe von a, b, c
Setze V ← a · b · c
Setze F ← 2(ab + bc + ac)
Ausgabe V, F
Stop
1. Eingabe der ersten Zahl x = xn xn−1 . . . x1 x0
2. Eingabe der zweiten Zahl y = ym ym−1 . . . y1 y0
3. Wenn n > m dann
3.1. Setzte yn ← yn−1 ← . . . ← ym+1 ← 0
4. Wenn m > n dann
4.1. Setzte xm ← xm−1 ← . . . ← xn+1 ← 0
5. Setze Ü ← 0
6. Für i ← 0 . . . max(n, m) führe durch
6.1. Setze S ← xi + yi + Ü
6.2. Setze Zi ← S mod 10
6.3. Setze Ü ← bS/10c
7. Falls Ü 6= 0
7.1. Zmax(n,m)+1 ← Ü
8. Ausgabe der Summe Z
Bemerkung: Der Übertrag kann nur 0 oder 1 sein! Beweis durch vollständige Induktion:
C.2. 2. ÜBUNG
75
1. Zu Anfang ist Ü = 0
2. Angenommen, der Übertrag ist an der Stelle k maximal 1. Dann ist die Summe
an der Stelle k S = 9 + 9 + 1 = 19. Damit ist der Übertrag in die Stelle k + 1
ebenfalls maximal 1. Allgemein:
S
Ü
= (b − 1) + (b − 1) + 1 = 2b − 1 < 2b
= b(2b − 1)/bc = 1
Weitere Bemerkungen zu dieser Übung:
Ist |B|2 = |B 2 |?
Sei B = {a, b}, dann ist |B| = 2.
|B|2 = 22
|B 2 | = |B ⊗ B| = |B| · |B| = 2 · 2
2
Beweis: |B| = |B| · |B| = |B 2 | = |B ⊗ B| = |B| · |B|
Wie errechnet man ldx, wenn man nur log10 zur Verfügung hat (wie z.B. auf dem Taschenrechner)?
Allgemein: gesucht ist y = logb x, gegeben z = logc x
Beides nun Basishoch (also blogb x und clogc x ) nehmen. Dann erhält man: by = x und cz = x.
Also ist by = cz . Daraus folgt:
logc by
y · logc b
= logc cz
= z · logc c
| {z }
1
y
=
y
=
z
logc b
logc x
logc b
cx
Also: y = logb x = log
logc b
Beispiel: Was ist log7 49?
y
y
y
y
C.2
1)
= log7 49
log10 49
=
log10 7
1, 69
≈
0, 845
= 2
2. Übung
2025
337
3AB16
2028
=
=
=
=
2 · 52 + 0 · 51 + 2 · 50
3 · 71 + 3 · 70
2
3 · 16 + 10 · 161 + 11 · 160
2 · 82 + 0 · 81 + 2 · 80
=
=
=
=
5210
2410
93910
13010
76
ANHANG C. LÖSUNGEN
2)
Dezimal
0
1
9
10
22
64
255
4321
Dual
0
1
1001
1010
10110
1000000
11111111
1000011100001
Oktal
0
1
11
12
26
100
377
10341
Hexadezimal
0
1
9
A
16
40
FF
10E1
3) 123416 = 0001 0010 0011 01002 ⇒ 5 Schalter sind an.
4) Affe = 41 66 66 65
5)
a) kleinste: 010 , 016
14
größte: |{z}
11 1111
| {z } 1111
| {z} 1111
|{z } = 3F F F16 = 1638310 = 2 − 1
3
F
F
F
b) kleinste: −819210 = −200016 = −214−1
größte: 214−1 − 1 = 819110 = 1F F F16
6) 5510 = 3716 =00 0011 01112 → Komplement: 11 1100 1000 → +1 =11 1100 1001 = 3C9
7)
205 div 16 =
12 div 16 =
⇒ 20510 = CD16
12
0
R 13
R12
0, 125 · 16 = 2, 0
⇒ 0, 12510 = 0, 216
205, 12510 = 00CD, 200016
Probe: 12 · 161 + 13 · 160 + 2 · 16−1 = 205, 12510
8) Beim Multiplizieren ist im Produkt der Skalierungsfaktor einmal zuviel enthalten:
(x · s) · (y · s) = x · y · s2
Beispiel: 1, 25 · 1, 25 → 125 · 125 = 15625 → 156 Dies ist sicherlich falsch. Korrektur nach
der Multiplikation: 156 → 1, 56
Bei der Division ist im Quotienten der Skalierungsfaktor gar nicht mehr enthalten:
x·s
x
y·s = y Das führt zu unzulässiger Rundung!
700
Beispiel: 7,00
2,00 (sollte 3,50 liefern) → 200 liefert 3!
70000
Korrektur vor der Division: x·s·s
y·s ⇒ 200 = 350 → 3, 50
9)
a) 2, 345 · 105 ≈ 1, 78909302 · 217
Vorzeichen: 0
Exponent: 127 + 17 = 144 = 100100002
Mantisse: 1, 79 = 1, 11001012
⇒ 01001000011001010 . . . 02 = 4865000016
Berechnung der Mantisse:
Nachkommastellen
z }| {
78909302
108
|{z}
8 Nachkommastellen
·
223
|{z}
23 Stellen in Mantisse
= 6619392
C.3. 3. ÜBUNG
77
Das Ergebnis nun in Hex wandeln: 65010016
Hinweis: Um den Exponenten auszurechnen kann auch der bld c von (in diesem
Fall) 2, 345 · 105 genommen werden (ergibt hier 17). Dann 2, 345 · 105 durch 217
teilen und 1, 78909302 erhalten
Bei negativem Vorzeichen im alten Exponenten muss aufgerundet und multipliziert
werden.
b) −1, 111 · 10−11 ≈ −1, 53 · 2−37
Vorzeichen: 1
Exponent: 127 + (−37) = 90 = 010110102
Mantisse: 1, 53 = 1, 10000112
⇒ 10101101010000110 . . . 02 = AD43000016
c) 33 · 1022 ≈ 1, 0918 · 278
Vorzeichen: 0
Exponent: 127 + 78 = 205 = 110011012
Mantisse: 1, 0918 = 1, 0001011112
⇒ 0110011010001011110 . . . 02 = 6684BC0016
d) −33 · 1022 ≈ −1, 0918 · 278
Vorzeichen: 1
Exponent: 127 + 78 = 205 = 110011012
Mantisse: 1, 0918 = 1, 0001011112
⇒ 1110011010001011110 . . . 02 = E684BC0016
e) 33 · 10−22 ≈ −1, 95 · 2−69
Vorzeichen: 0
Exponent: 127 + (−69) = 58 = 001110102
Mantisse: 1, 95 = 1, 11110012
⇒ 000111010111100100 . . . 02 = 1D79000016
f) −33 · 10−22 ≈ −1, 95 · 2−69
Vorzeichen: 1
Exponent: 127 + (−69) = 58 = 001110102
Mantisse: 1, 95 = 1, 11110012
⇒ 100111010111100100 . . . 02 = 9D79000016
10)
a) B7AAF F EE16 = 101101111010101011111111111011102
Vorzeichen: 1
Exponent: 011011112 = 11110 ⇒ 111 − 127 = −16
Mantisse: −1, 010101011111111111011102 = 2AF F EE16 = 281803010
⇒ − 2818030
· 2−16 ≈ −2, 038476 · 10−5
223
b) EEF F AAB716 = 111011101111111110101010101101112
Vorzeichen: 1
Exponent: 110111012 = 22110 ⇒ 221 − 127 = 94
Mantisse: −1, 111111110101010101101112 = 7F AAB716 = 836677510
· 294 ≈ −3, 956253 · 1028
⇒ − 8366775
223
C.3
3. Übung
1) Ein möglicher Huffman-Baum könnte so aussehen:
78
ANHANG C. LÖSUNGEN
0
NA
37
37
{BOOM, ..., YIP}: 21
37
0
1
{BOOM, WAH, GET, A, JOB, SHA}: 11
37
YIP 10
1
37
0
4
{A, JOB, SHA}:
{BOOM, WAH, GET}: 37
1
1
0
0
3
2
SHA 37
GET 37
4
{A,
JOB}:
37
2
{BOOM, WAH}: 37
0
1
1
2
2
A 37
JOB 37
1
WAH 37
16
37
0
BOOM
{BOOM, ..., NA}:
1
1
37
Zeichen Code Anzahl Bits Vorkommen
A 11100 5 Bits
2
BOOM 11000 5 Bits
1
GET
1101 4 Bits
2
JOB 11101 5 Bits
2
NA
0 1 Bit
16
SHA
1111 4 Bits
3
YIP
10 2 Bits
8
WAH 11001 5 Bits
1
Alles zusammen gibt dann 82 Bits. Für einen Code mit fester Länge gilt: 8 Symbole ⇒
3 Bits pro Symbol, also insgesammt 105 Bits.
Der mittlere Informationsgehalt pro Zeichen ist:
1
2
1
86
2
· 5+
·5 +
· 4+... +
·5=
≈ 2, 324
37
37
37
37
| {z } | {z } | {z }
| {z } 37
A
BOOM
GET
W AH
Entropie der Quelle:
−
2
2
· ld
37
37
−
1
1
· ld
37
37
− ... −
1
1
· ld
37
37
≈ 2, 291
Man sieht: Huffman-Codierung liefert (rationale) Approximation der Entropie. Die Codierung heißt daher auch “Entropie-Codierung”.
2)
a) 1-Bit-Fehler-Erkennung durch Paritätsbit.
Hamming-Distanz ≥ 2 (hier sogar = 2)
Gerade Parität
7
37
C.3. 3. ÜBUNG
79
P D D D D
0 0 0 0 0
1 0 0 0 1
1 0 0 1 0
0 0 0 1 1
1 0 1 0 0
0 0 1 0 1
0 0 1 1 0
1 0 1 1 1
1 1 0 0 0
0 1 0 0 1
0 1 0 1 0
1 1 0 1 1
0 1 1 0 0
1 1 1 0 1
1 1 1 1 0
0 1 1 1 1
Erfolgreiche Erkennung eines 1-Bit-Fehlers:
1011 → 11011 ⇒ 11001 : dies ist ein ungültiges Codewort!
Keine Erkennung eines 2-Bit-Fehlers:
1011 → 11011 ⇒ 10001 → 0001 !
b) 1-Bit-Fehler-Korrektur
Hamming-Distanz ≥ 3
Gerade Parität
P1 P2 D3 P4 D5 D6 D7
0
0
0
0
0
0
0
1
1
0
1
0
0
1
0
1
0
1
0
1
0
1
0
0
0
0
1
1
0
1
1
0
0
1
0
0
0
1
0
1
0
1
1
1
0
0
1
1
0
0
0
0
1
1
1
1
1
1
1
0
0
0
0
0
0
1
1
0
0
1
1
1
0
1
0
1
0
0
1
1
0
0
1
1
0
1
1
1
1
0
0
1
0
1
0
1
0
1
0
0
1
0
1
1
0
0
0
1
0
1
1
0
Beteiligung an Paritätsberechnung:
1: 1,3,5,7 Alle p-Bits, dessen Stelle in Binärschreibweise das 1. Bit gesetzt haben.
2: 2,3,6,7 Alle p-Bits, dessen Stelle in Binärschreibweise das 2. Bit gesetzt haben.
4: 4,5,6,7 Alle p-Bits, dessen Stelle in Binärschreibweise das 4. Bit gesetzt haben.
α) Erfolgreiche Korrektur eines 1-Bit-Fehlers:
1011 → 0110011
 ⇒ 0110001 → 0110011 → 1011
P1 , : ok

P2 , : falsch
Fehler in Bit 6 (6 ergibt sich aus 2+4)

P4 , : falsch
80
ANHANG C. LÖSUNGEN
β) Erfolgreiche Erkennung eines 2-Bit-Fehlers:
1011 → 011001
 ⇒ 0111001 → 0011001 → 1001 (!)
P1 , : ok

P2 , : falsch
“Fehler in Bit 2”

P4 , : ok
0011001 ist ein gültiges Codewort, d.h. man merkt nicht, dass man reingefallen
ist!
γ) Keine Erkennung eines 3-Bit-Fehlers:
1011 → 0110011
⇒ 1000011 → 0011 (!)

P1 , : ok 
P2 , : ok
“kein Fehler”

P4 , : ok
c) Hamming-Distanz ≥ 3 ⇒ Lösung von 2b) gilt auch hier.
Separate Fragestellung:
Wie viele Bits r braucht man zusätzlich zu den m Bits einer Nachricht, um alle e-BitFehler zu korrigieren?
Codewortlänge n = m + r
2m legale Nachrichten
Wie viele Punkte im Codewort-Raum werden durch die Nachricht belegt?
e X
n
n
n
n
+
+... +
=
1
+
|{z}
2
e
i
1
i=0
| {z }
| {z }
| {z }
gültiges
Codewort
Codewort Codewort Codewort
für 1-Bit- für 2-Bitfür e-BitFehler
Fehler
Fehler
⇒ Bedingung:
m
2 ·
e X
n
i=0
i
n
m+r
≤2 =2
⇒
e X
m+r
i=0
i
≤ 2r
Spezialfälle:
• e = 0:
0 X
m+r
i=0
i
= 1 ≤ 2r ⇒ r = 0
Keine Redundanz wenn keine Fehler korrigiert werden sollen.
• e = 1:
1 X
m+r
i=0
i
= 1 + m + r ≤ 2r
Dies ist die Formel aus der Vorlesung (für 1-Bit-Fehler).
• e = n (!!):
m+r
2
n
n
= 2 = (1 + 1) =
n X
n
i=0
Geht nur für m = 0: d.h. geht nicht!
i
i
1 ·1
n−1
=
n X
n
i=0
i
≤ 2r
C.4. 4. ÜBUNG
81
Korrektur aller 2-Bit-Fehler
m
r
1
4
2
5
4
6
8
7
16 9
32 10
C.4
1)
Korrektur aller 4-Bit-Fehler
m
r
1
8
2 10
4 11
8 13
16 16
32 18
4. Übung
block SuccPrim(ein: P rim ∈ N; aus: N P rim ∈ N)
lokal ok ∈ {w, f }
{
falls P rim = 2 dann {
N P rim ← 3;
} sonst {
wiederhole {
P rim ← P rim + 2;
istPrim(P rim, ok);
} solange 6= ok;
N P rim ← P rim;
}
}
block istPrim(ein p ∈ N; aus: ok ∈ {w, f })
lokal t ∈ N;
{
falls p ≤ 1 dann ok ← f sonst
falls p = 2 dann ok ← w sonst
falls p mod 2 = 0 dann ok ← f sonst{
t ← 3;
solange t · t ≤ p und p mod t 6= 0 wiederhole {
t ← t + 1;
}
ok ← (t · t > p);
}
}
2) Vorbemerkungen:
u=
30 = 2·
v = 105 =
w=
70 = 2·
ggT
=
3·
3·
5
5·7
5·7
5
Daher wäre zum Beispiel möglich:
ggT(u, v, w)=ggT(ggT(u, v),w)
ggT(u, v, w)=ggT(ggT(u, v),ggT(v, w))
82
ANHANG C. LÖSUNGEN
Eine Lösung hierfür wäre zum Beispiel:
block ggT3(ein: u ∈ N, v ∈ N, w ∈ N; aus: g ∈ N)
{
ggT(u, v, v);
ggT(v, w, g);
}
block ggT(ein: u ∈ N, v ∈ N; aus: g ∈ N)
{
solange (u 6= v) {
falls u > v {
u ← u − v;
} sonst {
v ← v − u;
}
} //u = v
g ← u;
}
Eine andere Lösung, wäre den bekannten Algorithmus auf drei Zahlen zu erweitern:
block ggT3(ein: u ∈ N, v ∈ N, w ∈ N; aus: g ∈ N)
lokal: k ∈ N; {
solange u 6= v ∨ v 6= w wiederhole{
k ← min3(u, v, w);
falls k < u dann u ← u − k;
falls k < v dann v ← v − k;
falls k < w dann w ← w − k;
}
g ← u;
}
block min3(ein: u ∈ N, v ∈ N, w ∈ N; aus: m ∈ N)
{
falls u ≤ v ∧ u ≤ w dann m ← u sonst
falls v ≤ u ∧ v ≤ w dann m ← v sonst
m ← w;
}
3) Vorbemerkungen:
n f(n)
1
1
2
2
3
2
⇒ f (n) = bld nc + 1
4
3
5
3
6
3
7
3
8
4
C.4. 4. ÜBUNG
83
block ldnplus1(ein: n ∈ N; aus: f ∈ N)
{
f ← 1;
solange n > 1 wiederhole {
n ← n div 2;
f ← f + 1;
}
}
4) Vorbemerkungen:
n
n
→
k
k+1
n · (n − 1) · . . . · (n − (k + 1) + 1)
1 · 2 · 3 · . . . · (k + 1)
n · (n − 1) · . . . · (n − k + 1) · (n − k)
=
1 · 2 · 3 · . . . · k · (k + 1)
n
n−k
=
·
k
k+1
=
Also etwas anders ausgedrückt:
n
n
n−k+1
=
·
k
k
k−1
n
für k = 1 . . . n,
=1
0
block binomkoeff(ein: n ∈ N0 , k ∈ N0 ; aus: nük ∈ N0 )
lokal: i ∈ N0 ;
{
nük ← 1;
für i ← 1 solange i ≤ k mit i ← i + 1 {
nük ← (nük · (n − i + 1)) div i;
}
}
5)
a)
block bhochn(ein: b ∈ N, n ∈ N; aus: erg ∈ N)
lokal: a ∈ N
{
solange n > 0 wiederhole {
a ← a · b;
n ← n − 1;
}
erg ← a;
}
84
ANHANG C. LÖSUNGEN
b)
block bhochn(ein: b ∈ N, n ∈ N; aus: erg ∈ N)
lokal: a ∈ N
{
solange n > 0 wiederhole {
falls n mod 2 = 0 dann {
b ← b · b;
n ← n div 2;
} sonst {
a ← a · b;
n ← n − 1;
}
}
erg ← a;
}
Also: Das Verfahren beruht auf der Binärdarstellung von n: steht ‘hinten’ eine
1, muss multipliziert werden, steht dort einen 0, muss quadriert werden. Das ist
effizienter: statt der absoluten Größe, geht es jetzt nur um die Zahlen der Stellen.
Anwendung: Kryptographie
d


e
 (Nachricht)  = Nachricht (mod n)
|
{z
}
Verschlüsseln
|
{z
}
Entschlüsseln
e, d, n: ca. 150-200stellige ganze Zahlen.
Beispiel: b9
a)
a
1
b
b2
b3
..
.
n
9
8
7
6
..
.
b8 1
b9 0
Beobachtung: Invariante ist i + n = 9 (wenn a = bi )
9 Schleifendurchgänge
b)
a
1
b
b
b
b
b9
b
b
b
b2
b4
b8
b8
n
9
8
4
2
1
0
5 Schleifendurchgänge
C.5. 5. ÜBUNG
C.5
1)
85
5. Übung
block ldnplus1(ein: n ∈ N; aus: erg ∈ N)
{
falls n = 1 dann erg ← 1 sonst {
ldnplus1(n div 2, erg);
erg ← erg + 1;
}
}
Maximale Rekursionstiefe: n
1
2
3
4
5
6
7
8
9
Anzahl
1
2
2
3
3
3
3
4
4
n
⇒ maximale Rekursionstiefe: (n) → b c → . . . → (1)
2 {z
|
}
bld nc+1
2) Hier werden zwei verschiedene Lösungen angeboten. Erste Lösung für

 0
n
n!
1
=
=
k
k!(n − k)!  n−1 +
k−1
n−1
k
n<k
k =0∨n=k
0<k<n
block binomkoeff(ein: n ∈ N, k ∈ N; aus: erg ∈ N)
lokal: erg1 ∈ N, erg2 ∈ N;
{
falls n < k {
erg ← 0;
} sonst falls k = 0 ∨ n = k {
erg ← 1;
} sonst {
binomkoeff(n − 1, k − 1, erg1);
binomkoeff(n − 1, k, erg2);
erg ← erg1 + erg2;
}
}
Zweite Lösung für
n
n
n−k+1 n
=
·
,
=1
k
k−1
k
0
86
ANHANG C. LÖSUNGEN
block binomkoeff(ein: n ∈ N0 , k ∈ N0 ; aus: nük ∈ N0 )
lokal: erg ∈ N0 ;
{
falls k = 0 {
nük ← 1;
} sonst {
binomkoeff(n, k − 1, erg);
nük ← (erg · (n − k + 1)) /k;
}
}
Maximale Rekursionstiefe für Lösung zwei: (k) → (k − 1) → . . . → (0)
|
{z
}
k+1
3)
a)
block bhochn(ein: b ∈ N, n ∈ N0 , aus: erg ∈ N)
{
falls n = 0 dann {
erg ← 1;
} sonst {
bhochn(b, n − 1, erg);
erg ← erg · b;
}
}
b)
block bhochn(ein: b ∈ N, n ∈ N0 , aus: erg ∈ N)
{
falls n = 0 dann {
erg ← 1;
} sonst {
falls n mod 2 = 0 dann {
bhochn(b · b, n div 2, erg);
} sonst {
bhochn(b, n − 1, erg);
erg ← erg · b;
}
}
}
c)
α)
β)
γ)
δ)
einfache iterative Lösung
Platz : O(1)
(da Platzbedarf konstant)
Zeit : O(n)
(da die Schleife n-mal durchlaufen wird)
schlaue iterative Lösung
Platz : O(1)
(da Platzbedarf konstant)
Zeit : O(log n) (da die Schleife maximal (2 · ld n)-mal durchlaufen wird)
einfache rekursive Lösung
Platz : O(n)
(da die maximale Rekursionstiefe n + 1 ist)
Zeit : O(n)
(da die maximale Rekursionstiefe n + 1 ist)
schlaue rekursive Lösung
Platz : O(log n) (da die maximale Rekursionstiefe (2 · ld n) ist)
Zeit : O(log n) (da die maximale Rekursionstiefe (2 · ld n) ist)
C.5. 5. ÜBUNG
4)
87
Lineal(0,8,3)
Lineal(0,4,2)
Lineal(0,2,1)
Lineal(0,1,0)
Lineal(1,2,0)
Zeichne(1,1)
Lineal(2,4,1)
Lineal(2,3,0)
Lineal(3,4,0)
Zeichne(3,1)
Zeichne(2,2)
Lineal(4,8,2)
Lineal(4,6,1)
Lineal(4,5,0)
Lineal(5,6,0)
Zeichne(5,1)
Lineal(6,8,1)
Lineal(6,7,0)
Lineal(7,8,0)
Zeichne(7,1)
Zeichne(6,2)
Zeichne(4,3)
Die Rekursionstiefe ist 4 = H öhe + 1.
0
1.
3.
2.
7.
4.
6.
5.
8
5) Aufrufbaum:
1
2
(1,1,2,3)
3 a 4
(2,1,3,2)
5
b
8
9
(2,2,1,3)
f
12
(1,2,3,1)
(1,1,2,3)
10 e 11
13 g 14
(0,1,3,2) (0,2,1,3) (0,3,2,1) (0,1,3,2) (0,2,1,3) (0,3,2,1) (0,1,3,2) (0,2,1,3)
Ausgabe:
(1,3,1,2)
6 c 7
(3,1,2,3)
d
88
ANHANG C. LÖSUNGEN
a
b
c
d
e
f
g
:
:
:
:
:
:
:
1
1
3
1
2
2
1
→
→
→
→
→
→
→
3
2
2
3
1
3
3
Dieses Prinzip der Abarbeitung nennt man “depth first”, also das Tiefste zuerst.
Zeitbedarf: O(2n )
Platzbedarf: O(n)
C.6
6. Übung
1)
!!P: a > 0 ∧ b = 0;
!!Z1 : a 6= b ∧ a > 0 ∧ b = 0;
solange a 6= b wiederhole {
a ← a + b;
!!Z2 : a 6= b ∧ a > 0 ∧ b = 0;
b ← b + a;
!!Z3 : a > 0 ∧ b = a;
}
!!Q: a > 0 ∧ b = a ∧ b > 0;
2)
a)
!!P: x > y;
!!Z1 : x − y > 0;
y ← x − y;
!!Z2 : y > 0;
!!Z3 : x + y > x;
y ← x + y;
!!Z4 : y > x;
!!Q: x < y;
b)
!!P: wahr;
falls x < 0 dann {
Z1 : x < 0;
Z2 : −x > 0;
y ← −x;
Z3 : y > 0 ∧ y = |x|;
} sonst {
Z4 : x ≥ 0;
y ← x;
Z5 : y ≥ 0 ∧ y = |x|;
}
Z6 : (y > 0 ∧ y = |x|) ∨ (y ≥ 0 ∧ y = |x|);
Z7 : y ≥ 0 ∧ y = |x|;
!!Q: y = |x|;
C.6. 6. ÜBUNG
c)
89
!!P: y ≥ 0;
solange y 6= 0 wiederhole {
y ← y − 1;
}
!!Q: y = 0;
α) Wenn die Schleife verlassen wird, gilt: ¬(y 6= 0), das heißt y = 0 : Q
β) Zu Beginn ist y ≥ 0. In der Schleife wird y heruntergezählt, daraus folgt, dass
die Schleife terminiert.
Achtung: Die Schleife muss abweisend sein!
!!P: y ≥ 0
wiederhole {
y ← y − 1;
} solange y 6= 0;
Terminiert nicht für y = 0!
d)
!!P: y + z = 10;
solange y 6= 0 wiederhole {
z ← z + 1;
y ← y − 1;
}
!!Q: z = 10 ∧ y = 0;
I(V): y + z = 10
1. P(V): y + z = 10 : I(V)
2. I(V) ∧ B(V): y + z = 10 ∧ y 6= 0
A(V): z 0 = z + 1, y 0 = y − 1
y 0 + z 0 = y − 1 + z + 1 = y + z = 10
also: y 0 + z 0 = 10 : I(A(V)) (dies ist die Invariante für die transformierten
Variablen.)
3. I(V) ∧¬ B(V): y + z = 10 ∧ y = 0 ⇒ z = 10 ∧ y = 0 : Q(V)
Terminierung? Ja, aber nur wenn zu Anfang auch folgendes gilt: y ≥ 0!!!
3)
a) bn naiv
!!P: n ≥ 0;
a←1
solange n > 0 wiederhole {
a ← a · b;
n ← n − 1;
}
erg ← a;
!!Q: erg = bn
I(V): a · bn = bn ∧ n ≥ 0
1. P(V): n ≥ 0 ∧ a = 1 ∧ n = n
⇒ n ≥ 0 ∧ a · bn = bn
2. I(V) ∧ B(V): n ≥ 0 ∧ a · bn = bn ∧ n > 0
⇒ n > 0 ∧ a · bn = bn
90
ANHANG C. LÖSUNGEN
0
A: a0 = a · b, n0 = n − 1, b0 = b, a0 · b0n = (a · b) · bn−1 = a · bn = bn
und n0 = n − 1 > 0 − 1 = −1, das heißt n0 ≥ 0
0
also: a0 · b0n = bn ∧ n0 ≥ 0: I(A(V))
3. I(V) ∧¬B(V): n ≥ 0 ∧ a · bn = bn ∧ n ≤ 0
⇒ n = 0 ∧ a · bn = bn ⇒ a = bn : Q(V)
b)
!!P: n ≥ 0;
a←1
solange n > 0 wiederhole {
falls n mod 2 = 0 dann {
b ← b · b;
n ← n div 2;
} sonst {
a ← a · b;
n ← n − 1;
}
}
erg ← a;
!!Q: erg = bn
I(V): a · bn = bn ∧ n ≥ 0
1. P(V): n ≥ 0 ∧ a = 1 ∧ n = n ∧ b = b
⇒ n ≥ 0 ∧ a · bn = bn
2. I(V) ∧ B(V): n ≥ 0 ∧ a · bn = bn ∧ n > 0
⇒ n > 0 ∧ a · bn = bn
2.1. falls n mod 2 = 0:
0
A:b0 = b · b, n0 = n div 2, a0 = a, a0 · b0n = a · (b · b)n div 2 = a · bn = bn
und n0 = n div 2 ≥ 0
2.2. falls n mod 2 6= 0
0
A: a0 = a · b, n0 = n − 1, b0 = b, a0 · b0n = (a · b) · bn−1 = a · bn = bn
und n0 = n − 1 > 0 − 1 = −1, das heißt n0 ≥ 0
3. I(V) ∧¬B(V): n ≥ 0 ∧ a · bn = bn ∧ n ≤ 0
⇒ n = 0 ∧ a · bn = bn ⇒ a = bn : Q(V)
4)
block FindeGrößte(ein: a ∈ feld[1. . .n] von integer; aus: g ∈ integer, m ∈ boolean)
lokal: i ∈ integer;
{
für i ← 2 solange i ≤ n mit i ← i − 1 wiederhole {
falls a[i] > g dann {
g ← a[i];
m ← falsch;
} sonst falls a[i] = g dann {
m ← wahr;
}
}
}
C.7. 7.ÜBUNG
C.7
91
7.Übung
1) Zunächst ein kleines Beispiel:
((3 ∗ 2) + 4)
+
*
4
3
2
block Eval(ein: expr ∈ Ausdruck; aus: result ∈ float)
lokal: res1, res2 ∈ float;
{
falls exprb.blatt dann {
result ← exprb.zahl;
} sonst {
Eval(exprb.links, res1);
Eval(exprb.rechts, res2);
falls exprb.op =
’+’: resultat ← res1 + res2;
’-’: resultat ← res1 − res2;
’*’: resultat ← res1 ∗ res2;
’/’: resultat ← res1/res2;
sonst: Fehlerabbruch(“Unbekannte Operation”);
}
}
2)
a) Auch ein Beispiel des Binärbaumes:
ˆ
=e
ˆ 1ˆe2 =e
ˆ e12
e1
e2
Auswertung: ee12 = eln e1 = ee
2
e2
·ln e1
Einfügen eines Falles in die mehrfache Alternative:
’b ’ : result ← exp(res2 ∗ ln(res1));
Achtung: Fallunterscheidung notwendig für res1 ≤ 0!
b) Beispiel des Binärbaumes:
=
ˆ − e1
92
ANHANG C. LÖSUNGEN
Eval(exprb.links, res1);
falls exprb.rechts 6= nil dann {
Eval(exprb.rechts, res2);
}
falls exprb.op =
’+’: resultat ← res1 + res2;
’-’: falls exprb.rechts = nil dann {
//unäres Minus
resultat ← −res1
} sonst {
//binäres Minus
resultat ← res1 − res2;
}
..
.
Bemerkung: Hier ist auch ein unäres Plus möglich, da nicht überprüft wird, ob bei
einem einzelnen Operand der Operator auch ein Minus ist.
3)
global: nc ∈ char // ein Zeichen Vorausschauen
block Lesen(aus: expr ∈ Ausdruck)
lokal: op ∈ char; z ∈ float; links, rechts ∈ Ausdruck;
{
falls nc ∈ {0 00 ,0 10 , . . . ,0 90 } dann {
LeseZahl(z);
new(expr);
exprb.blatt ← wahr;
exprb.zahl ← z;
} sonst falls nc = ’(’ dann {
NächstesZeichen(nc);
Lesen(links);
op ← nc;
NächstesZeichen(nc);
Lesen(rechts);
new(expr);
exprb.blatt ← falsch;
exprb.op ← op;
exprb.links ← links;
exprb.rechts ← rechts;
falls nc 6= ’)’ dann Fehlerabbruch(“’)’ erwartet!”);
NächstesZeichen(nc);
} sonst {
Fehlerabbruch(“Zeichen startet keinen Ausdruck!”);
}
}
C.7. 7.ÜBUNG
93
block main()
lokal: expr ∈ Ausdruck; z ∈float;
{
Bildschirmausgabe(“Bitte Ausdruck eingeben: ”);
NächstesZeichen(nc);
Lesen(expr);
Eval(expr, z);
Bildschirmausgabe(“Der Wert ist ”, z);
}
block LeseZahl(aus: z ∈ float)
lokal: ganz ∈ integer; gebrochen, gewicht ∈ float;
{
ganz ← 0
solange nc ∈ {0 00 ,0 10 , . . . ,0 90 } wiederhole {
ganz ← ganz ∗ 10 + (ord(nc) − ord(0 00 ));
|
{z
}
Zahlenwert der Ziffer
NächstesZeichen(nc);
}
gebrochen ← 0;
falls nc =0 .0 dann {
gewicht ← 1;
NächstesZeichen(nc);
solange nc ∈ {0 00 ,0 10 , . . . ,0 90 } wiederhole {
gewicht ← gewicht/10;
gebrochen ← gebrochen + gewicht ∗ (ord(nc)−ord(0 00 ));
NächstesZeichen(nc);
}
}
z ← ganz + gebrochen;
}
4) Syntaxdiagramm für Folge mit mindestens einer Verarbeitung:
folge
{
verarbeiteung
}
Syntaxdiagramm für Folge, die keine Verarbeitung zulässt:
folge
{
}
verarbeiteung
94
ANHANG C. LÖSUNGEN
Auswahl
falls
ausdruck
=
A
dann
verarbeitung
sonst
A
:
konstanter ausdruck
verarbeitung
sonst
wiederholung
solange
ausdruck
wiederhole
für
solange
schrittsteuerung
bezeichner
verarbeitung
solange
wiederhole
initialisierung
initialisierung
verarbeitung
wiederhole
verarbeitung
immer
verarbeitung
verarbeitung
ausdruck
wiederhole
←
ausdruck
ausdruck
mit
verarbeitung
;
Index
‘aus’-Parameter, 35
‘ein’-Parameter, 35
‘mit’-Parameter, 35
Integer, 50
konkrete, 49
strukturierte, 50
taxonomie, 49
Verbunde, 50
Zeiger, 51
Deklarationsteil, 35
depth first, 88
deterministische Algorithmen, 14
Digital, 15
Digitalrechner, 15
Dualzahlen, 19
Ablaufsteuerung, 29
Abstraktionsbarrieren, 15
abweisende Schleife, 31
Algorithmen, 14, 27
Analog, 15
ASCII, 20
Assoziativität, 65
Aufruf, 34
Aufrufbaum, 41
Auswahl, 30
einarmiges if, 30
einfache Alternative, 30
Endliche Automaten, 60
Endlosschleife, 34
Entropie der Nachrichtenquelle, 13
even parity, 24
Exponent, 22
Exponenten-Overflow, 23
Exponenten-Underflow, 23
Basisfall, 37
Basistyp, 51
bedingte Verarbeitung, 30
Binomenalkoeffizient, 83
Binär-Baum, 24
Binäre Suche, 12
Bit, 13
Blöcke, 34
Byte, 13
Fakultat, 37
Fano-Bedingung, 23
Fehlererkennung, 24
Fehlerkorrektur, 24
Festkommadarstellung, 22
Fibunacci-Zahlen, 38
Fließkommadarstellung, 22
floor, 73
Flussdiagramm, 29
Folge, 29
Formale Beschreibung, 60
call-by-reference, 35
call-by-value, 35
ceiling, 73
Codes, 19
Datenkomprimierung, 23
Datenstrukturen, 48
Datentypen, 48
abstrakte, 49
Aufzahlung, 50
Boolean, 50
Character, 50
einfache, 50
Felder, 50
Fließkomma, 50
Geschachtelte, 51
idealisierte, 49
ganze Zahlen, 21
ggT, 27, 81
globale Daten, 56
Größenordnung, 22
Hamming-Distanz, 24
Hexadezimalzahlen, 19
95
96
hierarchische Dekomposition, 35
Huffman-Baum, 77
Huffman-Codes, 52
Huffman-Codierung, 23
Informatik
angewndte, 17
praktische, 17
technische, 16
theoretische, 16
Information Hiding, 35
Kardinalität, 63
Kellerautomat, 61
Klausur, 9
Zulassung, 9
Kontextfreie Grammatik, 60
Kontrollelemente, 29
Kontrollstrukturen, 48
Korrektheit, 46
Kreuzprodukt, 63
Kryptographie, 84
Lexikalische Analyse, 60
Lexikalische Struktur, 59
Lineal, 40
Logarithmen, 64
lokale Variablen, 55
INDEX
Programmiersprache
funktionale, 28
logische, 28
prozeduale, 28
Programmiersprachen, 59
Präfix-Freiheit, 23
Pseudocode, 29
Reguläre Ausdrücke, 60
Reihenfolge, 65
Rekursion, 37, 53
Rekursionstiefe, 38, 85
Ressourcen, 42
Schleifeninvariante, 47
Semantik, 59
signifikanten Stellen, 22
Skalierung, 22
Skalierungsfaktor, 76
Spezifikation, 46
Stellenwertsysteme, 19
Struktogramm, 29
Syntaktische Struktur, 60
Syntax, 59
Syntax-Diagramme, 61
Syntaxdiagramm, 93
Token, 59
Türme von Hanoi, 42
Mantisse, 22
Maß für die Information, 12
mehrfache Alternative, 30
Mittlerer Informationsgehalt, 13
Unicode, 20, 21
n-Tupel, 12, 63
Nachbedingungen, 46
Nachricht, 12
nicht abweisende Schleife, 34
nil, 52
Nybble, 20
Wiederholung, 31
mit nachfolgender Bedingsprufung, 33
mit vorausgehender Bedingungsprüfung,
31
mit Zahlvariablen, 32
ohne Bedingungsprufung, 34
Wiederverwendung, 35
O-Notation, 43, 86
odd parity, 24
Oktalzahlen, 19
Parameterliste, 35
Paritätsberechnung, 79
Paritätsbit, 24
Parser, 61
Primzahl, 31
Priorität, 65
Verarbeitung von Information, 14
Vorbedingungen, 46
Zahlendarstellungen, 19
Zahlenrätsel, 12
Zehnerlogarithmen, 64
Zeichenkette, 12
Zeichensatz, 20
Zusicherungen, 46
zweiarmiges if, 30
Zweierkomplement, 21
Zweierlogarithmen, 64
Herunterladen