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