zum Skript - Physik in Jena

Werbung
Informatik für Physiker
Lesender:
Prof. Dr. Süÿe
Autoren Kapitel 1:
Silvio Fuchs, Mario Chemnitz
Autoren Kapitel 2:
Mario Chemnitz
Autoren Kapitel 3: Silvio Fuchs, Erik Buchholz, Mario Chemnitz
Grundlage:
Vorlesungsmitschriften der jeweiligen Studenten
Keine Prüfung durch Vorlesenden erfolgt.
Fehler vorbehalten.
22. Juli 2008
Inhaltsverzeichnis
1 Einführung in die Informatik
1.1
1.2
1.3
1.4
1.5
Algorithmen . . . . . . . . . . . . . . . .
Schritte zum Programmieren . . . . . .
Compiler vs. Interpreter . . . . . . . . .
Syntax . . . . . . . . . . . . . . . . . . .
Informationen und Zahlendarstellungen
1.5.1 Zahlendarstellung . . . . . . . .
1.5.2 Codes (Allgemein) . . . . . . . .
1.5.3 Blackcodes . . . . . . . . . . . .
1.5.4 Greycodes . . . . . . . . . . . . .
1.5.5 Ganze Zahlen kodieren . . . . . .
1.5.6 Gebrochene Zahlen kodieren . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
Einführung: Numerische Umsetzung der Newton'schen Iteration
Mittelwert und Standardabweichung . . . . . . . . . . . . . . .
Entropie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Kodierung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.4.1 Datenkomprimierung ohne Datenverlust . . . . . . . . .
2.4.2 Konstruktion eine Human-Codes . . . . . . . . . . . .
2.4.3 Kodierungsarten . . . . . . . . . . . . . . . . . . . . . .
2.4.4 Kryptographie-Arten . . . . . . . . . . . . . . . . . . . .
Sortieralgorithmen . . . . . . . . . . . . . . . . . . . . . . . . .
2.5.1 Distribution-Sort . . . . . . . . . . . . . . . . . . . . . .
2.5.2 Selection-Sort . . . . . . . . . . . . . . . . . . . . . . . .
2.5.3 Insert-Sort . . . . . . . . . . . . . . . . . . . . . . . . .
2.5.4 Bubble-Sort . . . . . . . . . . . . . . . . . . . . . . . . .
2.5.5 Tree-Sort . . . . . . . . . . . . . . . . . . . . . . . . . .
2.5.6 Merge-Sort . . . . . . . . . . . . . . . . . . . . . . . . .
2.5.7 Quick-Sort . . . . . . . . . . . . . . . . . . . . . . . . .
2.5.8 Permutation . . . . . . . . . . . . . . . . . . . . . . . .
Rekursivität . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.6.1 Kellerung . . . . . . . . . . . . . . . . . . . . . . . . . .
Bewertung von Algorithmen . . . . . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
2 Algorithmen zur Bildanalyse
2.1
2.2
2.3
2.4
2.5
2.6
2.7
3 Die Sprache C/C++
3.1
3.2
3.3
3.4
3.5
Geschichtlicher Überblick .
Einfache Syntaxübersicht .
3.2.1 Quellcodebeispiel . .
Der Zeichensatz in C/C++
Der Präprozessor . . . . . .
Kleines Programmbeispiel .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
1
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
3
3
3
3
4
4
4
7
8
8
8
9
10
10
11
11
12
13
14
16
19
21
21
22
23
23
24
24
25
25
25
28
28
31
31
31
31
31
32
32
INHALTSVERZEICHNIS
3.6
3.7
3.8
3.9
3.10
3.11
3.12
3.13
3.14
3.15
3.16
3.17
Einfache Ein -und Ausgaben . . . . . . . . . . . . . . . . . . . . . . .
Datentypen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Gültigkeitsbereiche . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Operationen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Prioritäten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Einfache Steuerstrukturen . . . . . . . . . . . . . . . . . . . . . . . .
Casting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Felder (Arrays) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.13.1 Eindimensionale Felder (Vektoren) . . . . . . . . . . . . . . .
3.13.2 C-Zeichenketten . . . . . . . . . . . . . . . . . . . . . . . . .
3.13.3 Zwei- und mehrdimensionale Felder (Matrizen und Tensoren)
Pointer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.14.1 Adressierung und Dereferenzierung . . . . . . . . . . . . . . .
3.14.2 Pointer-Arithmetik . . . . . . . . . . . . . . . . . . . . . . . .
3.14.3 Pointer und Felder . . . . . . . . . . . . . . . . . . . . . . . .
3.14.4 Pointer-Konstanten und Speicherzugri . . . . . . . . . . . .
3.14.5 Besondere Pointer . . . . . . . . . . . . . . . . . . . . . . . .
Filesysteme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.15.1 Beispiel zum Umgang mit Files . . . . . . . . . . . . . . . . .
3.15.2 Verarbeitung von Files . . . . . . . . . . . . . . . . . . . . . .
3.15.3 Praktische Probleme der Dateiverwaltung . . . . . . . . . . .
Spezielle Spezikationen in C++ . . . . . . . . . . . . . . . . . . . .
3.16.1 Einfache Erweiterungen gegenüber C . . . . . . . . . . . . . .
3.16.2 Objektorientierung . . . . . . . . . . . . . . . . . . . . . . . .
Nützliche Funktionen . . . . . . . . . . . . . . . . . . . . . . . . . . .
2
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
32
33
34
34
35
36
37
37
37
37
38
38
39
40
40
40
41
42
43
43
44
45
45
47
52
Kapitel 1
Einführung in die Informatik
1.1 Algorithmen
Algorithmen sind Abfolgen von Anweisungen, die in endlicher Zeit ausfürbar sind.
1.2 Schritte zum Programmieren
1. Aufgabe
2. Entwurf des Algorithmus
3. Darstellung mittels Pseudokode, Struktogrammen usw.
4. Kodierung in eine dem Computer verständliche Form. z.B Kodierung in C/C++, Java
(a) Erfassen mittels eines Editors
(b) Compilierung: vom Präprozessor zum Compiler
(c) Debugging
(d) in C entsteht jetzt ein le.o also ein Objektcode
(e) Der Linker verlinkt im Programm gebrauchte Funktionen mit den Laufzeitbibliotheken
(f) Ausführung des fertigen Programms und Testung
1.3
Compiler vs. Interpreter
Wärend C einen dem Computerverständlichen Kode mit Hilfe des Compilers produziert, benutz
z.B. Java einen Interpreter der einen Bytecode produziert und dann abgearbeitet wird. Um JavaProgramme auszuführen benötig man eine Java-Laufzeitumgebung. Das C Programm läuf jedoch
schneller, deshalb ist C eine weitverbreitete Entwicklersprache.
Compiler
- Komplettübersetzung des Algorithmus in
maschinennahe Sprache → dann Interpreter
- Schneller als pure Interpretation
- Bsp.sprache: C/C++
- Laufzeitsystem nicht notwendig, aber plattformabhängig
Interpreter
- Zeilenweise Ausführung (Interpretation)
- Übersetzung in eine Art Zwischensprache
- Bsp.sprache: Java, C#
- Laufzeitsystem muss installiert werden, dafür plattformunabhängig
3
KAPITEL 1. EINFÜHRUNG IN DIE INFORMATIK
1.4
4
Syntax
Die Grammatik und Orthographie einer Programmiersprache bezeichnet man als Syntax. Zum
Vergleich werden hier der Syntax einer for-Schleife in Pascal und C gegenüber gestellt:
Pascal: for i:=0 to 100 begin ANWEISUNG; end;
C: for(i=0;i<=100;i++){ANWEISUNG}
Zur anschaulichen Darstellung des Syntax, gibt es verschiedene Möglichkeiten:
1. Bachau-Nauer-Form EBNF
z.B.:
• A = [B] : Option
• A = B|C Auswahl
2. Syntaxdiagramme (PAPs, Struktogramme etc.)
1.5
Informationen und Zahlendarstellungen
Computerintern existieren nur binäre Ausdrücke und Variablen. Die Aufgabe der Benutzerschnittstelle ist es nun, Informationen so zu kodieren, dass die CPU und das Speichersystem binär mit
diesen Arbeiten kann. Dabei lassen sich mit n binären Variablen m= 2n Zustände darstellen.
Aufzeichnungen fehlen!! [...]
1.5.1 Zahlendarstellung
• Darstellung mittels EBCDI-Code:
+454
+454
4 Byte ungepackt
⇒ E4|F 4 F 5 F 4
↓
+⇒C
↓
−⇒D
⇒ 45
| {z4C}
2 Bytes
gepacktes dezimales Zahlenformat
• Darstellung mittels ASCII-Code:
± ∧ 9Byte → 18 Ziern aneinander gereiht (darstellbar?)
Ziernverwaltung - BCD-Zahlen(?)
454
↓
13.4
↓
Umwandlung
45400
&
1340
.
+−
∗/
Operationen
−→ Rückwandlung
KAPITEL 1. EINFÜHRUNG IN DIE INFORMATIK
5
Im wissenschaftlich-technischen Bereich werden Zahlen im B-adischen Zahlensystem
ausgedrückt. Regel:
z=±
n
X
ak · B k
k=−m
k
wobei ak die Ziern und B die Basis ist.
a) Dualsystem
B = 2, ai ∈ {0, 1}
b) Oktalsystem
B = 8, ai ∈ {0, 1, . . . , 7}
c) Dezimalsystem
B = 10, ai ∈ {0, 1, . . . , 9}
d) Hexadezimalsystem B = 16, ai ∈ {0, . . . , 9, A, . . . , F }
Sonderfälle:
n
X
z = ±
ak · B k
Linkspunktzahl (Integer)
z
=
±
k=0
−m
X
ak · B k
Rechtspunktzahl (gibt es nicht)
k=−1
Bsp.: z = 14.75 ist eine dezimale Festpunktzahl, die nun mit Hilfe des Probierverfahrens
in eine binäre Darstellung umgewandelt werden soll:
z = 1 · 23 + 1 · 22 + 1 · 21 + 0 · 20 + 1 · 2−1 + 1 · 2−2 = 1110(.)11
→Abbruch nach erreichen der Genauigkeitsgrenze
Bsp.: z = 0.1
z = 0 · 2−1 + 0 · 2−2 + 0 · 2−3 + 0 · 2−4 + 0 · 2−5 + . . .
→Unendliche Fortsetzung y Abschneiden erforderlich y Konvertierungsfehler
• Algorithmen:
Divisionsrestverfahren
Allgemein:
zdez
=
an · 2n + an−1 · 2n−1 + . . . + a1 · 21 + a0
z1
z2
=
=
..
.
=
z/2
z1 /2
Rest a0
zi /2
Rest ai
⇒
zbin ≡ an an−1 . . . a1 a0
zi+1
Rest a1
Beispiele: zdez = 28
28/2 = 14
14/2 = 7
Rest 0
7/2 = 3
3/2 = 1
Rest 1
1/2 = 0
Rest 1
Rest 0
Rest 1
⇒
11100 ≡ 28
KAPITEL 1. EINFÜHRUNG IN DIE INFORMATIK
Bsp.: bin → hex
6
1
0
1001
| {z} 0010
| {z} ≡ 9 · 16 + 2 · 16 ≡ 92
Bsp.: bin → okt
010 |{z}
010 ≡ 2 · 82 + 2 · 81 + 2 · 80 ≡ 222
10 |{z}
|{z}
Multiplikationsverfahren
Allgemein:
zdez
=
a−1 · 2−1 + a−2 · 2−2 + . . . + a−n · 2−n
z1
=
z·2
Wenn z1 ≥ 1 ⇒ a−1 = 1 ∧ z2 = z1 − 1
Wenn z1 < 1 ⇒ a−1 = 0
..
.
Beispiel: zdez = 0.1
0.1 · 2
0.2 · 2
0.4 · 2
0.8 · 2
(1.6 − 1 =) 0.6 · 2
(1.2 − 1 =) 0.2 · 2
=
=
=
=
=
=
..
.
=
z
0.2
0.4
0.8
1.6
1.2
0.4
→
→
→
→
→
→
0
0
0
1
1
0
da
da
da
da
da
da
<1
<1
<1
>1
>1
<1
→ Periodizität
0001100110011001100 . . .
→ Abbruch nach bestimmter Länge
• Operationen in Bitschreibweise:
Addition:
1+1
1 + 1 ⇒ [0| 1 ][0|1][0|1] . . . =⇒ [0|1][ 0 |1][0|1] . . . ⇒ = 1 0
−−−→
Der Anfangsindex steht auf einer Eins (Zier oben durch Rahmen markiert). 1 + 1 bedeutet
nun Rücke eine Indexstelle weiter und zwar in einer Folge von binären Gruppen ([0|1]).
Das Ergebnis ist die Zier an der Stelle, an der der neue, um Eins erhöhte Index steht (hier
also Null). Wenn bei der Indexverschiebung der ursprüngliche binäre Bereich überschritten
wurde (wie es oben der Fall ist), erhält man einen Übertrag (oben als hochgestelle Zahl vor
dem Ergebnis geschrieben), der nur für weitere Rechnungen eine Rolle spielt (s. Bsp. unten).
Analog erhält man also:
1+0
⇒ [0| 1 ][0|1][0|1] . . . =⇒ [0| 1 ][0|1][0|1] . . . ⇒
1+0
=
0
1
0+1
⇒ [ 0 |1][0|1][0|1] . . . =⇒ [0| 1 ][0|1][0|1] . . . ⇒
0+1
=
0
1
0+0
0+0
=
0
0
⇒ [ 0 |1][0|1][0|1] . . . =⇒ [ 0 |1][0|1][0|1] . . . ⇒
Bsp.:
1
+
1
1
1
0 1 0 1 1
0 1 1 0 1
1 1 0 0 0
KAPITEL 1. EINFÜHRUNG IN DIE INFORMATIK
7
Subtraktion:
1−1
1−1
⇒ [0| 1 ][0|1][0|1] . . . =⇒ [ 0 |1][0|1][0|1] . . . ⇒
←−−
=
0
0
Der Ablauf der Subtraktion ist äquivalent zu dem der oben beschrieben Addition, nur mit
dem Unterschied, dass wir aufgrund des Minusoperators die Indizes nun rückwärts laufen
lassen. Wird nun bei diesem Rückwärtsschritt ein binäerer Bereich überschritten, muss man
mit einem negativen Übertrag weiterrechnen. Analog haben wir also:
1−0
1−0
⇒ [0| 1 ][0|1][0|1] . . . =⇒ [0| 1 ][0|1][0|1] . . . ⇒
0−1
0−1
⇒ [0|1][ 0 |1][0|1] . . . =⇒ [0| 1 ][0|1][0|1] . . . ⇒
0−0
0−0
⇒ [ 0 |1][0|1][0|1] . . . =⇒ [ 0 |1][0|1][0|1] . . . ⇒
=
=
=
0
1
−1
0
1
0
ACHTUNG: Der negative Übertrag ist auf das darauolgende erste Element draufzuaddieren.
Bsp.:
−1
−1
−1
1
1
1
0
0
1
1
1
1
(+) 1
− 0
0
Multipliaktion:
Bsp.:
1 0
1
1
1
1
1
·
1 1
0
1
1
0
1 0 1 1
1 0 1 1 0
1 0 0 0 0 1 0
Perioden:
z = 0.9̄
∧
z · 10 = 9.9̄
⇒
z · 10 − z = 9 = z · (10 − 1) = z · 9
⇒
z=1
Beispiel:
z
z · 26
6
z · (2 − 20 )
=
=
=
⇒
1.5.2
0.111010
111010.111010
111010 ≡ 58
58
z=
63
Codes (Allgemein)
• Repräsentation von Informationen
• Einfache Codes: Blockcodes, alle Codewörter haben gleiche Länge
a) EBCDI-Code (8bit)
Aufteilung eines Bytes in
Bsp.: Ziern
1 1 1 1
1 1
1
1
..
.
0
0
0
|
0
· ·
· ·
· · · ·
{z
} |
{z
}
Zonenteil
Zonenteil
− F0
Vier Einser Zonenbits sind für Ziern charakteristisch.
0
1
0
1
− F9
KAPITEL 1. EINFÜHRUNG IN DIE INFORMATIK
8
b) ASCII-Code (8bit)
Ersten 7bit enthalten Standard-Kodierung und 1bit die variable Länderkodierung.
c) UNICODE (4 Byte)
d) Multi-Byte-Code
Bsp.: UTF8
1.5.3
0
0
0
0
1
0
0
1
Blackcodes
2
0
1
0
3
0
1
1
4
1
0
0
5
1
0
1
6
1
1
0
7
1
1
1
1.5.4 Greycodes
0
0
0
0
Bei
1 2 3 4 5 6 7
0 0 0 1 1 1 1
0 1 1 1 1 0 0
1 1 0 0 1 1 0
zwei benachbarten Codes ändert sich nur ein bit.
1.5.5
Ganze Zahlen kodieren
B-adisches System → B-Komplement-Codes
Häug:
1.bit
0 → positive Zahl ∨ 1 → negative Zahl
Positive Zahlen sind somit direkt darstellbar, negative Zahlen nur als B-Komplement-Code.
Bsp.: 3bit
±z
z* = Code
+0
000
z>0
+1
001
→
⇒
+2
010
+3
011
-4
100
-3
101
(
+z = z ∗ ;
1.bit ⇒ 0
∗
3
−z = z − 2 ; 1.bit ⇒ 1
z ∗ = −z + 23
Komplement zu 2n − 1
Bsp.: −3
z=3
z = −3 + 23 = 5
∗
⇒
Vorteile:
a) Null eindeutig
b) Subtraktion auf Addition zurückführen
−3
↔ 011
↔ 101
↔
101
-2
110
-1
111
KAPITEL 1. EINFÜHRUNG IN DIE INFORMATIK
z
=
=
=
=
=
9
z1 − z2
z1 , z2 > 0
z1 + (−z2 )
z1 + z2∗
z1 + 2n − z2
z1 − z2 + 2n
Bsp.: 5bit Bsp.: Zahlenbereiche
−
0
0
0
−1
−1
−1
1
0
0
0
0
1
0
0
1
1
0
1
1
8
−1
7
0 1 0 0 0
+ 1 1 1 1 1
[1] 0 0 1 1 1
2 Byte = 16 bit
−215 ≤ z ≤ 215 − 1
−32768 ≤ z ≤ 32767
4 Byte = 32 bit
−231 ≤ z ≤ 231 − 1
1.5.6
Gebrochene Zahlen kodieren
Darstellung:
z = Mantisse · BasisExponent
Standard-Code: IEEE Standard 754
→ 32bit Gleitkommazahl:
• Vorzeichen → 1bit
• Exponent → 8bit
• Mantisse → 23bit
→ normalisiert, d.h. 1.bit vorm Komma ist 1; somit eektiv 24bit Mantisse
• Null → Sonderdarstellung
• NAN → Über-/Unterlauf
• Gleitkommazahlen nicht äquidistant (Abstand zwischen oat (4 Byte) Zahlen gröÿer als
zwischen double (8 Byte) Zahlen)
Kapitel 2
Algorithmen zur Bildanalyse
2.1 Einführung: Numerische Umsetzung der Newton'schen
Iteration
Das Newtonsche Iterationsverfahren wird oft zur Nullstellenbestimmung genutzt. Es zeichnet sich
aus, durch hohe Konvergenzgeschwindigkeit, jedoch niedrige Konvergenzsicherheit. Mathematisch
lässt sich dieses wie folgt herleiten:
.
f (x) = f (x0 ) + f 0 (ξ) (x − x0 ) = 0
f (x0 )
⇒ x = x0 − 0
f (ξ)
f (x0 )
→ x1 = x0 − 0
→ mit ξ = x0 macht man einen Fehler! → Ausgleichen!
f (x0 )
⇒
xi+1 = xi −
f (xi )
f 0 (xi )
Anwendung der Newton-Iteration zur Bestimmung der Wurzel aus z ≥ 0:
√
Ausgangsfunktion: z = x → x2 − z = 0 = f (x)
µ
¶
f (xi )
x2 − z
1
z
⇒ xi+1 = xi − 0
= xi − i
=
xi +
f (xi )
2xi
2
xi
Für die nummerische Umsetzung wähle man sich nun ein Auösungsgrenze ε, die als Abbruchbedingung eingesetzt wird
1
· |xi+1 − xi | ≤ ε.
|xi |
double eps=0.01,z,X,Y;
//X->x_i; Y->x_i+1
scanf("%lf",&z);
Y=z;
do
{
X=Y;
Y=(X+z/X)/2.0;
}while(fabs(Y-X)>=eps);
//Absolutwertabfrage
Ein weiteres Iterationsverfahren ist das sog. Lernverfahren, welches man bspw. zu Mittelwertbildung verwenden kann
1
n
x̄n +
xn+1 .
x̄n+1 =
n+1
n+1
10
KAPITEL 2. ALGORITHMEN ZUR BILDANALYSE
2.2
11
Mittelwert und Standardabweichung
Der Mittelwert über die Farbwerte eines Bildes ist ein Maÿ für dessen Helligkeit, die Standardabweichung demenstprechend für dessen Kontrast. Wenn mit g(i, j) die Grauwerte einer Bildes
bezeichnet werden, so ergibt sich für den Mittelwert
ḡ =
X
1
g(i, j)
dimx · dimy i,j
und für die Standardabweichung
s
Sg =
X
1
2
(g(i, j) − ḡ) .
dimx · dimy − 1 i,j
Um die Standardabweichung zu Berechnen wird in der Regel eine zweite Schleife benötigt, da
vorerst der Mittelwert brechnet werden muss. Unter Akzeptanz eines geringen Fehlers lässt sich
die Berechnung beider Werte auf eine Schleife zurückführen:
x̄ :=
1X
xi
n i
⇒
S2
=
=
=
=
=
=
2.3
1 X
2
(xi − x̄)
n−1 i
¢
1 X¡ 2
xi − 2xi x̄ + x̄2
n−1 i
!
Ã
X
X
nX
1
2
2
1
xi − 2x̄
xi + x̄
n−1
n i
i
i
!
Ã
X
1
2
2
xi − 2x̄nx̄ + nx̄
n−1
i
!
Ã
X
1
2
2
xi − nx̄
n−1
i
Ã
!
³ X ´2
X
1
1
x2i −
xi
¤
n−1
n i
i
Entropie
Die Entropie wird auch in der Informatik als Maÿ für die Ordnung eines Systems genutzt. Auf die
Bildanalyse angewandt, erlaubt sie Aussagen über die mögliche verlustfreie Komprimierungsrate
eines Bildes. Die verschiedenen Zustände entsprechen dann bspw. den Grauwerten des Bildes, aus
denen man ein Histogramm entwickeln kann (Zustand i → Häugkeit pi mit i = 0, . . . , 255). Aus
diesem Grauwerthistogramm lässt sich die Entropie H0 dann näherungsweise berechnen. Erhält
man bspw. den Entropiewert H0 = 3.9 (bit), bedeutet dies, bei einer Grauwertkodierung von 8
bit, eine Komprimierungsmöglichkeit des Bildes von 50%.
Die Entropie ist wie folgt deniert
H0 = −
n
X
pi log2 pi ,
i=1
wobei die pi die jeweiligen Wahrscheinlichkeiten eines Grauwertes sind, also Anzahl des Grauwertes
x geteilt durch die Gesamtanzahl der Grauwerte. Da der Logarithmus aus Null nicht deniert ist,
wird eine Konvention getroen
0 · log2 0 =: 0.
KAPITEL 2. ALGORITHMEN ZUR BILDANALYSE
12
Im einfachsten Fall hat man bei der Arbeit mit Grauwerten ein Zwei-Zustandsbild (nur schwarze
und weiÿe Werte), welches sich auf eine 0-1-Verteilung zurückführen lässt. Für die Entropie ergibt
sich dann für m Grauwerte:
0 ≤ H0 ≤ log2 m
Im schlimmsten Fall rechnet man mit einer idealen Gleichverteilung der Werte →
In diesem Fall ergibt sich für die Entropie:
pi =
1
m
∀i.
m
X
1
1
1
H0 = .
log2
= − log2
= log2 m = H0
m
m
m
i=1
Speziell fà 14 r die Entropieberechnung eines Bildes deniert man sich zwei Zufallsvariablen X
und Y , wobei diese die jeweilige Menge der xi bzw. der yi darstellen.
Spaltenweise Entropie
H0 (Y ) =
X
P [yj ] log2 P [yj ]
j
Zeilenweise Entropie
H0 (X) =
X
P [xi ] log2 P [xi ]
i
Bedingte Entropie von Y bzgl. X
H0 (Y |X) =
=
−
−
XX
j
i
j
i
XX
P [xi ]P [yj |xi ] log2 P [yj |xi ]
P [yj , xi ] log2 P [yj |xi ]
Es gilt zudem
H(X, Y ) = H(X) + H(Y |X) = H(Y ) + H(X|Y ).
An dieser Stelle wird eine weitere Gröÿe als Maÿ für die Transportqualität eingeführt. Diese Gröÿe
I(X|Y ) = H(X) − H(X|Y ) = H(Y ) − H(Y |X)
heiÿt mutual information oder auch Transinformation. Gilt I(X|Y ) = H0 (Y ) sagt man der Kanal sei ok und bei I(X|Y ) = 0 herrsche Übertragunschaos. Man hat zudem einen Klassikator
(Zuweisungsfunktion), den man so entwerfe, dass die Transinformation maximal wird.
Bsp.:
(∗)
Buchstabe A −→ Buchstabe A
Klassikator (∗) bildet optimal identisch ab
y optimale Transinfo.
Weitere Informationen zur Entropie (u.a. Dierentielle Entropie) entnehme man bitte dem Skript
Grundbegrie der Infromationstheorie von Prof. Süÿe.
2.4
Kodierung
• Repräsentation von Informationen
• Ziele:
Eziente Algorithmen
Datensicherheit → Kryptographie (Verschlüsselung)
Datensicherheit → Minimierung von Fehlern bei Datenübertragungen (Bsp.: GreyCode)
Datenkompression → ohne/mit Verlust
KAPITEL 2. ALGORITHMEN ZUR BILDANALYSE
2.4.1
13
Datenkomprimierung ohne Datenverlust
Ausgangssitution:
A
B
=
=
Quell-Alphabet
{a1 , a2 , . . . , an }
{b1 , b2 , . . . , bn }
Code-Alphabet
Zu kodieren ist eine Zeichenkette/Zahlenfolge ai1 , ai2 , ai3 , . . . (Bsp.: Grauwerte eines Bildes). Kodierung bedeutet nun, dass wir jedem Element ai aus dem Quell-Alphabet ein Codewort zuweisen.
Eine Zeichenkette o.ä. wird somit in eine Folge von Codeworten umgesetzt, aus der dich die ursprüngliche Zeichenkette wiederherstellen lassen sollte.
Dention eines I-Codes:
Ein Code heiÿt I-Code, wenn kein Codewort Präx eines anderen Codewortes ist.
Für einen I-Code muss die Ungleichung von Kraft gelten:
k −d1 + k −d2 + . . . + k −dn ≤ 1
mit di als Länge des i-ten Codewortes. Bsp.: Blockcodes
k = 2, di = 8 ∀i
⇒
2−8 · (256) = 28 · 2−8 = 1
k1 = 0, k2 = 00 k3 = 000
⇒
2−1 + 2−2 + 2−3 < 1
Bsp. für präxbelasteten Code:
a = 00, b = 10, c = 101, d = 110
Eine zu verschlüsselnde Symbolkette sei nun 10110. Diese lässt sich mittels des gegebenen CodeAlphabets (a,b,c,d) nicht eindeutig verschlüsseln y Mehrfachinterpretation möglich und somit
kein Code!
Bsp. für Code, der kein I-Code ist:
ai = {1, 10, 100, 1000, 10000, . . .}
Die Einsen sind in diesem Beispiel-Code eine Art Trennzeichen. Der Code ist somit präxfrei.
Ein Beispiel für einen nicht-binären I-Code ist der Morse-Code.
Im Folgenden arbeiten wir Hilfe eines binären Code-Alphabets, d.h. B = {0, 1}. Für eine geeignete Einschätzung einer optimalen Kodierung, werden an dieser Stelle Elementen ai aus dem
Quell-Alphabet (Informationsquelle) Wahrscheinlichkeiten P (ai ) = pi zugeordnet. Gedächtnisfreie
Informationsquellen zeichnen sich durch folgenden Zusammenhang aus
P (ai1 . . . ain ) = P (ai1 ) · . . . · P (ain ).
Mit Hilfe der Wahrscheinlichkeiten lä sich zudem die mittlere Länge aller Codewörter errechnen
L=
n
X
i=1
di · pi =
X
di · P (ai ),
i
welche ein Maÿ für die minimal mögliche Codelänge zu sein scheint. An dieser Intention angelehnt,
schein die Denition des Human-Codes zu sein.
KAPITEL 2. ALGORITHMEN ZUR BILDANALYSE
14
Dention eines Human-Codes:
Ein Human-Code nennt man einen I-Code mit kleinst möglicher mittlerer Länge L. Bsp.:
ai
pi
bi
Ungleichung von Kraft ist erfüllt:
⇒ L
2.4.2
=
=
=
{A, B, C, D, E, F }
{0.4, 0.2, 0.1, 0.1, 0.1, 0.1}
{0, 10, 1100, 1101, 1110, 1111}
1
1
1
+ 2 + 4 ≤1
1
2
2
2
= 0.4 · 1 + 0.2 · 2 + 0.4 · 4 = 2.4
Konstruktion eine Human-Codes
Um eine Human-Code zu entwickeln, ordnen wir zuerst o.B.d.A das Quell-Alphabet derart um,
dass für die zugehörigen Wahrscheinlichkeiten gilt p1 ≥ . . . ≥ pn . Die weitere Umordnung erfolgt
rekursiv. Rekursionsaufruf:
a1 . . . an−1 , an
| {z }
a1 . . . an−1,n
→ p1 . . . pn−1,n = pn−1 + pn
Nun ordnet man die Elemente ai um, sodass gilt p1 ≥ . . . ≥ pn−1 , und fährt mit dem nächsten
Aufruf fort (?). Als Abbruchbedingung hat man dann, dass die Anzahl der ai gleich Eins sei, sodass
man schlussendlich nur ein Element a1,2,...,n−1,n mit der Wahrscheinlichkeit p1,2,...,n−1,n hat. Die
Wiederaufsplittung, welche den eigentlichen Human-Code ergibt, erfolgt in folgender Weise:
p2,3
0 .
p2
↓
10
→1
&1
p3
↓
11
Bsp.: 1. Umordnung
A
0.4
E
0.2
B
0.1
C
D
0.1 0.1
A
0.4
{D, F }
0.2
E
0.2
B
C
0.1 0.1
A
0.4
{B, C}
0.2
{{D, F }, E}
0.4
A
0.4
{A, {B, C}}
0.6
{{D, F }, E}
0.4
{D, F } E
0.2
0.2
F
0.1
1.Schritt
2.Schritt
3.Schritt
{B, C}
0.2
4.Schritt
KAPITEL 2. ALGORITHMEN ZUR BILDANALYSE
15
2. Wiederaufsplittung
{A, {B, C}} {{D, F }, E}
→0
→1
{{D, F }, E}
A
{B, C}
1
00
01
A
{B, C}
{D, F }
00
01
10
A
{D, F }
E
00
10
11
A
E
B
00
11
010
E
11
B
010
C
011
C
011
D
100
F
101
Die eingerahmten Code-Elemente bilden einen Human-Code.
Ein weiteres Verfahren, dass oft in einem Hufmann-Code mündet, ist das Shannon-Fano-CodeVerfahren. Diese Verfahren schlieÿt nach und nach die Werte hoher Wahrscheinlichkeit aus. Bei
Werten gleicher Wahrscheinlichkeit kommt die Intervall-/Feldhalbierung zum Einsatz. Ausgangssituation ist wieder ein nach den Wahrscheinlichkeiten sortiertes Quell-Alphabet. Bsp.:
A
0.4
1
|
|
|
|
|
|
|
|
E
0.2
B
0.1
C
0.1
D
0.1
F
0.1
0
0
0
00
00
1.Schritt
0
0
01
011
| 01
|
| 010
|
|
|
|
|
|
|
001
0011
| 001
|
| 0010
| 00
|
| 000
|
|
2.Schritt
3.Schritt
4.Schritt
Die eingerahmten Code-Elemente bilden anscheinbar eine Art Human-Code. Nun wissen wir
aber L → Lmin ≥ 1. Ist A = {a1 , a2 } binär, so gilt fà 41 r die dazugehörigen Wahrscheinlichkeiten
p1 + p2 = 1. Somit folgt
Lmin = 1 · p1 + 1 · p2 = p1 + (1 − p1 ) = 1
aber L > 1.
Der vermeintliche Human-Code ist also gar keiner. Was können wir nun tun? (Folgende Sym-
boliken können leider nicht ausformuliert werden, da ich sie selber nicht verstehe.
Wer helfen kann, melde sich bitte!)
Annahme: Unabhängige Informationsquelle
Bsp.: Human-Code für S 2
Symbol
S
P (S)
Symbol
S2
00
01
P (S 2 ) 0.81 0.09
0 10
0
0.9
110
1
0.1
10
0.09
11
0.01
111
Lmin = 0.81 · 1 + 0.09 · 2 + 0.09 · 3 + 0.01 · 3 = 1.29 ≥ 1
Allerdings:
bit
1.29
= 0.645
2
Symbol
aus S
KAPITEL 2. ALGORITHMEN ZUR BILDANALYSE
16
und somit Human-Code (?). Gehts vielleicht noch besser? → Shannon
Entropie:
n
X
H(S) = −
pi log2 pi
i=1
→ für Bsp.: H(S) = 0.469 bit
Für gedächtnisfreie Informationsquellen gilt:
H(S k ) = k · H(S)
Theorem:
H(S) ≤ Lmin ≤ H(S) + 1
Anwendung für gedächtnisfreie Informationsquellen:
H(S k ) ≤ Lmin (S k )
k · H(S) ≤ Lmin (S k )
H(S) ≤
Lmin (S k )
k
≤ H(S k ) + 1
≤ k · H(S) + 1
1
≤ H(S) +
k
↓k→∞
= H(S)
2.4.3
Kodierungsarten
• Human
S, S 2 , S 3 , S 4 → H(S) = 0.1 mit 0.1 ≤ Lmin ≤ 1.1. Wir wissen aber nun Lmin ≥ 1, somit
haben wir 1 ≤ Lmin ≤ 1.1, was eine sehr genaue Angabe der benötigten Kompressionsgröÿe
für H(S) < 1 zulässt. Der Code sei also gut für H(S) < 1.
Um dies zu realisieren hatte man die Idee, kein Codebuch mit zu übertragen. Dies ist bspw.
der Fall beim
• Wörterbuchverfahren
Verfahren die nach diesem Prinzip arbeiten sind die Verfahren von Lempel, Ziv (LZ) und
Lempel, Ziv und Welch (LZW), welche u.a. bei dem Bildformat .gif angewandt werden. Bsp.:
Annahme: Grundcode besteht aus 256 Dualzahlen (bspw. ASCII - 8bit)
Zu kodieren: Symbolkette: ABABABACD...
Codebuch
0
..
.
255
2. 256
5. 257
8. 258
..
.
Sendet:
1. A (∗)
3. B
6. AB
9. ABA
..
.
Sender
..
.
..
.
..
.
AB
BA
ABA
..
.
8bit
9bit
9bit
9bit
..
.
Codebuch
0
..
.
255
4. AB
7. BA
10. ABA
..
.
Empfängt:
A
B
AB
A B A (∗∗)
..
.
Empfänger
...
...
...
8bit
ASCII
Grundcode
9bit
9bit
9bit
Ende Codebuch
8bit
9bit
9bit
9bit
...
KAPITEL 2. ALGORITHMEN ZUR BILDANALYSE
17
Die eingerahmten Ordnungsnummern kennzeichnen die Reihenfolge des Übertragungsvorgangs. Der Übertargungsvorgang läuft prinzipiell wie folgt ab:
1. Senden des ersten Symbols mit n bit
2. Erweiterung des Codebuchs des Senders (s. auch (∗))
3. Gleichzeitig empfängt der Empfänger das mit n bit kodierte Segment und vergleicht es
mit seinem Codebuch.
4. Kommt kein vergleichbarer Eintrag im Empfänger-Codebuch vor, versucht er das empfangene Segment zu interpretieren, indem er den vordersten Buchstaben hinten nochmal
anhängt.
5. Kann er es dekodieren, schreibt er einen neuen Eintrag in das Codebuch, welcher das
vorher empfangenen Segmente und das aktuelle empfangene Segmente enthält (s. dazu
Schrittfolge 3. zu 4.).
Anmerkungen:
(∗) Der gesendete Buchstabe und der in der Symbolkette Darauolgende werden in das Codebuch des Senders augenommen, sofern diese Kombination noch nicht im Codebuch des
Senders enthalten ist.
(∗∗) Ausnahme: Das letzte A wird gleich anhangen (also beim Empfangen erkannt), weil es
sich am Anfang der Zeichenkette bendet, nicht weil die Kombination schon im Codebuch
steht. Die Kombination ABA wird erst im Nachhinein in das Codebuch des Empfängers übernommen. Hier wird also eine Annahme des Empfängers getroen, die scheinbar funktioniert.
Wir haben somit ein selbstlernendes System.
• Arithmentische Kodierung
Huf f manA B
C
D E
F
Bsp.:
(Quell-Alphabet mit Wahrscheinlichkeiten)
0.4
0.1 0.1 0.1 0.2 0.1
Kontruktion einer Art Histogramm:
A
B
C
D
E
F
→
→
→
→
→
→
[0.0; 0.4]
[0.4; 0.5]
[0.5; 0.6]
[0.6; 0.7]
[0.7; 0.9]
[0.9; 1.0]
Zu kodierende Symbolkette: EBAD...
1. E ∈ [0.7; 0.9] → 0.8 → Zier 8 + Länge 1
2. EB ∈ [A; B]
mit A = 0.7 + 0.4 · (0.9 − 0.7) = 0.78 und B = 0.7 + 0.5 · (0.9 − 0.7) = 0.8
→ Zier 78 + Länge 2
3. EB ∈ [A; B]
mit A = 0.78 + 0 · (0.8 − 0.78) = 0.78 und B = 0.78 + 0.4 · (0.8 − 0.78) = 0.788
→ Zier 78 + Länge 3
→ Untescheidung von 2. durch Länge
4. usw. für längere Zeichenketten
Für das Beispiel EBA wird die Zahl 78 und separat die Länge 3 übermittelt. Die jeweils
neuen Intervallgrenzen ergeben sich aus folgenden Formeln:
neu
alt
A = Galt
· (Galt
u + Gu
o − Gu )
KAPITEL 2. ALGORITHMEN ZUR BILDANALYSE
18
neu
alt
B = Galt
· (Galt
u + Go
o − Gu )
mit Galt
u als untere Grenze des Wahrscheinlichkeitsintervalls der vorherigen (schon kodierten)
Gruppierung, Galt
o als obere Grenze des Wahrscheinlichkeitsintervalls der vorherigen (schon
kodierten) Gruppierung, Gneu
als untere Grenze des Wahrscheinlichkeitsintervalls des neuen
u
(noch zu kodierenden) Symbols und Gneu
als obere Grenze des Wahrscheinlichkeitsintervalls
o
des neuen (noch zu kodierenden) Symbols.
Für längere Zeichenketten wird eine rieÿige Zahl entstehen, welche nur mittels partieller
Speicherung bearbeitet und übermittelt werden kann. Bspw. kann sie mit Hilfe mehrerer
long int's gespeichert werden:
1.long 78124 . . . + 2.long 93842 . . . → Zusammensetzung zum Gesamtcode
• Burrows-Wheeler-Kompressionsalgorithmus
Bsp.: Zeichenkette: BESEN
1. Rotieren
N
E
S
E
2. Sortieren
1.
2.
3.
4.
5.
B
N
E
S
B E
E N
E S
N B
S E
(∗)
E
B
N
E
S
B
E
E
N
S
E
B
N
E
E
N
S
B
E
S
E
B
N
S
B
E
E
(∗∗)
3. Letzte Spalte (∗) kodieren
4. Aus letzter Spalte (∗) wird erste Spalte (∗∗) durch Sortieren erzeugt
5. Einstrittspunkt mit abspeichern (NS ...)
6. Move-to-Front-Stufe
Zu kodieren: NSBEE
E N S
⇒ Codenr. für ersten Buchstaben N: 2
1 2 3
N B E S
Nun N nach vorne holen:
⇒ Codenr. für S: 3
0 1 2 3
Alphabetisch sortiert:
B
0
Nun S nach vorne holen:
S
0
N
1
B
2
E
3
⇒ Codenr. für B: 2
Nun B nach vorne holen:
B
0
S
1
N
2
E
3
⇒ Codenr. für E: 3
E B S
0 1 2
B E E
2 3 0
N
3
⇒ Codenr. für E: 0
Nun E nach vorne holen:
=⇒ kodiert:
N
2
S
3
Das gewünschte Ziel dieses Verfahrens ist ein Ausdruck, bestehend aus Ziern und möglichst
vielen Nullen, bspw. 23200000004000000 . . .. Dies ist eine Folge, die noch mit einem beliebigen Kompressionsalgorithmus zu kodieren ist. Viele Nullen sind erwünscht, da diese eine
Art Ordnung erzeugen, was eine geringe Entropie und somit eine hohe Kompressionsrate zur
Folge hat.
KAPITEL 2. ALGORITHMEN ZUR BILDANALYSE
19
• Kodierung generell
Ein Kompressionsalgorithmus beinhaltet ein Vorhersagemodell und einen universellen Kodierer → Prädiktor - Korrektor. Ein Beispiel für einen der einfachsten Prädiktoren ist der
Identische Operator I, der u.a. bei dem PNG-Bildformat Anwendung ndet.
Weitere Kompressionsarten:
Verlustfreie Kompression
Bsp.: JPEG-, MPEG-Bildformat → Clusterbildung
Transformationen
Bsp.: JPEG → DCT oder Wavelets (+Quantisierung,+Human)
Vektorquantisierung
Bsp.: TIFF-, EPS-Bildformat
Fraktale Kodierung
Approximation
2.4.4
Kryptographie-Arten
Die Kryptographie hat zum Ziel die sichere Verschlüsselung von Daten.
a) Kryptographie mit privatem Schlüssel
Standards: DES, AES
Bsp.: Symmetrisches Verfahren mit privatem Schlüssel
Kodierung:
v =u+k
mit v als verschlüsselte Nachricht, u als ursprüngliche Nachricht, k als Key und + als bspw.
bitweise Addition (XOR).
Dekodierung:
v+k =u+k+k =u
Diese Verfahren wird als one-time-pad (G.S.Vernam 1917) bezeichnet, da nur bei einmaliger
Anwendung eines zufällig gewählten Schlüssels k dieser auch absolut sicher ist. Ein Problem
dabei besteht in der Anwendung nur eines Schlüssels zur Kodierung eines ganzen Bildes. Dies
hat bei zeilenweise Verschlüsselung zur Folge, dass die Kodierung nach unten hin immer unsicherer wird. Ist der Key einmal gefunden, lässt sich die Kodierung problemlos zurückführen.
Bsp.: ENIGMA
• Sehr sicheres Verschlüsselungssystem aus dem 2.Weltkrieg
• Verwendung eines Schlüssels, der sich im Laufe der Verschlüsselung noch verändert
y für jeden Text völlig anderer Schlüssel
b) Public-Key-Kryptographie
Wichtig: Einweg-Funktion, Einweg-Hashfunktion H(M)
h = H(M )
mit M als Nachricht und h als Hashwert. Zudem gilt:
(a) Zu gegebenem M ist es leicht, h zu berechnen (ca. 1ns Laufzeit)
(b) Zu gegebenem h ist es schwer, ein M zu berechnen (ca. 2-3 Tage Laufzeit)
(c) Zu gegebenem M ist es schwer, eine Nachricht M' zu berechnen mit H(M)=H(M')
KAPITEL 2. ALGORITHMEN ZUR BILDANALYSE
20
Bsp.: Passwort-Verwaltung am PC
Intern legt das System das Passwort als h ab und bedient sich zudem eines weiteren Tricks,
nämlich der SALT-Liste. Die SALT-Liste ist eine Liste mit weiteren Dekodierungsschlüsseln,
aus der das System sich einen auswählt um h noch weiter zu kodieren und M somit noch mehr
abzusichern.
Bsp.: RSA-Algorithmus
Dieser wurde 1978 von Rivest, Shamir und Adelman erfunden.
Kodierung:
• Wähle zufällig zwei groÿe Primzahlen p und q
• Bilde n = p·q und nutze 0, 1, . . . , n−1 (Folge von bits) als Quellalphabet (Bsp.: n = 3
immer 3bit Blöcke → Alphabet: 0 . . . (23 − 1))
→
• Wähle zufällig eine Zahl i mit 0 ≤ i ≤ n − 1, die relativ prim zu φ(n) ist, wobei φ(n) die
Euler'sche φ-Fkt. ist (φ(n) ≡ Anzahl der Zahlen kleiner n, die relativ prim zu n sind)
→ φ(p) = p − 1,
φ(q) = q − 1
→ φ(n) = φ(p · q) = (p − 1) · (q − 1)
⇒ Wir können nun eine Zahl j = 0, . . . , n − 1 nden, sodass i · j ≡ 1(mod φ(n)).
• Kodiere ein Symbol v = 0, 1, . . . , n − 1
w ≡ vi
mod n
und sende w statt v!
Dekodierung:
v ≡ wi
mod n
M kann somit leicht berechnet werden.
Der Algorithmus arbeitet folglich mit zwei Schlüsseltypen, dem public key (hier: n und i), der
öentlich zur Verfügung steht, und dem private key (hier: j, p und q), den nur der Empfänger
benötigt. Diese Verschlüsselung kann nur geknackt werden, wenn φ(n) gefunden wird. Dies ist
nur möglich wenn man n = p · q so lösst, dass φ(n) = (p − 1) · (q − 1). Dies ist jedoch ein
technisches Problem, da bisherige Primzahlfaktorisierungsalgorithmen sehr langsam sind und
zudem p und q sehr groÿ gewählt wurden.
Bemerkung:
I)
lim
k→∞
π(k) · log k
=1
k
wobei π(k) der Anzahl der Primzahlen kleiner k entspricht.
→ π(1050 ) ≈
1050
≈ 5 · 1047
log 1050
y Es gibt auf jeden Fall genug Primzahlen, um die Sicherheit dieser Kodierung zu gewährleisten.
II) Generierung von Primzahlen:
Wähle zufällig 50 stellige Zahlen (hohe Primzahlwahrscheinlichkeit) und teste diese auf
Primzahleigenschaften. Hierfür existieren bereits einige sehr schnelle Algorithmen.
KAPITEL 2. ALGORITHMEN ZUR BILDANALYSE
21
III) Derzeitiger Standard: n → 1024bit ⇒ p, q → 512bit
c) Digitale Signaturen
Kodierung:
• Wende auf Text eine Einweg-Hash-Fkt. an und füge h dem Text hinzu
• Kodiere den Hashwert h mit einem privatem Schlüssel
• Sende Text in Form von dem verschlüsselten h zum Empfänger
Dekodierung:
• Dekodiere h mit public key
• Berechne h und vergleiche
→ Umgekehrter RSA-Algorithmus
2.5
Sortieralgorithmen
Beispiele:
• Distribution-Sort
• Selection-Sort
• Insert-Sort
• Bubble-Sort
• Quick-Sort
• Merge-Sort
• Tree-Sort
• Heap-Sort
2.5.1
Distribution-Sort
(Counting-Sort, Histogramm-Methode)
Idee: Adressraum (Anzahl theoretisch möglicher Zustände) ←→ Anzahl tatsächlich zu sortierender
Zustände
Voraussetzung: Adressraum sei "klein"
Bspw. Adressraum aus 256 verschiedenen Zuständen
0
0
0
0
0
0
t t t t t t ...
0 1 2 3 4 5
0
t
255
Bsp.: 3 3 5 7 255 255 0 0
,→ 2x 3, 1x 5, 1x 7, 2x 255, 2x 0
⇒ Anlegen eines Histogramms / Adressraums:
2
0
0
2
0
1
0
1
0
t t t t t t t t t ...
0 1 2 3 4 5 6 7 8
2
t
255
→ nun in richtiger Reihenfolge (sortiert) ausgeben: 0 0 3 3 5 7 255 255 → fertig!
Problem: Adressraum muss klein sein! Da hier nur Arbeit mit int (256x 4 Byte) ist diese Bedingung
erfüllt. Aber bei Arbeit mit gröï¾ 21 eren Objekten erfolgt Stack-overow / Speicherplatzüberschreitung.
Bsp.: Grauwerte eines Bildes
KAPITEL 2. ALGORITHMEN ZUR BILDANALYSE
• Festlegung von Clustern
• Sortierung der Grauwerte in dem Cluster
• Übertragung des clusterzentralen Pixels in ein neues Bild
• Cluster wandern lassen
Abbildung 2.1: Arbeitsschema des Distribution-Sort-Algorithmus
⇒ Medianlterung (Median: Der bzgl. der Sortierung in der "Mitte" stehende Wert)
Bsp.:
• 5 3 200 7 16
• Sortierung
• 3 5 7 16 200
Der umrahmte Wert wird als Median bezeichnet.
Bsp.: Zeichenkette mit max. fünf Zeichen, Kleinbuchstaben und Leerzeichen
• Zustände: 26 Kleinbuchstaben + Leerzeichen = 27 Zustï¾ 21 nde pro Zeichen
• Adressraum: 275 max. mï¾ 21 gliche verschiedene Zustände
Zustand ≡ Zeichenkette ←→ Nummer
→ 27 adisches Zahlensystem
a
b
c
..
.
z
2.5.2
→
→
→
→
→
→
0
1
2
3
..
.
27
Selection-Sort
Bsp.: Z A D B E
1. Suche Maximum und Vertausche mit dem hintersten Wert des aktuellen Bereichs
→EADBZ
2. Suche Maximum des um Eins kleineren Bereichs und Vertausche
→BADEZ
→BADEZ
→ A B D E Z → Fertig!
22
KAPITEL 2. ALGORITHMEN ZUR BILDANALYSE
2.5.3
Insert-Sort
Bsp.: Z A D B E
1. Nehme erstes Element und Vergleiche mit Folgewert
→ A Z D B E → Umrahmte Werte bereits sortierte Liste
2. Nun nehme Folgeelement und füge es an richtiger Position in die sortierte Liste ein
→ ADZ BE
→ ABDZ E
→ A B D E Z → Fertig!
2.5.4
Bubble-Sort
Bsp.: Z A D B E
1. Nehme erstes Element und Vergleiche stückweise mit den Folgeelementen
→ Z↔A D B E
→ A Z↔D B E
→ A D Z↔B E
→ A D B Z↔E
→ A D B E Z → Erster Durchlauf fertig!
2. Nächster Durchlauf mit dem nun ersten Element
→ A↔D B E Z
→ ...
3. So oft wiederholen bis Liste wirklich sortiert!
Bsp. in C/C++:
int i, n, anz, result;
char namen[20][16], hname[16];
... //Eingabe von n<=20 Strings
do {
anz=0; //Prï¾ 12 fvariable
for(i=0; i<n-1; i++)
{
if(strcmp(namen[i],namen[i+1])>0)
{
++anz;
strcpy(hname, namen[i]);
strcpy(namen[i], namen[i+1]);
strcpy(namen[i+1],hname);
}
}
}while(anz>0)
Anwendungsbeispiel:
Aufgabe: Der zu sortierende Datenbestand besteht aus Datensätzen, die enorm viel Speicher benötigen.
Wunsch: Sortieren ohne die Datensätze zu bewegen.
Lösung: Lege ein Feld von Pointern an, diese zeigen jeweils auf die Datensätze. Sortierung durch Umordnung der Pointer.
Ordnung
k
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
Datensatz a[k] A S O R T I N G E X A M P
L
E
Pointer
p[k] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
Nach Sortierung:
Pointer p[k] 1 11 9 15 8 6 14 12 7 3 13 4 2 5 10
23
KAPITEL 2. ALGORITHMEN ZUR BILDANALYSE
2.5.5
24
Tree-Sort
Bsp.: Zeichenkette: ASORTINGEXAMPLE
Ziel: Anlage eines Baumes (tree), der auf einfach Art und Weise ausgewertet werden kann und
somit die korrekte Sortierung zurückliefert.
1. Erster Buchstabe (A) bildet den ersten Knoten
2. Vergleiche zweiten Buchstaben (S) mit dem Knoten (A)
3. Je nach Vergleichsergebnis Buchstaben dementsprechend an den Baum hängen
• Buchstabe ≤ Knoten ⇒ Buchstabe wird ein neuer linker Knoten
• Buchstabe > Wurzel ⇒ Buchstabe wird ein neuer rechter Knoten
• Ist die zugewiesene Position an dem Baum schon durch einen Knoten besetzt, wird der
neue Buchstabe wiederrum mit diesem verglichen und dementsprechend weitergeschoben
Am Bsp.: A<S⇒Buchstabe S wird eine Knoten auf der rechten Seite
4. Vergleiche dritten Buchstaben (O) mit dem ersten Knoten (A): A<O⇒Soll ein Knoten auf
der rechten Seite werden
5. Rechte Seite schon besetzt (durch S) y Vergleiche neuen Buchstaben mit zweiten Knoten:
S>O⇒O wird linker Knoten bzgl. des Knotens S
6. Vergleiche vierten Buchstaben (R) mit dem ersten Knoten (A)
7. . . .
Ergebnis:(funktioniert noch nicht)
Der zweite Teil der Sortierung erfolgt nun durch die geeignete Ausgabe. Diese wird rekursiv vorgenommen (s. dazu 2.6):
1. Ausgabe des linken Astes
2. Ausgabe des Knotens
3. Ausgabe des rechten Astes
2.5.6
Merge-Sort
Merge-Sort ist ein rekursiver Algorithmus (s.a. 2.6), der hier nur kurz in Form eines Pseudo-Codes
wiedergegeben werden soll:
Modul Sort(Liste)
IF (n>1) THEN
Sort(erste Listenhaelfte)
Sort(zweite Listenhaelfte)
Mische beide zusammen
Schema zum Mischen der beiden sortierten Listen:
Teilliste 1
A
1.Vgl.
←→
2. Vgl.
Teilliste 2
A
C
D
←→
Ausgabe:A
2. → C > A Ausgabe:A
.%
3.Vgl.
1. → A = A
B
...
F
3. → C > B
Ausgabe:B
KAPITEL 2. ALGORITHMEN ZUR BILDANALYSE
2.5.7
25
Quick-Sort
Der Quick-Sort ist ein rekursiver Algorithmus (s.a. 2.6), der hier wiederum nur an einem Beispiel
gezeigt werden soll.
Bsp.: Quick-Sort mit Median-Partitionierung
void quicky(double a[], int l, int r)
{
int i;
if(r>l)
{
i=partition(a,l,r);
quicky(a,l,i-1);
quicky(a,i+1,r);
}
}
Schema zur Funktion partition
Ursprüngliche Liste
5
Nach Medianndung 5
Median: 6
1
1
3
3
7
6
9 6 8
9 7 8
Die Aundung des Medians ist jedoch im Vgl. sehr zeitaufwendig. Aus diesem Grund sind andere
Quick-Sort Algorithmen günstiger.
2.5.8
Permutation
Die Permutationen zerfallen in Zyklen:
µ
1 2 3 4
3 2 1 4
5
5
¶
= (1 3) · (2) · (4) · (5)
Die Sortierung des obigen Beispiels zerfällt in folgende Zyklen:
p[k] → (1)(2 11 13)(3 9 7 14 5 8 12 4 15 10)(6)
Mit Hilfe dieser Zyklen, kann man die Datensätze ezient (d.h. maximal einmal) bewegen und
sortieren, wobei die Einerzyklen erhalten bleiben. Der Rest wandert gemäÿ der obigen Zyklen.
2.6
Rekursivität
Fast jeder Algorithmus, der iterativ (in Schleifen etc.) ausgeführt werden kann, kann auch rekursiv
geschrieben werden. Rekursive Funktionen arbeiten nach dem Prinzip "Teile und Herrsche". Es
sind Funktionen, die sich, bis eine bestimmte Abbruchbedignung erfüllt ist, selber aufrufen (in der
Regel mit anderen Parametern). Dies soll anhand eines kurzen Pseudocodes dargestellt werden:
Modul A(D)
IF "triviales Problem"
THEN return(explizite, triviale Loesung)
ELSE
a) Teile D in D1,D2,...,Dn
und Berechne A(D1),A(D2),...,A(Dn)
b) Setze Teilloesung zur gesamten Loesung A(D) zusammen und return (A(D))
Wie immer erfolgt die weitere Erläuterung an Beispielen.
Bsp. 1: Türme von Hanoi
KAPITEL 2. ALGORITHMEN ZUR BILDANALYSE
26
Schema der Tuerme von Hanoi
hanoi(a,b,c,n);
/* a-Quellstapel; b-Zielstapel; c-Zwischenstapel (Arbeitsspeicher);
n-Anzahl der Scheiben */
void hanoi(int a[], int b[], int c[], int n)
{
if(n==1)
//Nehme oberste Scheibe von a und lege sie auf b oben ab
else
{
hanoi(a,c,b,n-1);
hanoi(c,b,a,n-1);
}
}
Bsp. 2: Summe von n Zahlen → a1 + a2 + . . . + an−2 + an−1 → D2 ; Vorherige Summanden in D1
int summe_iterativ(int a, int n)
{
sum:=0;
for(int i=0; i<n; i++)
{
sum+=a[i];
}
return(sum);
}
int summe_rekursiv(int a, int n)
{
if(i==1) return a[0];
else return(summe(a,n-1)+a[n-1]);
}
Bsp. 3: Fakultät n! = n · (n − 1)!
int fak(int n)
{
int fakult;
if(n==0) fakul=1;
else fakul=fak(n-1)*n;
return(fakul);
}
Bsp. 4: Umkehrung von Strings
void reverse(void)
{
char z;
z=GetChar();
if(z!=" ")
{
reverse();
PutChar(z);
}
}
//Umkehrung aufgrund der Stack-Abarbeitungsreihenfolge der CPU --> Schema!!
KAPITEL 2. ALGORITHMEN ZUR BILDANALYSE
27
Bsp. 5: Umwandlung einer Dezimalzahl in eine Hexadezimalzahl
void hex(int x, char a[16])
{
if(x<16) PutChar(a[x])
else
{
hex(x/16,a);
PutChar(a[x%16]);
}
}
Bsp. 6: Fibonacci-Zahlenfolge a0 = 0, a1 = 1, a2 = 1, . . . , an = an−2 + an−1
int fib(int n)
{
int fibon;
if(n<2)
{
if(n==0) fibon=0;
if(n==1) fibon=1;
}
else fibon=fib(n-1)+fib(n-2);
return(fibon);
}
Problem:
an
an−1
an−2
an−3
an−2
an−3
an−4
...
...
...
...
Mehrfachberechnung von Zweigen y fehlende Ezienz, Rekursion sollte hier nicht genutzt werden.
Bsp. 7: Urnenmodell
Startkonguration: Urne mit weiÿen (◦) und schwarzen (•) Kugeln. Mit jedem Zug wird entweder
eine schwarze oder eine weiÿe Kugel entfernt, bis die Urne völlig leer ist. Gesucht sind alle möglichen
Kombinationen der Zugreihenfolge.
◦◦•
◦•
◦◦
•
◦
◦
leer
leer
leer
wws
wsw
sww
Vorgehensweise: Anzahl der Blätter ist die Anzahl der möglichen Kombinationen ⇒ Blätter rekursiv zählen, d.h.u.a. Baum in linken und rechten Teilbaum aufteilen.
Bsp.
Bsp.
Bsp.
Bsp.
8: Tree-Sort (s. 2.5.5)
9: Merge-Sort (s. 2.5.6)
10: Quick-Sort (s. 2.5.7)
11: Matrixmultiplikation (folgt erst später)
KAPITEL 2. ALGORITHMEN ZUR BILDANALYSE
2.6.1
28
Kellerung
Die Rekursion im numerischen Sinne basiert auf dem Kellerprinzip, welches unmittelbar mit der
Abarbeitungsreihenfolge des Stacks (temporärer Datenstapel) durch die CPU zusammenhängt.
Daten, mit denen momentan gearbeitet wird, werden auf den Stack (Stapel) abgelegt. Dies erfolgt
logischerweise von unten nach oben. CPU nimmt zur Bearbeitung die Daten von oben weg, was
dazu führt, dass zuletzt abgelegte Daten zuerst bearbeitet werden.
Die wird bei der Rekursion ausgenutzt. Ein groÿes Problem wird auf die Trivialfälle heruntergebrochen, welche zuletzt auf dem Stack abgelegt werden. (Die Trivialfälle sind meist die, bei denen
nur noch wenige Einzelelemente bearbeitet werden müssen.) Nach der Aufsplitung fängt die CPU
an mit auswerten und nimmt sich zuerst die obersten Daten vom Stapel, die Trivialfälle. Daraufhin
kommen die Probleme mit der nach unten hin (bzgl. des Stacks) zunehmenden Komplexität. Da
die Trivialfälle schon berechnet wurden sind, können die komplexeren Probleme nun mit deren
Hilfe u.U. schneller bearbeitet werden.
Der Geschwindigkeitsvorteil ist jedoch bei vielen einfachen Algorithmen hinfällig. Berechnet man
bspw. eine Summe von Zahlen rekursiv und iterativ, wird man keinen Geschwindigkeitsgewinn
feststellen, da immer eine bestimmte feste Anzahl an Summationen gemacht werden muss, egal
ob iterativ oder rekursiv. Bei der Multiplikation zweier rieÿiger Matrizen sieht dies jedoch ganz
anders aus.
Fortsetzung folgt in den Semesterferien
2.7 Bewertung von Algorithmen
Wir haben zwei Funktionen f (n) und g(n), wobei n die Anzahl der eingehenden Daten ist. f (n)
sei eine Testfunktion, die die Zeitkomplexität eines Algorithmus' wiederspiegelt. g(n) dagegen
beinhaltet Vergleichswerte für den Algorithmus (bspw. Rechenzeit). Wir denieren nun drei Ordnungsmengen:
g ∈ O(f ) ≡ ∃c > 0 ∃n0 > 0 ∀n ≥ n0 : g(n) ≤ c · f (n)
g ∈ Ω(f ) ≡ ∃c > 0 ∃n0 > 0 ∀n ≥ n0 : g(n) ≥ c · f (n)
g ∈ Θ(f ) ≡ g ∈ O(f ) ∧ g ∈ Ω(f )
Beispiel:
g(n) = n2 + 3n ≤ 2n2
∀n ≥ n0 = 3
→ f (n) falsch, da f (n) < g(n) für n À 0
g(n) = 2n2 + 5n ≤ c n2
→ nun f (n) richtig, da f (n) > g(n) für n À 0
Je nach Anwendung können verschiedene Fälle bei dem Durchlauf eines Algorithmus' eintreten.
Man unterscheidet "best", "average" und "worst case".
Übliche Klassen von f
• 1 ↔ g(n) ≤ c · 1
→ logarithmische Laufzeit
• log n
• n
→ konstante Laufzeit
→ lineare Laufzeit
• n log n
• n2 , n 3 , nt
→ fast lineare Laufzeit
→ quadratische, kubische, polynomiale Laufzeit
Um g(n) zu bestimmen, muss noch festgelegt werden, was die "Elementarbausteine" sind (was
auch immer das heiÿen mag).
Bsp. 1: Selection Sort
KAPITEL 2. ALGORITHMEN ZUR BILDANALYSE
29
• Grundoperation: Vergleich
if(a[i]>max) max=a[i];
• 1. Durchlauf
t ...
1
• 2. Durchlauf
t ...
1
→ g(n) =
n
X
i=
i=1
t
n
t
t
n−1 n
→ n-Operationen
→ (n-1)-Operationen
n2
n
n(n + 1)
=
+ ≤ c · n2
2
2
2
⇒ g(n) ∈ O(n2 )
Bsp. 2:
• Distribution Sort: O(n)
• Skalarprodukt: O(n)
• Matrixmultiplikation: O(n3 )
• Bubble Sort: "best case" O(n), "worst case" O(n2 )
• Insert Sort: O(n2 )
Bsp. 3: Binäres Suchen ↔ Sequenzielles Suchen (O(n))
Voraussetzung: Datenbestand muss bereits sortiert sein
n = 2m Elemente
¶
µ
1
1 1
· · ... ·
= 1 ⇒ m = log2 n
n
2 2
2 m mal
Komplexität
• g ∈ O(f ) höchstens
• g ∈ Ω(f ) niedrigstens
• g ∈ Θ(f ) genau
Bsp.: Merge Sort
T (n)
=
h ³n´
ni
+c
+ cn
2 2T
2
³ n ´4
4T
+ 2cn
h 4³ n ´
ni
+c
+ 2cn
4 2T
4
³ n ´8
8T
+ 3cn
³8 n ´
16T
+ 4cn
16
n · T (1) + (log2 n)cn
⇒
T (n) ∈ O(n log2 n)
=
=
=
=
=
Bsp.: Quicksort
KAPITEL 2. ALGORITHMEN ZUR BILDANALYSE
"worst case"
T (n)
=
=
T (n − 1) + T (1) + bn + c
b
n · T (1) + (n2 + n − 2) + (n − 1) · c
2
⇒
T (n) ∈ O(n2 )
=
n−1
1 X
(T (k) + T (n − k)) + bn + c
n−1
"average case"
T (n)
k=1
⇒ T (n) ∈ O(n log n)
30
Kapitel 3
Die Sprache C/C++
3.1 Geschichtlicher Überblick
Die Programmiersprache C/C++ entstand 1973 um Programme für UNIX zu entwerfen. Dem
entsprechend ist sie stark an UNIX angelehnt. Entwickelt wurde C/C++ von Brian Kerningham
und Dennies Ritchie. 1978 erreichte die Sprache den Quasi-Standart K und R C und 1988 den
Standart ANSI-C. Nach Better C folgte die Standartisierung unter ISO und die objektorientierte
Weiterentwicklung C++.
3.2 Einfache Syntaxübersicht
3.2.1
Quellcodebeispiel
#include<stdio.h>
#include<math.h>
#include<image.h>
/* Kommentar */
int main()
{
int i,j,k;
c=d;
}
//standart input/output
//Mathebibliothek
//ICE Bibliothek
//Kommentar
//initiieren des Hauptprogrammes
//Anfang Block
//Variablendeklaration als Integer
//c ergibt sich aus d
//Ende Block
3.3 Der Zeichensatz in C/C++
Zum C-Zeichensatz gehören Klein- und Groÿbuchstaben, Ziern und die "üblichen Sondereichen".
Kommentare werden mit /*...*/ vom restlichen Quelltext getrennt. Mit \\ kann EINE Zeile
auskommentiert werden. Variablennamen bestehen aus Buchstaben, Ziern und _, wobei der
Name mit einem Buchstaben oder _ beginnen muss.
Schlüsselwörter sind zum Beispiel: double, if, while, void, else,... . Sie haben eine feste Bedeutung
für den C-Compiler. Mit Makronamen werden vom System feste Grössen deniert. Die Zahl π ist
so bspw. unter M_PI und die eulersche Zahl unter M_E gespeichert. Makronamen bestehen aus
Groÿbuchstaben und _ .
31
KAPITEL 3. DIE SPRACHE C/C++
3.4
32
Der Präprozessor
Der Präprozessor bereitet den Quelltext für den Compiler auf. So werden zum Beispiel Kommentare gelöscht sowie include's ausgeführt. Also der Quelltext der eingefügten Dateien und Funktionen
kopiert. So bedeuten:
1. #include<datei.h>
\\suche in speziellen Systemverzeichnissen /usr/include
2. #include "studio.h" \\suche im aktuellen Verzeichnis
3.5
Kleines Programmbeispiel
#include<stdio.h>
#include<math.h>
#include<image.h>
int main()
{
float radius,height volume, surface_area;
printf("\nBerechnet Volumen und Oberflaeche eines Zylinders\n"); \\Ausgabe des Strings
\\"\n" bedeutet newline
printf("\n\nGib den Radius ein: ");
scanf("%f",&radius);
\\liest die Variable radius formatiert in Float ein
printf("\nGib die Hoehe ein: ");
scanf("%f",&height);
volume=M_PI * radius*radius*height;
surface_area= 2.0 * M_PI*radius*(radius+height);
/*Die Verwendung einer normalen 2 statt 2.0 kï¾ 12 nnte u.U. zu einem int statt float fï¾ 21 hren*/
printf("\nVolumen ist:%10.4f",volume);
}
\\Formatierung in 10 Vorkommastellen
\\und 4 Nachkommastellen (Float)
printf("\nOberflaeche ist:%10.4f",surface_area);
3.6 Einfache Ein -und Ausgaben
• printf("controlstring",argument list);
Argumente sind also: Zeichenketten + Formatbeschreiber + Escapesequenzen
Formatbeschreiber sind u.a.: %f oat, %lf double, %i %d integer, &li long, %s String, %c
char
• scanf("%f%f%f",&e,&b,&h);
In der Eingabe werden die Variable mit Leerzeichen getrennt. Der Adressoperator & sagt dem
Programm, dass es den eingelesenen Wert, der bswp. fï¾ 21 r b bestimmt ist, auf die Adresse
von b schreiben soll. ffslush(stdin) leert den Standardein-/-ausgabepuer.
• Spezielle C++ Ausgabefunktion
cout<<a<<b<<h<<endl;
cout<<'\n'<<variable<<"Gib Text ein:"<<endl;
• Spezielle C++ Eingabefunktion
KAPITEL 3. DIE SPRACHE C/C++
33
cin>>a>>b>>c;
• Bsp.: Ein kleines Programm welches sich selbst ausdruckt
#include<stdio.h>
main()
{
char*c="main(){char*c=%c%s%c;printf(c,34,c,34,10);}%c";
printf(c,34,c,34,10);
}
Ausgabe: main()char*c="main()char*c=%c%s%c;printf(c,34,c,34,10);%c";printf(c,34,c,34,10);
3.7
Datentypen
1. integer:
• int a,b,c,D=34;
• long int a,b=-5,c;
• short int c;
• unsigned int A,B=5; (ohne Vorzeichen)
2. oat:
• oat c=1.15;
• oat d=3.1e-15;
• double c,d; (bevorzugt verwenden)
• long double e;
3. characters:
• char c1,c2; (Länge 1 Byte
• char d='F';(ASCII-code des Zeichens F)
• char → integer mit 1 Byte
• signed char a; (vorzeichenbehaftet)
• unsigned char d; (volle Bytes ohne Vorzeichen
4. strings:
• char name [20]; + "Nullbyte" (Array mit chars)
• wchar_t a='a'; (2 Byte)
5. Konstanten:
• direkt
• #dene PI 3.14 (Textersetzung) (#Erkennungszeichen für Präprozessor)
• const double pi=3.14;
Gibt man im Programm Zahlen ein, so werden diese entsprechend interpretiert. Alle Zahlen
ohne Dezimalpunkt werden als integer und alle mit als double behandelt. Desweiteren kann
man integer in Oktal- bzw. Hexadezimalschreibweise angeben, indem man eine führende 0
bzw. 0x voranstellt.
KAPITEL 3. DIE SPRACHE C/C++
34
6. Typumwandlungen:
• Problem: a=(1/3)*h*g
//=0, da Argumente int sind und 1/3=0
• Lösung 1: a=(1.0/3.0)*h*g
//richtiges Ergebnis, da 1.0 nun als float interpretiert
• Lösung 2: a=1.0/3.0*h*g
//auch richtig
• Lösung 3: a=1*h*g/3
//auch richtig, aber nicht schï¾ 12 n
• Lösung 4: a=(float)1/(float)3*h*g
//Beste Lï¾ 21 sung
• Typumwandlung (oat) wandelt "int 1" in "oat 1.0"
Man unterscheidet ASCII-Kode und UNICODE (4 Byte) um Kompatibilität zu gewährleisten und
Nullbytes zu vermeiden, benuzt man UTF8 (Zeichen mit variabler Länge).
3.8
Gültigkeitsbereiche
Variablen innerhalb eines Blockes {. . .} sind ausserhalb (davor) nicht bekannt.
int f;
\\global gueltig
int main()
{
double c,d;
\\im gesamten Block main() gï¾ 21 ltig
{
int i,j,k;
char c;
\\fï¾ 21 r diesen Unterblock gï¾ 21 ltig
c='P';
\\geschrieben wird auf zweitem c (char)
}
for(int i=0,i<n;i++)\* i wird direkt in den Konditionen der for-Schleife
deklariert und ist auch nur in dieser gï¾ 21 ltig */
{...}
}
3.9 Operationen
1. Zuweisung (=):
a=c*d; (a ergibt sich aus c mal d)
a=b=c=d=100; (Mehrfachzuweisungen erlaubt)
2. Vergleichs-Operatoren:
< > (kleiner/gröÿer als)
<= >= (kleiner/gröÿer gleich)
== != (gleich(ist nicht dasselbe wie =)/ungleich)
3. Rechen-Operatoren:
+ - * / %(Modulo)
Bsp.:
float a,b,c; c=a/b; (Gleitkommadivision)
int a,b,c; c=a/b; (Ganzzahlige Division konvertiert c in ganze Zahl)
Denition Modulo (%): a≡b(mod n), a-b=k*n, k ∈ Z
Bsp.:
c= 7 % 2; Rest 1
c=-7 % 2; Rest -1
KAPITEL 3. DIE SPRACHE C/C++
35
4. Inkrement,Dekrement
int i;
i=i+1; → i++ oder ++i
i=i-1; → i-- oder --i
Präxnotaion y=++x+5; → y=(x+1)+5
Postxnotation: y=x++ +5; → y=(x+5)+1
5. Zuweisungsoperatoren:
+= -= *= /= %=
Bsp.:
x=x+delta; → x+=delta; (unärer Operator +=)
6. Kommaoperator
Bsp. 1:
for(int i=0, int j=0; i<10, j<10;i++,j++)...;
//statt
for(int i=0; i<10; i++)
for(int j=0; j<10; j++)
...;
Bsp. 2: int i = (1,2,3,5);
Bsp. 3: int a[50,30][40];
//i=5;
//a[30][40]
7. Darstellung des n-ten Logarithmus: logn a :=
a=log(a)/log(2);
ln a
ln 2
8. Logische Ausdrücke:
• Vergleichsausdrücke (z.B. a==1)
• Nullidentitäten
zahl!=0 → true
zahl==0 → false
Bsp.: 3-3 liefert false; 5 liefert true, da 5<>0
• Klasse Boolean - bool(nicht Standard)
• Bsp.:
g=GetVal(pic1,i,j);
if(g>=uGrenze)
PutVal(pic2,i,j,255);
else
PutVal(pic2,i,j,0);
3.10
Prioritäten
1. ()
2. + - unäre Operatoren (Vorzeichen)
3. ++ -4. * / %
5. + 6. = += -=
Bei gleichwertigen Operatoren gilt Linksassoziativität.
KAPITEL 3. DIE SPRACHE C/C++
3.11
36
Einfache Steuerstrukturen
1. while-Schleife
while(condition){...}
Bsp.:
int n=1;
while(n<=10)
{
m=n*n;
n++;
printf("Quatratzahlen%d",m);
}
2. for-Schleife
for(int i=0;i<=10,i++)
Bsp.: Mittelwert aller Grauwerte
double sum=0.0;
int i,j,g;
int dimx, dimy;
for(i=0;i<dimx;i++)
for(j=0; j<dimy; j++)
{
g=GetVal(pic1,i,j);
sum+=g;
}
sum/=double(dimx)*double(dimy);
3. do-Schleife
do {...} while(condition);
4. Abbruchbefehl break sorgt für das vorzeitige Verlassen einer gesamten Schleife
5. Abbruchbefehl continue sorgt für das Verlassen genau eines (des aktuellen) Schleifendurchlaufes
6. Einfache Verzweigung
if(condition){"cond. true" Block} else{"cond. false" Block}
7. Mehrfachverzweigungen:
switch(Bedingung)
{
case konst_1: Ausdruck_1; break;
case konst_2: Ausdruck_2;
case konst_3: Ausdruck_3; break;
default: Ausdruck_normal;
}
// Bedingung muss ganze Zahl ergeben
// im Fall 1 wird 1. gemacht
// im Fall 2 wird 2. und 3. gemacht
8. Bedingte Ausdrï¾ 21 cke:
Bedingung ? ausdruck_ja : ausdruck_nein;
KAPITEL 3. DIE SPRACHE C/C++
3.12
37
Casting
Das Casting wandelt einen Zahlentyp in einen anderen um. In C gibt es dafür die Operatoren
(type) wie z.B. (int). In C++ kann man alternativ dazu den Ausdruck klammern, der gecastet
werden soll. Bei komplizierteren Ausdrücken, kann es notwendig sein beides zu klammern. Mit
Casting kann man z.B. explizit runden. Beispiel:
• int c;
float d;
c=2.6;
c=int(2.6);
d=Random(1);
c=int(d+0.5);
c=int(-d-0.5);
c=floor(d);
//c==2; besser ist
// erzeugt eine Zufallszahl zwischen 0 und 1
// bei negativen Zahlen
// oder die Funktion
• (short int)(a * 1.5)
3.13
Felder (Arrays)
3.13.1 Eindimensionale Felder (Vektoren)
Deklaration: type name[laenge];
Dabei muss die Variable laenge jedoch eine Konstante sein. Nicht erlaubt sind also Felder, deren
Gröÿe man vorher einliest. Beispiel:
#define MAX_SIZE 80
float x[MAX_SIZE];
int squares[5]={1,2,4,9,16};
int null[8]={0};
int zwei[7]={2,2};
// Initalisierung
// fuellt alle Elemente mit 0
// fuellt alle weiteren mit 0
Die Indizierung der Felder beginnt bei 0. Beispiel:
double x[10],y[10],skalar=0.0;
for(int i=0;i<10;i++)
skalar+=x[i]*y[i];
3.13.2
C-Zeichenketten
Zeichenketten in C bestehen aus einem char-Feld. Die eigentliche Zeichenkette wird von einem
Nullbyte abgeschlossen.
[numbers=none]
char material[6]={'s','t','e','e','l','\0'}; //ergibt das gleiche wie
material="steel";
Für die Verarbeitung von C-Zeichenketten gibt es spezielle Funktionen, welche mit #include <string.h>
eingebunden werden müssen.
1. l=strlen(Z_kette); gibt die Länge der Zeichenkette bis zum Nullbyte
2. strcpy(ziel, quelle); kopiert die Quelle auf das Ziel
3. strcat(a,b); hängt b an a an.
4. result=srtcmp(a,b); vergleicht die Zeichenketten a und b. Dabei vergleicht es Zeichenweise
beim ersten Zeichen beginnend.
KAPITEL 3. DIE SPRACHE C/C++
if (result < 0);
if (result == 0);
if (result > 0);
38
// "a < b"
// "a == b"
// "a > b"
5. sprintf(Zeichenkette, "Text", arg_list); schreibt den Text, wie printf in die Zeichenkette.
3.13.3
Zwei- und mehrdimensionale Felder (Matrizen und Tensoren)
Beispiel:
int
int
int
int
matrix[dimx][dimy];
kmplx[2][4][8][16][32];
zwei_zwei[2][2]={{1,2},{3,4}}
zaehlen[3][3]={1,2,3,4,5,6,7,8};
// Initalisierung
// ein Element fehlt -- aber welches?
Bei Zuweisungen in for-Schleifen, die sich nicht ändern, sollte man eine Hilfsvariable verwenden,
da das Programm sonst bei jedem Zugri erst berechnen muss, wo es hinschreiben soll (Siehe
Abschnitt Pointer 3.14).
Beispiel: Matrixprodukt
double a[10][10],b[10][10],c[10][10],sum;
int i,j,k;
for (i=0;i<10;i++)
for (j=0;j<10;j++)
{
sum=0;
for (k=0;k<10;k++)
sum += a[i][k]*b[k][j];
c[i][j]=sum;
}
3.14
Pointer
Pointer sind, wie die ï¾ 12 bersetzung schon sagt, Zeiger auf Speicherbereiche, sozusagen, typisierte
Adressen. Angewandt haben wir diese Variablenform schon bei dem scanf("...",&a) Befehl. Das
Einlesen und anschlieï¾ 12 ende Schreiben eines Datensatzes erfolgt auf die Adresse der Variablen a,
vermittelt durch den Adressoperator &. Deklariert wird ein Pointer wie folgt:
type *name;
Bsp.:
float *ptr1;
int *ptr2 = &x;
//Direktinitialisierung mit einer int Variablen x
//Achtung! nicht
int* ptr3, ptr4;
//denn nun ist ptr3 ein Pointer auf einen Integer-Speicherbereich, aber
//ptr4 eine gew\"{o}hnliche Integer Variable
Um den Unterschied zwischen normalen Variablen und Pointern klar zu machen, stellen wir hier
beide Datenspeicherformen gegenï¾ 12 ber.
Variable:
• Zuweisung (Reservierung) eines festen Speicherplatzes bei der Deklaration
KAPITEL 3. DIE SPRACHE C/C++
39
• Füllen dieses Speicherplatzes (bei Adresse x) mit Daten bei Denition
Pointer:
• Zuweisung eines variablen Speicherplatzes, der bei Deklaration auf nil (bzw. NULL) zeigt
• Zeigt auf neuen Speicherplatz bei Denition
Abbildung 3.1: Funktionsweise Pointer
3.14.1
Adressierung und Dereferenzierung
Die Arbeit mit Pointer wäre natürlich bei weitem nicht so attraktiv, wenn man den Speicherplätzen
keine Daten zuweisen oder diese aus ihnen lesen könnte. Um dies zu tun, gibt es die beiden
Operatoren * und &. Der *-Operator wird Dereferenzierungsoperator genannt und gibt sozusagen
den Inhalt des Speicherbereiches zurück, auf den der Pointer, auf den der Operator angewendet
wird, zeigt.
int* ptr;
*ptr == Wert, der unter der Adresse ptr steht.
Der &-Operator wird auch Adressoperator genannt, da er die Adresse der Variablen zurück gibt,
auf die er wirkt. Im Prinzip ist dieser also das Inverse des *-Operators.
int zahl;
&zahl == Adresse, unter der die Variable zahl im Speicher zu finden ist.
Bsp. zur Initialisierung und Zuweisung:
int *ptr1 = &x;
//Die Adresse ptr1 (eigentlich nil) wird mit der von
//Varible x initialisiert
*ptr = 10;
//In den Speicher (also in Var. x) der Adresse ptr1
//wird eine 10 geschrieben
int y = *ptr1;
//Var. y mit dem Inhalt von Adresse ptr1 initialisiert
y = *ptr1*x/y**ptr1;
//== x*x/y*x; unuebersichtlich!!
y = (*ptr1)*x/y*(*ptr1); //besser!
Bsp. zur Identitätsbeziehung der Operatoren:
int a,b=2;
a = *&b;
int* ptr1=&a;
int* ptr2=&b;
ptr2=ptr1;
//== a=b;
//!= b=a, da hier keine Daten kopiert werden, sondern nur
//Adressen ueberschrieben werden, deswegen == ptr2=&a
Zudem Vorsicht auch bei Vergleichen:
if((*ptr1)==10)...;
KAPITEL 3. DIE SPRACHE C/C++
3.14.2
40
Pointer-Arithmetik
Bsp.:
int *ptr;
//Addition
ptr++;
ptr = ptr + 1;
//erlaubt!
//Inkrement um eine Adresseinheit (Typenabhaengig,
//bspw. int -> Schrittweite 4 Byte)
ptr += 2;
//Subtraktion
ptr--;
ptr -= 2;
Bei Pointern verboten sind jegliche Arten von Multiplikationen und Divisionen.
3.14.3 Pointer und Felder
Pointer können u.a. zur Verwaltung von Feldern genutzt werden. Bsp.:
int x[5]={1,2,3,4,5};
x==&x[0] → x ist eine Pointerkonstante! Übergibt man x bspw. an ein Unterprogramm, übergibt
man den Zeiger auf das erste Element des obigen Arrays. Bsp.:
int *ptr;
x = ptr;
ptr = x;
//Syntaxfehler!? Wahrscheinlich weil ptr->NULL(nil)(?)
//erlaubt!
Mit Hilfe der Pointer-Arithmetik ermöglicht dies eine alternative Variante, um sich innerhalb von
Arrays zu bewegen. Bsp.:
int z = *(x+3);
//== x[2];
Natürlich kann es auch Felder mit Pointer-Einträgen geben. Bsp.:
int a[50];
int *p[50];
double* pq[50];
double** q;
Adresse von q
4Byte
//4 Byte, Ziffernliste
//4 Byte, Adressliste
//auch 4 Byte, weil Pointer
//== p[]
−→
Adresse Feld 1
↓
double var1
Adresse Feld 2
↓
double var2
Adresse Feld 3 . . .
↓
double var3 . . .
Vor allem die letzte Beziehung kann zur dynamischen Konstruktion eines mehrdimensionalen Arrays genutzt werden. [Hier den Absatz zur Speicherplatzreservierung durch die malloc
Fkt. einfügen!!!]
3.14.4 Pointer-Konstanten und Speicherzugri
Eine durch das Schlüsselwort const inistialisierte Konstante ist schwächer gesichert, als ein durch
char* p = "konstante" (ähnlich für andere Typen) initialisierte Pointer-Variable. Der Grund
dafür ist, dass const Werte im normalen Speicher abgelegt werden und der Compiler nur darauf
achtet, dass nicht auf const geschrieben wird. Wenn man den Compiler mit einem Trick umgehen
kann, ist der const Wert trotzdem beschreibbar.
Bsp. 1: Deklarationen
KAPITEL 3. DIE SPRACHE C/C++
41
char const *ptr;
//dasselbe wie const char*, mit dem Unterschied,
char* const ptr=&x;
//dass gleich initialisiert werden muss
const char* const ptr=&c; //const char-Var. mit const-Pointer Eintrag
Bsp. 2: Konstantenlter des Compilers
const int i=5;
i=10;
//Fehlermeldung durch Compiler!
const char* ptr; char x="a";
ptr=&x;
//Einmalzuweisung erfolgt, nun schreibgeschuetzt
*ptr = 'A';
//Fehlermeldung durch Compiler!
Bsp. 3: Casting/Compiler umgehen
const int i=5;
const int* ptr1=&i;
int* ptr2;
//normalerweise Fehler, aber ptr1 auch als const deklariert
ptr2=(int*)ptr1;
*ptr2 = 50;
//casting
//Keine Fehlermeldung, obwohl i und ptr1 schreibgeschuetzt
Bsp. 4: Wirklich schreibgeschützte Konstanten
char a[]="Meyer";
char* b="Meyer";
//legt Feld im Arbeitsbereich des Hauptspeichers an
//legt Pointerkonstante im geschuetzten Bereich des
//Hauptspeichers an
*a='N';
*b='N';
//aber
*a=*b;
//-> a[]="Neyer"
//Fehler! Falsche Adressierung zur LAUFZEIT!
//moeglich!
Ablage im Hauptspeicher:
Arbeitsbereich
M
t
↑
a
e
t
y
t
e
t
r \0
t t
Schreibgeschützter
Bereich
M
t
↑
b
e
t
y
t
e
t
r
t
\0
t
3.14.5 Besondere Pointer
a) NULL-Zeiger
Dieser Zeiger zeigt auf einen Dummy-Speicherbereich, der nicht zur Bearbeitung, sondern nur
zur Vermittelung gedacht ist, und wird oft auch als nil (not in list) Element bezeichnet. Bsp.:
int *p=NULL;
//== *p=0;
if(p=NULL)...;
//Fehlerabfrage, ob p falsch initialisiert
b) Nicht-typisierte (void) Zeiger
Bsp. 1: Automatisches Typcasting
double* x; double y;
void *p;
KAPITEL 3. DIE SPRACHE C/C++
42
x=p;
x=(double*)p;
//typenlos wird typisiert: void*=>double*
//typcasting, hier: == x=p;
p=x;
y=*p;
//double*=>void*
//Zuweisungsfehler! (da p vom Typ void*)
Bsp. 2: Feldverwaltung
void *p;
int x[50]; int *y;
p=x;
y=p;
z=y[2];
//void*=>int*
int z;
//== z=x[2];
c) Character-Felder/Strings
Bsp.: String kopieren
char string_1[]="Schulze";
string_1 ist ein Array mit 8 Elementen (7 Buchst. + Schusszeichen).
1. Variante:
char string_2[50];
char* pq=string_1;
char* pz=string_2;
while(*pq)
//Solange wie *pq!=0 mache
*(pz++) = *(pq++); //Uebertrage Symbole.
*pq='\0';
//Dann Schlusszeichen setzen.
Achtung! Postx pz++ wird erst nach dem Übertragen ausgeführt, also wird erst ganz zum
Schluss inkrementiert.
2. Variante:
pq--; pz--;
do
*(++pz) = *(++pq);
while(*pq);
Nun wird zuerst inkrementiert und dann Übertragen.
3.15
Filesysteme
Grundsätzlich muss man Filenamen in 2 Typen unterscheiden: Den logischen Namen, der den
Namen des Files im Programm angibt und den physischen Namen, welcher der wirkliche Name
der Datei im Betriebssystem ist. Im Programm muss der Bezug zwischen diesen beiden Namen
erst hergestellt werden. Dabei können Namen nicht nur direkt auf Dateien zeigen sondern auch
auf andere Schnittstellen des Betriebssystems z.B. LPT1,COM usw. Der logische Filename ist in
C ein sogenannter Filepointer. Er wird mit
FILE* infile_ptr,outfile_ptr;
initialisiert. Die grundlegende Programmstruktur beim Umgang mit Filesystemen gliedert sich wie
folgt:
KAPITEL 3. DIE SPRACHE C/C++
43
OPEN file
...
Verarbeitung
...
CLOSE file
Ein File wird mit
FILE* fopen(char* filename,char mode) //Funktion gibt einen Filepointer zurueck
aufgerufen.
3.15.1
Beispiel zum Umgang mit Files
FILE* file_ptr;
char filename[81];
gets(filename);
file_ptr = fopen(filename,"r");
file_ptr = fopen(filename,"w");
if(file_ptr=NULL)
{Verarbeitung}
fclose(file_ptr);
//Datei zum Lesen (r) bereitstellen
//Datei zum Schreiben (w) bereitstellen
//Speicherbereich freigeben und verbleibenen
//Buffer physisch schreiben
In C/C++ existieren vordenierte logische Dateinamen. Dies sind zum Beispiel:
• stdin ... Pointer auf Tastatur
• stdout ... Pointer auf Terminal bzw. Ausgabe
• stdprn ... Pointer direkt an Drucker (Achtung Drucker muss Daten nicht verstehen!)
• stdaux ... serielle Schnitstelle
3.15.2
Verarbeitung von Files
• Formatierter Zugri
Der Zugri auf die Datei erfolgt formatiert, d.h. der Inhalt der Datei wird interpretiert, beispielsweise als ASCII-Code. Allerdings werden physisch natürlich auch nur Bytes geschrieben.
Beim Einlesen einer Datei wird ein internen Filepointer generiert, der auf den Anfang, der
im Puerspeicherbereich liegenden Datei zeigt. Bei jeder Aktion mit der Datei wandert der
interne Filepointer (iFp) ein Byte weiter. Beispiel für formatierte Zugrie sind:
fgetc(file_ptr);
fputc('A',file_ptr);
//Liest Zeichen aus file_ptr
//Schreibt Zeichen A an Postion des aktuellen
//iFp in die Datei
• Formatierungsprogrammbeispiel
FILE* infile_ptr;
FILE* outfile_ptr;
infile_ptr = fopen("dateiname","r");
outfile_ptr = fopen("dateiname","w");
//Lesezugriff
//Schreibzugriff
while((char ch=fgetc(infile_ptr))!=EOF) //EOF als Stopzeichen -> Ende Datei
fputc(ch,outfile_ptr);
KAPITEL 3. DIE SPRACHE C/C++
44
Dieses Programm kann nicht nur Textdateien kopieren wie man zunächst vermuten würde, es
kann alle Dateien kopieren die nicht die Bitweise Kodierung des Zeichens EOF besitzen. Die
Bits in der Datei werden zwar als Charakter interpretiert aber dennoch so in die Zieldatei
geschrieben. Physisch werden also wirklich alle Bits kopiert. Falls die Datei die Bitkodierung
des Zeichens EOF enthält würde das Programm hier das kopieren abbrechen. Um dies zu
Umgehen bietet C/C++ die Funktion feof(file_ptr) welche angibt ob die Datei zu Ende
ist. Vorher muss natürlich der iFp auch auf eine Stelle die hinter dem Ende der Datei ist
rücken damit feof FALSE zurückgibt. Das Programm muss also folgendermassen verändert
werden:
ch = fgetc(infile_ptr);
while(!=feof(infile_ptr))
{
fputc(ch,outfile_ptr);
ch = fgetc(infile_ptr);
}
Zum Einlesen von Strings können die Funktionen fgets() und fputs() benutzt werden.
Beispiele sind:
char buffer[10];
fgets(buffer,n,file_ptr);
//n ist maximale Anzahl an Chars
fscanf(infile_ptr,"Syntax wie scanf Funktion");
fprintf(outfile_ptr,"Syntax wie scanf Funktion");
• Binäre "Formatierung"
Um Dateien binär auszulesen und zu bearbeiten braucht man die Funktionen fread(),
fwrite(). Sie lesen und schreiben die auf unterster Ebene die physischen Bits einer Datei.
Diese Funktionen sind nicht portabel! Je nach Prozessorarchitektur und interner Bauweise
des Rechners werden Dateien unterschiedlich physisch gepeichert. So muss die Bitkodierung
einer Integerzahl nicht immer von rechts erfolgen. Die mit fread() ausgelesenen Dateien
können auch nur auf dem selben Computer wieder gelesen werden, ohne ihren Inhalt zu
verändern oder nicht mehr zu verstehen. Eine Möglichkeit die Dateien dennoch portabel zu
machen, ist die vollständige Standartisierung aller Bits in einer Datei.
• Funktionen zur Manipulation des internen Filepointers
Um den internen Filepointer zu steuern können folgende Funktionen verwendet werden:
void rewind(file_ptr);
int i=ftell(file_ptr);
fseek(file_ptr,int i);
3.15.3
//iFp auf Anfang
//Position des iFP
//iFp auf Position i setzen
Praktische Probleme der Dateiverwaltung
Um einen Eintrag im Text zu nden gibt es mehrere Möglichkeiten:
1. sequentielle Suche
2. sortieren nach HOM dann binäres Suchen
3. direkte Addresierung
KAPITEL 3. DIE SPRACHE C/C++
3.16
45
Spezielle Spezikationen in C++
3.16.1 Einfache Erweiterungen gegenüber C
1. Ein- und Ausgabe
Spezielle Bibliotheken (ohne .h-Extension)
#include<iostream>
#include<string>
#include<cmath>
//==math.h
beinhalten spezielle Befehle für C++. Bsp.:
cout << a << b << endl;
cout << "Meyer" << endl;
cin >> a >> b;
Vorteil bzgl. printf: cout erkennt den Datentyp der Ausgabevariablen (hier a und b) automatisch
Nachteil bzgl. printf: Formatierung von Gleitkommazahlen (bspw. durch %10.2lf bei printf)
nur schwer möglich.
Bsp.:
#include<fstream>
//Filestream
ifstream fin("eingabe.txt");
char ch, buf[80];
//input file stream --> fin
fin >> ch;
fin >> buf;
fin.getline(buf,80);
ofstream fout("ausgabe.txt");
fout << ch;
//Lesen!
//Ganze Zeile in buf einlesen
//output file stream --> fout
//Schreiben!
2. Blockkommentare /*...*/
3. Deklarationen dürfen überall stehen.
4. Dynamische Datenobjekte
In C:
int *p_int;
p_int = (int*)malloc(40*sizeof(int));
free(p_int);
In C++:
int *p;
p = new int;
*p=15;
delete p;
Felder: int[]-Operator
KAPITEL 3. DIE SPRACHE C/C++
46
int *pa = new int[14];
//auch dynamisch moeglich
int **pa = new int*[10];
/*Vorsicht bei der Speicherfreigabe*/
delete []pa;
//Operator [] mitnehmen
delete []*pa;
5. fehlt!
6. fehlt!
7. Funktionen mit variabler Parameterzahl
Bsp.:
int contour(Image pic);
int contour(Image pic, char* image_name);
Die Funktion contour wurde mit der zweiten Deklaration überladen. Der Compiler kann
nun zwischen diesen beiden Denitionen unterscheiden, indem er die übergebenen Parameter
prüft und der zugehörigen Funktion zuweist. Dies ist möglich, da der Compiler den Funktionen intern anderen Bezeichner zuweist (hier bspw.: contour_Image und contour_Image_char).
Beide Funktionen, die Einfache und die Überladene, müssen jedoch separat deniert werden.
Möchte man diese Doppeldenition umgehen, besteht die Möglichkeit Default-Parameter in
die Funktionsdeklaration und -denition einzubauen. Die Funktion ist damit nicht mehr
überladen. Bsp.:
//Deklaration
int contour(Image pic, char* image_name="NO");
//Definition
...
//Anwendung
contour(pic1);
//moeglich, da image_name standardmaessig auf "NO"
contour(pic1, picname);
//auch moeglich, image_name wird ueberschrieben
8. Überladen von Funktionen
Mit dem Überladen von Funktionen bezeichnet man die Doppelverwendnung ein und desselben Funktionsnamens für zwei unterschiedliche Funktionen. Diese müssen sich in der Anzahl
und/oder dem Datentyp der Parameter unterscheiden. Angewendet können sie dann ganz
normal. Der Compiler entscheidet anhand der übergebenen Variablen, welcher Denition er
nun dem Aufruf zuordnet. Bsp.:
double max(double x, double y);
int max(int x, int y);
//--> separate Defintionen
Wie oben schon beschrieben funktioniert dies aufgrund der internen Namensvergabe des
Compilers. Diese internen Namen werden als Signaturen bezeichnet und lauten in diesem
Beispiel max_double_double und max_int_int. Wie man sieht geht der Datentyp des Rückgabewertes nicht mit in die Signatur ein und kann von daher auch kein Unterscheidungsmerkmal der Funktionen sein.
9. Casting
In C: a=(float)i; In C++: a=float(i); → Typumwandlungskonstruktor
KAPITEL 3. DIE SPRACHE C/C++
3.16.2
47
Objektorientierung
Um groÿe Programme ezienter und übersichtlicher zu machen, wurde die prozedurale Sprache
C zu der objektorientierten Sprache C++ erweitert. Bei der objektorientierten Programmierung
deniert man abstrakte Datentypen (sog. Klassen), die eine Vereinigung von schon existierenden Datentypen (sog. Eigenschaften) und Funktionen (sog. Methoden/Ereignisse) darstellen. Die
beinhalteten Datentypen und Funktionen können auch intern deniert sein und müssen nicht explizit extern bekannt sein. Die einzige Forderung die gestellt wird, ist, dass Daten eines Objektes
(Instanzierung einer Klasse) nur funktional manipulierbar sein sollen.
Klassen und Objekte
Denition Klasse:
Eine Klasse beschreibt einen konkreten abstrakten Datentyp in seinen Eigenschaften und Verhalten
(Methoden).
Anwendung:
Klassen dienen dazu, Datenelemente und Elementfunktionen (Methoden) in einem Datentyp zu
kapseln. Sie gestatten es, verschiedene Zugrisberechtigungen für die einzelnen Elemente zu vergeben, und können durch Vererbung Klassenhierarchien aufbauen. Dirk Louis; C/C++ - Die
praktische Referenz; Verlag Markt+Technik; 2003 München.
Denition Objekt:
Eine Variable mit dem Datentyp einer Klasse bezeichnet man als Instanz oder Objekt dieser Klasse.
Bsp.: Datum mit Tag, Monat und Jahr
//Defintion:
class datum
{
private:
/* Daten-Default */
int tag, monat, jahr;
//Unantastbar von aussen
//int t=1; verboten
public:
/* Methoden, Elementfunktionen, Memberfunktionen, oeffentliche Datentypen */
void set_datum(int t, int m, int j);
void aktuell();
void print();
void inc();
}
//Anwendung:
int main()
{
datum urlaub, heute, morgen;
//3 Objekte vom Typ datum
...
//Den Objekten koennen Auftraege erteilt werden:
heute.aktuell();
//Aktuelles Datum wird in heute eingepraegt
//Eigentlich vermutete Anwendung aktuell(heute)
//nicht moeglich
heute.print();
//Nur funktionelle Nutzung der internen Objektvariablen/-fkt. moeglich
heute.tag = 18;
//Syntaxfehler, da tag "private"!
}
KAPITEL 3. DIE SPRACHE C/C++
48
Die Frage ist nun, ist es möglich Methoden in der Klasse zu deklarieren (Prototypen), aber diese
auÿerhalb zu denieren (Implementation)?
Dies ist in der Tat möglich, was hier wieder an einem Beispiel gezeigt werden soll:
void datum::set_datum(int t, int m, int j)
{
/* Hier darf auf private Daten zugegriffen werden */
tag=t;
monat=m;
jahr=j;
/* Aufrufe wie datum.tag=t sind jedoch verboten, weil ein Objekt vom typ datum
ja noch nicht existiert. */
}
Neu an dieser Implementierung ist der Scrope-Operator '::'. Mit :: kann man immer auf den
nächst höheren Gültigkeitsbereich zugegreifen. Bsp.:
int a=5;
{
int a=10,b=0;
b= ::a;
}
//b beinhaltet nun den Wert 5
Die nächste Frage, der wir uns nun stellen wollen, lautet: Ist es möglich Methoden zu denieren,
die als Parameter ein Objekt der die Methode beinhaltenden Klasse übergeben bekommt?
Die Antwort lautet auch hier, ja und zwar mit Hilfe klassenbezogener Zeiger (this). this ist
eine Konvention von C++ und stellt einen Zeiger dar, der immer auf das aktuelle Objekt zeigt,
intern jedoch anders behandelt wird (d.h. es ist also keine einfache Schreibweise sondern essenziell
für die korrekte Bearbeitung des Codes). Bsp.:
set_datum(datum heute)
{
heute.tag=t;
//Fehler!!
/* Geht nicht, da Objekt datum ja durch u.a. set_datum definiert, welches an
dieser Stelle noch nicht vollstaendig bekannt ist. */
this->tag=t;
//korrekter Aufruf des oben Gewuenschten
}
Konstruktoren
Konstruktoren spielen bei der Initialisierung von Objekten und anderen Daten eine entscheidende
Rolle. In dem Beispiel int a[2][2]={{1,2},{3,4}}; wird im Hintergrund ein Kontruktor aufgerufen, der den Speicher für diese 2x2 Matrix reserviert und diesen mit den spezischen Werten
füllt.
Der Konstruktor hat die Verantwortung, dass sich ein Objekt vom Augenblick der Entstehung
an in einem korrekten Zustand bendet.
Spezikationen:
• Spezielle Memberfunktionen (Objektfunktionen)
• (System) Default-Konstruktoren existieren für Standard-Datentypen/-objekte
• Regel: Denition eigener Konstruktoren für spezielle Probleme
• Kontruktor hat keinen return-Wert
• Name des Konstruktors ist derselbe, wie der, der zu kontruierenden Klasse
KAPITEL 3. DIE SPRACHE C/C++
49
Bsp.:
class datum
{
...
public:
datum();
datum(int t, int m, int j);
...
}
//Konstruktor
//ueberladener Konstruktor
datum::datum()
{
//nichts
}
datum::datum(int t, int m, int j)
{
tag=t;
monat=m;
jahr=j;
}
main()
{
datum heute;
datum heute(2,7,2008);
//!Achtung! nicht:
datum heute();
}
//Aufruf datum()
//Aufruf datum(2,7,2008)
//--> Prototypendeklaration einer Fkt. heute
Initialisierung mit Listen (entfällt wegen Unverständlichkeit)
Spezielle Konstruktorarten
a) Typumwandlungskonstruktoren
Bsp.:
int i;
double a;
i=a;
Bsp.:
class date
{
int t,m,j;
...
public:
explicit date(char *s);
...
}
int main()
{
//C++-Typumwandlung, jedoch gefaehrlich
KAPITEL 3. DIE SPRACHE C/C++
}
...
date d=date("4.7.2008");
date d="4.7.2008";
50
//Typumwandlung; funktioniert!
//Gewuenschte Typumwandlung funktioniert nicht
b) Kopierkonstruktoren
Es existiert immer ein (System-) Kopierkonstruktor. Dieser sollte jedoch immer selber geschrieben werden. Bsp.:
date b("...");
date a(b);
date a=b;
//a wird sofort mit b initialisiert erschaffen
//a wird erschaffen und b wird zugewiesen
Bsp.:
Image pic1(dimx, dimy, 255);
Image pic2(pic1);
class Image
{
int dimx, dimy;
int maxval;
int* data;
}
public:
...
Schema der Datenzuweisung der Image-Deklarationen Prototyp der Klasse X:
X(const X&x);
X(X x);
Achtung! Der Kopierkonstruktor wird oft implizit vom System benutzt, z.B. bei der Parameterübergabe void f1(Date d){...}. Der Kopierkonstruktor wird bei diesem Beispiel immer
dann aktiviert, wenn die Funktion aufgerufen wird, da bei call-by-value Funktionen immer eine
Kopie der Parameter erzeugt wird.
Destruktor
Der Destruktor ist das Gegenstück des Konstruktors und erledigt die Aufräumarbeiten. Bsp.:
class beispiel
{
private:
int zahl;
public:
beispiel(int i);
~beispiel();
}
beispiel::beispiel(int i);
//Konstruktor
//Destruktor
KAPITEL 3. DIE SPRACHE C/C++
{
}
51
zahl=i;
beispiel::~beispiel()
{;}
main()
{
beispiel *b1 = new beispiel(3);
delete b1;
beispiel b1 = beispiel(3);
}
//<-> beispiel b1(3)
Friend Klassen und Funktionen
• Befreundete Klassen und Funktionen bekommen die Erlaubnis dennoch auf private Daten
zugreifen zu können
• Schlüsselwort (innerhalb der Fkt.Implementation; Freunde müssen der Fkt. genannt werden):
friend
Einige C++ Standardklassen
1. Klasse string
Bsp.:
#include<string>
string bez1;
/* Aufruf des Standardkonstruktors --> Kreiierung eines Objektes, dass leer
ist, bis auf das Schlusszeichen '/0' */
string bez2("hallow");
/* Initialisierung des string-Objekts mit einer C-Zeichenkette
--> Typumwandlungskonstruktor */
string bez3(bez2);
//--> Kopierkonstruktor
string bez4("Meyer");
bez1=bez2+bez4;
//+ ist ueberladener Operator fuer strings
//Weitere ueberladene Operatoren +=
//Achtung: bez1="ABC"+"EDF"; -->Syntaxfehler, da Summation von C-Zeichenketten nicht definiert im C++ Grundstamm
String-Elementfunktionen:
• size_type size() Liefert Anzahl der Zeichen im String, nicht die Anzahl der Bytes.
Bsp.:
string a("abc");
int len=a.size();
• cout << a << endl;
2. Rest fehlt!
//Aufruf einer Memberfkt., auch moeglich: len=size(a);
KAPITEL 3. DIE SPRACHE C/C++
3.17
52
Nützliche Funktionen
• int sizeof(var) - gibt die Byteanzahl(!) des Datentyps der Variable var zurück
Bsp.:
char a;
int b=0;
b = sizeof(a);
b = sizeof(b);
\\b ist nun gleich 1
\\b ist nun gleich 4
• double pow(double base, double exponent) - Exponetialfunktion, die das Ergebnis zu
dem Ausdruck baseexponent zurückliefert (statt double können auch long double oder oat
Variablen übergeben werden)
• double tan(double arg) - Tangenzfunktion (statt double können auch long double oder
oat Variablen übergeben werden)
So nutzbar sind desweiteren die folgenden trigonometrischen Funktionen: sin(), asin()
(Arkussinus), cos(), acos(), atan(), cot(), acot()
• Generierung von Zufallszahlen:
void Randomize() inizialisiert (erneut) den Zufallszahlengenerator, damit er wirklich verschiedene Zahlen ausgibt.
int Random(int val) erzeugt eine im Interval [0, val] gleichverteilte Zufallsvariable.
double RandomD() erzeugt eine im Interval [0, 1] gleichverteilte Zufallsvariable.
double GaussRandom(double sigma) erzeugt eine normalverteilte Zufallsvariablen mit dem
Erwartungswert 0 und der Standardabweichung sigma
• double fabs(double) - Betrag einer Gleitkommazahl
int abs(int) - Betrag einer ganzen Zahl
Herunterladen