Prof. Dr. Karl–Udo Jahn HTWK Leipzig Fakultät Informatik, Mathematik und Naturwissenschaften Vorlesungsbegleitende Folien zur Lehrveranstaltung Theoretische Grundlagen der Informatik im Wintersemester 2009/2010 Gliederung Was ist Informatik? Problemlösungsprozess Literaturempfehlungen 1. mathematische Grundbegriffe 1.1 Mengenbegriff, Elementrelation, Teilmengenrelation, mengentheoretische Operationen 1.2 Abbildungen, Funktionen, Operationen, Mächtigkeit von Mengen 1.3 Relationen, Halbordnungs–, Ordnungs– und Äquivalenzrelationen 2. Bits und Bytes 3. Alphabete, Zeichenketten 4. gerichtete und ungerichtete Graphen 5. Variablen– und naiver Algorithmusbegriff; Notationsformen von Algorithmen 6. zwei Beispiele für Modellbildung und Abstraktion 6.1 string–matching–Problem; Akzeptor (zunächst nur informell) 6.2 Prüfungsplanung mittels Fächerkonfliktgraph 7. Zahlen in verschiedenen Stellenwertsystemen; Konvertierungen ineinander 8. interne Darstellung von Zahlen; Gleitpunktarithmetik und Rundungsfehler 8.1 interne Darstellung ganzer und reeller Zahlen; underflow und overflow 8.2 Arithmetik und Rundungsfehler 8.3 Maschinengenauigkeit 8.4 Supercomputer 9. kontextfreie Grammatiken; erweiterte Backus–Naur–Formen; Syntaxdiagramme 10. die natürlichen Zahlen 10.1 Charakterisierung duch ein Peano–System 10.2 Modelle natürlicher Zahlen 10.3 induktive Beweise und rekursive Definitionen 10.4 Bäume; Syntax–, Aufruf– und Suchbäume; Durchlaufverfahren 10.5 natürlichzahlige Terme in infix–, präfix– und postfix–Notation; Kellerspeicher; abstrakte Datentypen 10.6 Beispiele rekursiv definierter Funktionen 10.7 abzählbare Unendlichkeit; das erste Cantorsche Diagonalverfahren 10.8 Mächtigkeit der Menge algorithmisch unlösbarer Probleme; zweites Cantorsches Diagonalverfahren 10.9 Zufallszahlen und Zufallszahlen–Generatoren 10.10 randomisierte Algorithmen 11. Laufzeit von Programmen 11.1 asymptotische Laufzeitabschätzung 11.2 die O–Notation und ihre Eigenschaften 11.3 typische asymptotische Laufzeiten 11.4 ein einfacher Sortieralgorithmus und seine Laufzeit 11.5 binäre Suche in sortierten Arrays und ihre Laufzeit 11.6 schwer lösbare Probleme 12. spezielle algorithmisch unlösbare Probleme 12.1 Halteproblem 12.2 Äquivalenzproblem für Programme 12.3 Post’sches Korrespondenzproblem 12.4 zehntes Hilbert’sches Problem 12.5 Gödelisierung 13. Grundbegriffe der Aussagenlogik 13.1 Begriff der Aussage, Beispiele 13.2 Verknüpfungen von Aussagen und deren Formalisierung 13.3 boolesche Terme (aussagenlogische Ausdrücke) 13.3.1 rekursive Definition und Definition durch EBNF 13.3.2 Semantik boolescher Terme 13.4 Boolesche Algebra 13.5 Schleifeninvarianten; Verifikation von Programmen 13.6 Struktur indirekter Beweise 14. Quantoren; Grundbegriffe der Prädikatenlogik 14.1 prädikatenlogische Terme 14.2 prädikatenlogische Ausdrücke 14.3 freie und gebundene Vorkommen von Variablen 14.4 Semantik der Prädikatenlogik 14.5 Modellbegriff, Erfüllbarkeit, Allgemeingültigkeit, Folgerungsbegriff, semantische Äquivalenz 14.6 programmiertechnische Umsetzung von prädikatenlogischen Ausdrücken 15. Automaten und Sprachen 15.1 endliche Akzeptoren 15.1.1 deterministische Akzeptoren 15.1.2 nichtdeterministische Akzeptoren 15.1.3 nichtdeterministische Akzeptoren mit Epsilon–Übergängen 15.2 reguläre Ausdrücke 15.3 Chomsky–Grammatiken 15.4 Pumping–Lemma für reguläre Sprachen 15.5 Abschlusseigenschaften regulärer und kontextfreier Sprachen 15.6 deterministische endliche Automaten mit Ausgabe 15.7 Kellerautomaten 15.7.1 nichtdeterministische endliche Kellerautomaten 15.7.2 deterministische endliche Kellerautomaten 15.8 Turing–Maschinen 15.8.1 deterministische Turing–Maschinen 15.8.2 Turing–Berechenbarkeit 15.8.3 These von Church 16. loop– und while–Berechenbarkeit 16.1 loop–Programme 16.2 while–Programme (Änderungen und Ergänzungen vorbehalten) Informatik Wissenschaft von der automatischen Verarbeitung von Signalen, durch die Informationen repräsentiert werden, insbesondere mit Hilfe von elektronischen Rechenanlagen. Daher werden untersucht: Struktur, Wirkungsweise, Fähigkeiten und Konstruktionsprinzipien von Rechenanlagen (Prozessoren, Speicher, Bussysteme, Schnittstellen, ...; Parallelrechner, Vektorrechner, ...) Strukturen, Eigenschaften und Beschreibungsmöglichkeiten von Informa- tionen und von Informationsverarbeitungsprozessen (Signale, Daten, Dateien, Datenbanken, Programme, Programmiermethoden, formale Beschreibungsmethoden, ...) Möglichkeiten der Strukturierung, Formalisierung und Mathematisierung von Anwendungsgebieten sowie der Modellbildung und Simulation (Flugsimulator, Simulation von chemischen und physikalischen Prozessen, Crashsimulation, Bild– und Spracherkennung sowie –Verarbeitung, Computertomographie, globale Wettervorhersage, e–commerce–Systeme...) 1 Einen wichtigen Platz nehmen ein abstrakte Begriffe und Objekte (Variablen, Terme, Ausdrücke, Relationen, Datenmodelle, Algorithmen, abstrakte Automaten, (abstrakte) Datentypen, Module, Klassen, Objekte, · · ·) formale Strukturen (Programmablauf– und Datenflusspläne, Pseudocodes, Datenstrukturen, Programm– und Sprachstrukturen, Syntaxdiagramme, Graphen, Petri–Netze, Entity–Relationship–Diagramme, Entscheidungstabellen, · · ·) und deren Transformation nach formalen Regeln Methoden der algorithmischen Problemlösung (modulare Zerlegung von Problemen, top–down–Analyse und bottom–up–Synthese, Rekursionsprinzipien, Prototyping, · · ·) und Programmierparadigmen; Wahl der Programmiersprache Beurteilung von Algorithmen hinsichtlich ihrer Korrektheit und Komple- xität; Wartbarkeit und Erweiterbarkeit von Programmen effektive Darstellung von Datenmodellen, abstrakten Objekten und forma- len Strukturen im Rechner und automatische Durchführung von Transformationen ”Abstraction in the sense we use it implies simplification, the replacement of a complex and detailed real–world situation by an understandable model within which we can solve a problem. That is, we ’abstract away’ the details whose effect on the solution to a problem is minimal or nonexistent, thereby creating a model that lets us deal with the essence of the problem.” (Aho/Ullman) 2 Man unterscheidet: Technische Informatik: funktioneller Aufbau von Computern und dazugehörigen Peripheriegeräten, Rechnernetze; logischer und technischer Entwurf von Schaltungen und Geräten Praktische Informatik: Methoden und Hilfsmittel zum Entwurf und letztlich zur Umsetzung von Problemlösungen in Computerprogramme; Beurteilung der Problemlösungen Theoretische Informatik: grundlegende Strukturen und Prozesse mit mathematischen Hilfsmitteln modellieren und untersuchen; zeigt prinzipielle Grenzen des Computereinsatzes auf und schafft Voraussetzungen für neuartige Computeranwendungen Angewandte Informatik: Oberbegriff für Zweige anderer Fachgebiete, die sich des Computers zur Problemlösung bedienen (medizinische Informatik, Bioinformatik, Medieninformatik, Kommunikationsinformatik, Information Brokering, ...) 3 Problemlösungsprozess Rückinterpretation der Ergebnisse Problem der realen Welt Modellbildung und Abstraktion Problemspezifikation Strukturierung und Modularisierung Algorithmierung Programmierung Problemlösung globale Wettervorhersage automatische Bild- und Spracherkennung, Sprachübersetzung Simulation physikalischer und chemischer Reaktionen Platzbuchungssysteme e-commerce-Systeme usw. Die während der Modellbildung und Abstraktion verwendeten formalen Methoden und Ausdrucksmittel müssen mächtig genug und sollten einfach implementierbar sein. ”Computer scientists must create abstractions of real–world problems that can be understood by computer users and, at the same time, that can be represented and manipulated inside a computer.” (Aho/Ullman) 4 Literatur (einschließlich Kapitelüberschriften) Aho, A. V. and J. D. Ullman: Foundations of Computer Science (C Edition). W. H. Freeman and Company 2000 siehe auch http://infolab.stanford.edu/∼ullman/focs.html (am 1.9.2009 vorhanden) Computer Science: The Mechanization of Abstraction; Iteration, Induction and Recursion; The Running Time of Programs; Combinatorics and Probability; The Tree Data Model; The List Data Model; The Set Data Model; The Relational Data Model; The Graph Data Model; Patterns, Automata and Regular Expressions; Recursive Descriptions of Patterns; Propositional Logic; Using Logic to Design Computer Components; Predicate Logic Asteroth, A. und Ch. Baier: Theoretische Informatik. Pearson Studium 2002 Abstrakte Rechnermodelle, Entscheidungsprobleme, Komplexitätsklassen, Das P–NP– Problem, Grammatiken, Reguläre Sprachen, Kontextfreie Sprachen, Deterministisch kontextfreie Sprachen, Entscheidungsprobleme für formale Sprachen Eirund, H., Müller, B. und G. Schreiber: Formale Beschreibungsverfahren der Informatik. Teubner–Verlag 2000 Grundlagen aus Mathematik und Logik, formale Sprachen, Automaten und Verfahren, Beschreibung nebenläufiger Prozesse, Algorithmenbegriff Gries, D.: The science of programming. Springer–Verlag 1981 Propositions and Predicates, The Semantics of a Small Language, The Development and Verification of Programs Hopcroft, J. E., Motwani, R. and J. D. Ullman: Einführung in die Automatentheorie, Formale Sprachen und Komplexitätstheorie. Pearson Studium 2002 Automaten: Die Methode und der Wahnsinn, Endliche Automaten, Reguläre Ausdrücke und Sprachen, Eigenschaften regulärer Sprachen, Kontextfreie Grammatiken und Sprachen, Pushdown–Automaten, Eigenschaften kontextfreier Sprachen, Einführung in Turing– Maschinen, Unentscheidbarkeit, Nicht handhabbare Probleme, Weitere Problemklassen Hromkovic, J.: Algorithmische Konzepte der Informatik. Teubner–Verlag 2001 Alphabete, Wörter, Sprachen, endliche Automaten, Turingmaschinen, Berechenbarkeit, Komplexitätstheorie, Algorithmik für schwere Probleme, Randomisierung, Kommunikation und Kryptographie Kastens, U. und H. Kleine Büning: Modellierung: Grundlagen und formale Methoden. Hanser–Verlag 2008 Modellierung mit Wertebereichen, Terme und Algebren, Logik, Modellierung mit Graphen, Modellierung von Strukturen, Modellierung von Abläufen, Fallstudien 5 Klaeren, H. und M. Sperber: Vom Problem zum Programm. 3. Auflage 2001, Teubner–Verlag was ist Informatik, was ist Programmierung, induktive Definitionen, Rekursion und Induktion, Paare und Listen, higher–order–Programmierung, Datenabstraktion, abstrakte Datentypen, binäre Bäume, datengesteuerte Programmierung, Zuweisungen und Zustand, objektorientiertes Programmieren, logische Kalküle, der λ–Kalkül, kontextfreie Grammatiken, metazirkuläre Interpretation, mathematische Grundlagen, Geschichte der Informatik Meinel, Chr. und M. Mundhenk: Mathematische Grundlagen der Informatik. Teubner–Verlag 2002 Aussagen, Mengen und Mengenoperationen, mathematisches Beweisen, Relationen, Abbildungen und Funktionen, diskrete Stochastik, Boolesche Algebra, Graphen und Bäume, Aussagenlogik Sander, P., Stucky, W. und R. Herschel: Automaten, Sprachen, Berechenbarkeit. Teubner–Verlag 1992 Mathematische Grundlagen, Automaten, Formale Sprachen, Turing–Maschinen, Algorithmen und berechenbare Funktionen Schöning, U.: Logik für Informatiker. BI Wissenschaftsverlag 1992 Aussagenlogik, Prädikatenlogik, Logik–Programmierung Schöning, U.: Theoretische Informatik kurz gefaßt. BI Wissenschaftsverlag 1992 Automatentheorie und formale Sprachen, Berechenbarkeitstheorie, Komplexitätstheorie Schöning, U.: Ideen der Informatik. Oldenbourg–Verlag 2006 Algorithmik, Graphen, formale Sprachen, Grammatiken und Automaten, Berechenbarkeit und deren Grenzen, Aussagenlogik und Boole’sche Schaltungen, Prädikatenlogik und Programmverifikation, Information, Codierung und Kryptologie Vossen, G. und K.–U. Witt: Grundlagen der Theoretischen Informatik mit Anwendungen. Vieweg–Verlag 2000 endliche Automaten, reguläre Sprachen, endliche Maschinen und Automatennetze, kontextfreie Sprachen, Kellerautomaten, Anwendungen kontextfreier Sprachen, Chomsky– Hierarchie, Turingautomaten, Berechenbarkeit, Entscheidbarkeit, Komplexität Wegener, I.: Theoretische Informatik – eine algorithmenorientierte Einführung. Teubner–Verlag 2005 Turingmaschinen, churchsche These und Entscheidbarkeit, Die NP–Vollständigkeitstheorie, Endliche Automaten, Grammatiken, die Chomsky–Hierarchie und das Wortproblem, Kontextfreie Grammatiken und Sprachen, Kellerautomaten und kontextfreie Sprachen, Deterministisch kontextfreie Sprachen 6 ein paar Bemerkungen zum Studienbeginn Lernen ist harte Arbeit jeder muss seinen eigenen Stil finden; bloßes Durchlesen der Aufzeichnungen, weiterer Materialien und von Büchern reicht nicht, Durcharbeiten ist angesagt Durcharbeiten umfasst u.a.: – Begriffe lernen, Beispiele dazu verstehen, eigene Beispiele bilden oder nach Objekten suchen, die entsprechende oder verwandte Eigenschaften haben – Übungsaufgaben lösen – Methoden und Verfahren verstehen und anwenden – sich Fragen zum Stoff notieren und diese im Seminar bzw. im Praktikum stellen – immer wieder wiederholen ohne Formeln und Formalismen geht es nicht von Anfang an ”dranbleiben” stets nach höchsten Leistungen streben Folien: Begleitmaterial zur Lehrveranstaltung, aber kein Ersatz dafür Akademische Freiheit bedeutet: Es darf mehr gearbeitet werden, als verlangt wird. (Feodor Lynen, (1911-1979), Nobelpreisträger 1964) 7 einige mathematische Grundbegriffe (Mengen, Abbildungen, Funktionen, Operationen, Relationen) 1. naiver Mengenbegriff, Elementrelation, Teilmengenrelation (Inklusion), mengentheoretische Operationen (a) Mengenbegriff, Elementrelation (Enthaltenseinsrelation) ∈ Georg Cantor (1845–1918), Begründer der Mengenlehre: ”Unter einer Menge verstehen wir jede Zusammenfassung M von bestimmten wohlunterschiedenen Objekten unserer Anschauung oder unseres Denkens (welche die Elemente von M genannt werden) zu einem Ganzen.” ”x ist ein Element von M” wird notiert durch ”x ∈ M” ”x ist nicht Element von M” wird notiert durch ”x ∈ / M” Schreibweisen für Mengen: explizite Notation der Elemente innerhalb geschweifter Klammern, z.B. {a} (gesprochen: Einermenge a); es ist a ∈ {a}, aber x ∈ / {a} für jedes x 6= a {a,b} (gesprochen: Zweiermenge a,b); es ist a ∈ {a, b} und b ∈ {a, b}, aber x ∈ / {a, b} für jedes x mit x 6= a und x 6= b verbale Beschreibung und Einführung von Bezeichnungen, z.B. N:= {0 , 1 , 2 , · · · } = Menge der natürlichen Zahlen Z:= { · · · , -2 , -1 , 0 , 1 , 2 , · · · } = Menge der ganzen Zahlen Q:= Menge der rationalen Zahlen R:= Menge der reellen Zahlen ∅ := die leere Menge; stets ist x ∈ / ∅ für alle x Definition durch Aussonderung (aus einer schon vorhandenen Menge), z.B. {n | n ∈ N ∧ n ungerade} = {n ∈ N | n ungerade} = Menge der ungeraden natürlichen Zahlen {n ∈ N | n ≥ 2 und n hat nur sich selbst und die Zahl 1 als Teiler} = Menge der Primzahlen {n ∈ N | ∃i(i ∈ N ∧ n = 2i)} = Menge aller derjenigen natürlichen Zahlen, die Zweierpotenzen (mit natürlichzahligen Exponenten) sind 8 Für Mengen M1 , M2 , M3 , · · · sind auch {M1 }, {{M1}}, {M1 , M2}, {M1 , M2, M3}, {M1 , {M2, M3}}, {{M1 }, {M2, M3}}, · · · wieder Mengen. Falls eine Menge nur Mengen als Elemente hat, so wird sie auch als Mengensystem bezeichnet. Definition des geordneten Paares (a,b) nach K. Kuratowski (1921): (a, b) := {{a}, {a, b}} Während {a, b} = {b, a} ist (es kommt nicht auf die Reihenfolge der Notation der Elemente einer Menge an), gilt: (a, b) = (c, d) ↔ a=c∧b=d (b) Teilmengenrelationen ⊆ und ⊂ Für beliebige Mengen A, B sei A ⊆ B (A ist Teilmenge von B, B ist Obermenge von A) :↔ ∀x(x ∈ A → x ∈ B) (jedes Element von A ist auch Element von B) A ⊂ B (A ist echte Teilmenge von B, B ist echte Obermenge von A) :↔ A ⊆ B ∧ A 6= B Beispielsweise gilt {n ∈ N | n gerade} ⊂ N ⊂ Z ⊂ Q ⊂ R ∅ ⊆ A für jede Menge A A=B ↔ A⊆B∧B ⊆A Mit Hilfe der Teilmengenrelation ⊆ wird der Begriff der Potenzmenge definiert: PA := {X | X ⊆ A} (die P otenzmenge von A) Wenn A n Elemente hat, so hat PA 2n Elemente. (c) mengentheoretische Operationen Durchschnitt A ∩ B zweier Mengen A, B A ∩ B := {x | x ∈ A ∧ x ∈ B} (Menge aller derjenigen Elemente, die sowohl in A als auch in B enthalten sind) Vereinigung A ∪ B zweier Mengen A, B A ∪ B := {x | x ∈ A ∨ x ∈ B} (Menge aller derjenigen Elemente, die in mindestens einer der Mengen A bzw. B enthalten sind) Differenz A \ B zweier Mengen A, B A \ B := {x ∈ A | x ∈ / B} (Menge aller derjenigen Elemente, die in A, aber nicht in B enthalten sind) 9 (kartesisches) Produkt A × B zweier Mengen A, B A × B := {(a, b) | a ∈ A ∧ b ∈ B} (Menge aller derjenigen geordneten Paare, deren erste Komponente in A und deren zweite Komponente in B enthalten ist) Wenn A n Elemente hat und B hat m Elemente, so hat A × B m ∗ n Elemente. Sind beispielsweise die Mengen A bzw. B durch A := {a, b} und B := {b, c, d} gegeben, so gilt: A ∩ B = {b} A ∪ B = {a, b, c, d} A \ B = {a} A × B = {(a, b), (a, c), (a, d), (b, b), (b, c), (b, d)} 2. Abbildungen, Funktionen, Operationen (a) Abbildungen Es seien A, B Mengen. Es heißt f genau dann eine Abbildung aus A in B, wenn f ⊆ A × B gilt. Vorbereich (Definitionsbereich, domain) Db(f ) von f ⊆ A × B: Db(f ) := {x ∈ A | ∃y(y ∈ B ∧ (x, y) ∈ f )} Nachbereich (Wertebereich, codomain, range) W b(f ) von f ⊆ A × B: W b(f ) := {y ∈ B | ∃x(x ∈ A ∧ (x, y) ∈ f )} Damit ist auch f ⊆ Db(f ) × W b(f ). Die zu f inverse Abbildung f −1 ist definiert durch f −1 := {(y, x) | (x, y) ∈ f }. f ⊆ A × B heißt Abbildung von A in B :↔ Db(f ) = A aus A auf B :↔ W b(f ) = B von A auf B :↔ Db(f ) = A ∧ W b(f ) = B Die Verkettung (Hintereinanderausführung, Komposition) g◦f zweier Abbildungen f , g ist wie folgt erklärt: g ◦ f := {(x, y) | ∃z((x, z) ∈ f ∧ (z, y) ∈ g)} (b) Funktionen Funktionen sind spezielle Abbildungen: Eine Abbildung f heißt genau dann eine Funktion (bzw. eindeutige Abbildung), wenn aus (x, y1) ∈ f und (x, y2) ∈ f stets y1 = y2 folgt. 10 Schreibweisen: f : A → B bedeute, dass f eine Funktion von A in B ist. Für eine Funktion f : A → B wird anstelle von (x, y) ∈ f auch y = f (x) geschrieben. Sind f und g Funktionen, so wird für (g ◦ f )(x) auch g(f (x)) notiert. Die Funktionen f loor : R → Z und ceil : R → Z mit f loor(x) := max{n ∈ Z | n ≤ x} ceil(x) := min{n ∈ Z | x ≤ n} kommen oft in Informatik–Anwendungen vor. (Außerhalb von Programmen werden sie auch als ⌊x⌋ bzw. ⌈x⌉ geschrieben.) Mit B A wird die Menge aller Funktionen von A in B bezeichnet. Eine Abbildung f heißt eineindeutig (injektiv, one-to-one) oder umkehrbare Funktion genau dann, wenn f eindeutig ist und aus (x1, y) ∈ f und (x2, y) ∈ f stets x1 = x2 folgt. Mit f ist auch f −1 wieder eineindeutig. (c) Mächtigkeit von Mengen, endliche und unendliche Mengen Zwei Mengen A und B heißen genau dann gleichmächtig oder von gleicher Kardinalität (in Zeichen card(A) = card(B)), wenn es eine eineindeutige Abbildung von A auf B gibt (eine solche Abbildung heißt auch eine Bijektion). Unendlichkeitsdefinition nach Richard Dedekind (1831–1916): A unendlich :↔ ∃X(X ⊂ A ∧ card(X) = card(A)) A heißt genau dann endlich, wenn A nicht unendlich ist. Man kann zeigen, dass A genau dann endlich ist, wenn es eine natürliche Zahl n gibt mit card({1, · · · , n}) = card(A). Diese sogar eindeutig bestimmte Zahl n heißt die Elementeanzahl von A. Man schreibt dann auch card(A) = n. Endliche Mengen sind genau dann gleichmächtig, wenn sie die gleiche Elementeanzahl haben. Jede Teilmenge einer endlichen Menge ist wieder endlich, und jede Obermenge einer unendlichen Menge ist wieder unendlich. Beispielsweise ist N eine unendliche Menge. Genau die Mengen A mit card(A) = card(N) heißen abzählbar unendliche Mengen. Z, Q, die Menge aller Primzahlen und die Menge aller Zeichenketten über einem (endlichen) Alphabet sind abzählbar unendliche Mengen. 11 Eine Menge heißt genau dann abzählbar, wenn sie endlich oder abzählbar unendlich ist. Unendliche Mengen, die nicht abzählbar unendlich sind, heißen überabzählbar unendliche Mengen. Beispielsweise sind R, {0, 1}N und PN überabzählbar unendliche Mengen (die sogar untereinander gleichmächtig sind). Es hat PA stets eine größere Mächtigkeit als A selbst. (d) Operationen Operationen sind spezielle Funktionen: Gilt f : A → A, so heißt f auch eine einstellige (unäre) Operation auf A. Ist f : A × A → A, so heißt f auch eine zweistellige (binäre) Operation auf A. Z.B. ist die Betragsfunktion f : R → R, d.h. f (x) = |x| für jedes x ∈ R, eine einstellige Operation auf R. Bei binären Operationen f verwendet man anstelle von f (x, y) meistens die infix–Schreibweise xf y (z.B. i + j statt +(i, j) für die Addition + auf der Menge N). Eine nullstellige Operation f auf A, notiert als f : → A, ist nichts anderes als ein Element von A. Wichtige binäre Operationen auf Z sind die ganzzahlige Division div und die modulo–Operation mod: n div m := ⌊|n|/|m|⌋ ∗ sgn(n ∗ m) (dabei ist sgn : R → {−1, 0, 1} mit sgn(x) := −1 für negative x, sgn(x) := 0 für x = 0, und sgn(x) := 1 für positive x) n mod m := n − m ∗ (n div m) (beide nur für m 6= 0 erklärt). Es bezeichnen also n div m den ganzen Teil des Quotienten n/m und n mod m den Rest bei der ganzzahligen Division von n durch m. (e) n–stellige Funktionen Um n–stellige Funktionen bzw. Operationen für n ≥ 3 definieren zu können, muss zunächst der Begriff des n–Tupels als Verallgemeinerung des Begriffes des geordneten Paares eingeführt werden. Dies kann mit Hilfe einer rekursiven (auch: induktiven) Definition geschehen: Für n = 2 und beliebige Elemente x1 , x2 sei das n–Tupel (x1 , · · · , xn) definiert durch das bereits bekannte geordnete Paar (x1, x2) (Induktions–Anfang). Sei jetzt n > 2 und sei (x1, · · · , xn−1) bereits definiert (Induktions– Voraussetzung bzw. Induktions–Annahme) Dann sei (x1, · · · , xn) := ((x1, · · · , xn−1), xn) (Induktions–Schritt). 12 Es wird also (x1, · · · , xn) definiert als das geordnete Paar mit den beiden Komponenten (x1, · · · , xn−1) und xn. Es gilt: (x1, · · · , xn) = (y1, · · · , ym) ↔ n = m ∧ x1 = y1 ∧ · · · ∧ xn = yn . Ein n–Tupel (x1, · · · , xn) und ein m–Tupel (y1, · · · , ym ) sind also genau dann einander gleich, wenn n = m ist und außerdem xi = yi gilt für jedes i mit 1 ≤ i ≤ n (auf gleichen Plätzen befinden sich in beiden Tupeln jeweils gleiche Elemente). Nun kann auch das n–fache Pron dukt Xi=1 Ai (bzw. A1 × · · · × An ) für Mengen A1, · · · , An definiert werden als die Menge aller n–Tupel (a1, · · · , an ) mit ai ∈ Ai für alle i, 1 ≤ i ≤ n. Formalisiert niedergeschrieben: n Xi=1 Ai := {(a1, · · · , an ) | ∀i(1 ≤ i ≤ n → ai ∈ Ai )} n Eine n–stellige Funktion f ist eine Funktion f : Xi=1 Ai → B für gewisse Mengen A1 , · · · , An, B. (Analog kann f ⊆ A × B dann eine n n–stellige Abbildung genannt werden, wenn A = Xi=1 Ai ist für gewisse Mengen A1, · · · , An.) n Falls A1 = · · · = An = B ist, so wird anstelle von Xi=1 Ai auch einfach n B (n-te Potenz von B) geschrieben. Eine Funktion f : An → A wird auch als eine n–stellige Operation auf A bezeichnet. (f) rekursive Definitionen Der Begriff des n–Tupels wurde rekursiv bzw. durch vollständige Induktion eingeführt. Auf analoge Weise können Operationen n– stellig verallgemeinert werden. Zugrunde liegen solchen Definitionen Rekursionstheoreme, von denen jetzt zwei oft benutzte Versionen angegeben werden sollen. Rekursionstheorem 1: Es seien A eine beliebige nichtleere Menge, a ∈ A und g : A → A. Dann existiert genau eine Funktion f : N → A mit f (0) := a (Rekursions– bzw. Induktions–Anfang) f (n′) := g(f (n)) (Rekursions– bzw. Induktions–Schritt) ′ (n : Nachfolger von n, d.h. n′ := n + 1) Rekursionstheorem 2: Es seien A eine beliebige nichtleere Menge, a ∈ A und g : N × A → A. Dann existiert genau eine Funktion f : N → A mit f (0) := a (Rekursions– bzw. Induktions–Anfang) ′ f (n ) := g(n, f (n)) (Rekursions– bzw. Induktions–Schritt) Bemerkung: Ist der Induktions–Anfang durch f (n0) := a gegeben und n0 > 0, so wird die Menge {n0, n0 + 1, n0 + 2, · · ·} ⊆ N der Definitionsbereich von f . 13 3. Relationen (a) n–stellige Relationen Es seien n ≥ 1 eine natürliche Zahl und R, A Mengen. Dann heißt R eine n–stellige Relation in A, falls R ⊆ An ist (n = 2: binäre Relation). Verallgemeinerung: R heißt n–stellige Relation, wenn es Mengen A1, · · · , An gibt, so dass R ⊆ A1 × · · · × An ist. Bei binären Relationen R kann man anstelle von (a, b) ∈ R auch die infix–Schreibweise aRb benutzen. Beispielsweise ist (3, 7) ∈≤ bzw. 3 ≤ 7 für die kleinergleich–Relation ≤ in N, und es ist (4, 16) ∈ | bzw. 4|16 für die Teiler–Relation | in Z (”n|m” bedeute, dass n ein Teiler von m ist, d.h., dass es ein k ∈ Z gibt mit n ∗ k = m). Für jede Menge A kann die identische Relation IdA in A definiert werden: IdA := {(a, a) | a ∈ A}. Analog wie für Abbildungen ist die Verkettung S ◦ R für binäre Relationen R, S erklärt. (b) reflexive Halbordnung(–srelation) Eine binäre Relation R ⊆ A2 heißt genau dann eine reflexive Halbordnung in A, wenn für beliebige a, b, c ∈ A gilt: (a, a) ∈ R (Reflexivität) (a, b) ∈ R ∧ (b, c) ∈ R → (a, c) ∈ R (Transitivität) (a, b) ∈ R ∧ (b, a) ∈ R → a = b (Antisymmetrie) (c) irreflexive Halbordnung(–srelation) Eine binäre Relation R ⊆ A2 heißt genau dann eine irreflexive Halbordnung in A, wenn für beliebige a, b, c ∈ A gilt: (a, a) ∈ /R (Irreflexivität) (a, b) ∈ R ∧ (b, c) ∈ R → (a, c) ∈ R (Transitivität) (a, b) ∈ R → (b, a) ∈ /R (Asymmetrie) Die gewöhnliche ≤–Relation in R ist eine reflexive Halbordnung in R, die <–Relation dagegen eine irreflexive Halbordnung. Das geordnete Paar (R, ≤) bzw. (R, <) wird deshalb auch als reflexiv bzw. irreflexiv halbgeordnete Menge bezeichnet. (d) reflexive Ordnung(–srelation) Eine binäre Relation R ⊆ A2 heißt genau dann eine reflexive Ordnung in A, wenn für beliebige a, b, c ∈ A gilt: (a, a) ∈ R (Reflexivität) (a, b) ∈ R ∧ (b, c) ∈ R → (a, c) ∈ R (Transitivität) (a, b) ∈ R ∧ (b, a) ∈ R → a = b (Antisymmetrie) (a, b) ∈ R ∨ (b, a) ∈ R (Vergleichbarkeit, Linearität) 14 (e) irreflexive Ordnung(–srelation) Eine binäre Relation R ⊆ A2 heißt genau dann eine irreflexive Ordnung in A, wenn für beliebige a, b, c ∈ A gilt: (a, a) ∈ /R (Irreflexivität) (a, b) ∈ R ∧ (b, c) ∈ R → (a, c) ∈ R (Transitivität) (a, b) ∈ R → (b, a) ∈ /R (Asymmetrie) (a, b) ∈ R ∨ (b, a) ∈ R ∨ a = b (Konnexität) Somit sind reflexive bzw. irreflexive Ordnungen spezielle reflexive bzw. irreflexive Halbordnungen. Die gewöhnliche ≤–Relation in R ist auch eine reflexive Ordnung in R, die <–Relation eine irreflexive Ordnung, so dass (R, ≤) bzw. (R, <) sogar reflexiv bzw. irreflexiv geordnete Mengen sind. (f) Äquivalenzrelationen Eine binäre Relation R ⊆ A2 heißt genau dann eine Äquivalenzrelation in A, wenn für beliebige a, b, c ∈ A gilt: (a, a) ∈ R (Reflexivität) (a, b) ∈ R ∧ (b, c) ∈ R → (a, c) ∈ R (Transitivität) (a, b) ∈ R → (b, a) ∈ R (Symmetrie) Beispiele für Äquivalenzrelationen: in jeder Menge A die identische Relation IdA in jeder Menge A die volle Relation A × A in jedem Mengensystem die Gleichmächtigkeitsrelation in Z für jedes m ∈ Z die Kongruenz modulo m: es heißt r kongruent s modulo m (in Zeichen: r ≡ s mod m) :↔ r − s ist ein ganzzahliges Vielfaches von m Wenn R eine Äquivalenzrelation in A ist, so wird für jedes a ∈ A mit [a]R := {b ∈ A | aRb} die Äquivalenzklasse (bzw. Restklasse) von a bzgl. R bezeichnet. Die Menge A/R := {[a]R | a ∈ A} aller Äquivalenzklassen bzgl. R heißt auch der Quotient (bzw. die Faktormenge) von A bzgl. R. Es ist A/R ein Mengensystem mit paarweise elementfremden (disjunkten) Mengen, es ist ∅ ∈ / A/R, und die Vereinigung aller Mengen S aus A/R (notiert als A/R) ist A. (Jedes Mengensystem mit diesen drei Eigenschaften heißt auch eine Zerlegung von A.) Äquivalenzrelationen spielen eine herausragende Rolle in der Informatik. 15 einige Begriffe und Eigenschaften in Bezug auf natürliche Zahlen k ∈ N heißt ein Teiler von n ∈ N (symbolisch: k | n) genau dann, wenn es ein m ∈ N gibt mit k ∗ m = n formal: k | n :←→ ∃m(m ∈ N ∧ k ∗ m = n) k ∈ N heißt der größte gemeinsame Teiler von n, m ∈ N (k = ggt(n, m) bzw. k = gcd(n, m)) genau dann, wenn gilt: k | m ∧ k | n ∧ ∀t(t ∈ N ∧ t | m ∧ t | n −→ t | k) Satz von der Division mit Rest: Zu zwei beliebigen natürlichen Zahlen n, k mit k 6= 0 gibt es genau zwei natürliche Zahlen q und r, so dass gilt: n=q∗k+r ∧ 0≤r<k Bezeichnungen: q = n div k bzw. q = n ÷ k , r = n mod k (Die Operationen div und mod sind sogar für ganze Zahlen n, k mit k 6= 0 erklärt, siehe Folie 12.) Es sei m ∈ N, m 6= 0; zwei natürliche Zahlen r und s heißen genau dann kongruent modulo m (symbolisch: r ≡ s mod m), wenn (r mod m) = (s mod m) ist, d.h., wenn r und s bei Division durch m den gleichen Rest haben. (Verallgemeinerung auf ganze Zahlen: s. Folie 15.) Satz von der eindeutigen Primfaktorzerlegung : Jede natürliche Zahl lässt sich bis auf die Reihenfolge der Faktoren auf genau eine Weise als Produkt von Primzahlen schreiben. Existenz eines Minimums : Jede nichtleere Menge natürlicher Zahlen besitzt ein Minimum. formal: ∀M(M ⊆ N ∧ M 6= ∅ → ∃n(n ∈ M ∧ ∀m(m ∈ M → n ≤ m))) Wichtige Sachverhalte zu den Primzahlen siehe z.B. unter www.utm.edu/research/primes zusätzliche Literatur: Knuth, D. E.: The Art of Computer Programming. Vol. 2: Seminumerical Algorithms. Addison Wesley 1981 Forster, O.: Algorithmische Zahlentheorie. Vieweg 1996 www.mathematik.uni-muenchen.de/∼forster/sw/aribas.html Schroeder, M. R.: Number Theory in Science and Communication. Springer 2005 16 Bits und Bytes Bit (binary digit): ein Bit kann genau zwei Zustände annehmen, nämlich 0 und 1; es ist die kleinste Einheit zur Speicherung von Daten im Computer; Byte: besteht aus 8 Bit; kleinste adressierbare Einheit in Speichern des Computers; z.B. ist der Hauptspeicher des Computers in Bytes unterteilt, wobei jedes Byte seine eigene Adresse (”Hausnummer”) hat, über die sein Inhalt manipuliert werden kann; ein Byte kann 28 = 256 paarweise verschiedene Zustände annehmen Abkürzungen zur Charakterisierung von Bit– und Byte–Mengen K = 210 = 1 024 (Kilo) M = 220 = 1 048 576 (Mega) G = 230 = 1 073 741 824 (Giga) T = 240 = 1 099 511 627 776 (Tera) P = 250 = 1 125 899 906 842 624 (Peta) E = 260 = 1 152 921 504 606 846 976 (Exa) Z = 270 = 1 180 591 620 717 411 303 424 (Zetta) Y = 280 = 1 208 925 819 614 629 174 706 176 (Yotta) Die letzte Zahl in Worten: Eine Quadrillion Zweihundertacht Trilliarden Neunhundertfünfundzwanzig Trillionen Achthundertneunzehn Billiarden Sechshundertvierzehn Billionen Sechshundertneunundzwanzig Milliarden Einhundertvierundsiebzig Millionen Siebenhundertsechs Tausend Einhundertsechsundsiebzig Zur Gegenüberstellung: Anzahl der möglichen Tipps 6 aus 49 plus Superzahl: 49 6 ∗ 10 = 139838160 ≈ 1.4 ∗ 108 Alter der Erde: 109 (≈ 230 ) Jahre Anzahl der Atome in der Erde: 1051 (≈ 2170 ) Anzahl der Atome im Universum: 1077 (≈ 2256 ) Anzahl der Primzahlen mit einer Länge bis zu 512 Bit: 3 ∗ 10151 (≈ 2512 /(512 ∗ ln(2))) (falls n ≥ 17 ist, so gibt es mindestens n/ln(n) Primzahlen, die kleiner oder gleich n sind) (s. B. Schneier: Angewandte Kryptographie. Pearson Studium 2006) 17 Alphabet Ein Alphabet A ist eine endliche nichtleere Menge mit den beiden Eigenschaften von zwei beliebig vorgegebenen Elementen aus A kann effektiv festgestellt werden, ob sie identisch sind oder nicht für jede (endliche) Zeichenkette (engl. string) Z mit Elementen nur aus A und jede natürliche Zahl i kann effektiv festgestellt werden, welches Element von A sich auf dem i–ten Platz von Z befindet Oft ist zusätzlich noch eine Ordnung bzw. Reihenfolge der Elemente von A gegeben. Beispiele: A := {|} (ein einelementiges Alphabet) A := {0, 1} (Menge der Dualziffern) A := {¬, ∧, ∨, →, ↔, ∀, ∃} (Menge der klassischen aussagenlogischen Funktoren und prädikatenlogischen Quantoren) A := {0, 1, 2, 3, 4, 5, 6, 7} (Menge der Oktalziffern) A := {0, 1, 2, 3, 4, 5, 6, 7, 8, 9} (Menge der Dezimalziffern) A := {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F } (Menge der Hexadezimal– ziffern) A :=lateinisches Alphabet A := {A, · · · , Z, a, · · · , z, 0, · · · , 9, +, /} := Base64–Alphabet (hinzu kommt ’=’ als Padding– (Auffüll–)Symbol, siehe z.B. www.ietf.org/rfc/rfc1521.txt) A :=ASCII–Alphabet (27 Elemente) A := U nicode–Alphabet (216 Elemente) (www.unicode.org) ”The Unicode Standard is a character coding system designed to support the worldwide interchange, processing, and display of the written texts of the diverse languages and technical disciplines of the modern world. In addition, it supports classical and historical texts of many written languages.” Zum Beispiel haben der griechische Buchstabe µ die (hexadezimale) Codierung 03BC, das Eurozeichen ¿ die Codierung 20AC und das Unendlichzeichen ∞ die Codierung 221E. 18 ASCII–Alphabet (American Standard Code for Information Interchange, ISO–7–Bit–Code) Dez Hex 0 00 1 01 2 02 3 03 4 04 5 05 6 06 7 07 8 08 9 09 10 0A 11 0B 12 0C 13 0D 14 0E 15 0F 16 10 17 11 18 12 19 13 20 14 21 15 22 16 23 17 24 18 25 19 26 1A 27 1B 28 1C 29 1D 30 1E 31 1F Ctrl–Ch ∧ @ ∧ A ∧ B ∧ C ∧ D ∧ E ∧ F ∧ G ∧ H ∧ I ∧ J ∧ K ∧ L ∧ M ∧ N ∧ O ∧ P ∧ Q ∧ R ∧ S ∧ T ∧ U ∧ V ∧ W ∧ X ∧ Y ∧ Z ∧ [ ∧ \ ∧ ] ∧∧ ∧ Char NUL SOH STX ETX EOT ENQ ACK BEL BS HT LF VT FF CR SO SI DLE DC1 DC2 DC3 DC4 NAK SYN LTB CAN EM SUB ESC FS GS RS US Dez 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 Hex Char 20 SPC 21 ! 22 ” 23 # 24 $ 25 % 26 & 27 ’ 28 ( 29 ) 2A * 2B + 2C , 2D 2E . 2F / 30 0 31 1 32 2 33 3 34 4 35 5 36 6 37 7 38 8 39 9 3A : 3B ; 3C < 3D = 3E > 3F ? Dez 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 Hex Char 40 @ 41 A 42 B 43 C 44 D 45 E 46 F 47 G 48 H 49 I 4A J 4B K 4C L 4D M 4E N 4F O 50 P 51 Q 52 R 53 S 54 T 55 U 56 V 57 W 58 X 59 Y 5A Z 5B [ 5C \ 5D ] ∧ 5E 5F Dez 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 Hex 60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F 70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7F Char ‘ a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ∼ DEL Die 32 Zeichen der ersten Spalte und das letzte Zeichen sind Steuerzeichen und nicht druckbar. Sie werden durch eine Tastenkombination, z.B. Strg+@ bzw. Ctrl+@ für ∧ @, erzeugt. Einige der Steuerzeichen können in C– bzw. C++– Quelltexten durch sogenannte Escape–Sequenzen, die mit einem Backslash beginnen, dargestellt werden. 19 Dez 0 7 8 9 10 11 12 13 Ctrl–Ch ∧ @ ∧ G ∧ H ∧ I ∧ J ∧ K ∧ L ∧ M Esc–Seq \0 \a \b \t \n \v \f \r Bedeutung Zeichenkettenende Klingel, Systempiepser Backspce, Rückwärtsschritt Horizontaltabulator Zeilenvorschub, neue Zeile Vertikaltabulator Formularvorschub Wagenrücklauf Bedeutung weiterer Steuerzeichen: NUL SOH STX ETX EOT ENQ ACK SO SI DLE DC NAK SYN ETB CAN EM SUB ESC FS GS RS US Null, keine Operation, String–Ende Beginn des Vorspanns, start of heading Anfang des Textes, start of text Ende des Textes, end of text Ende der Übertragung, end of transmission Antwortanforderung, enquiry positive Rückmeldung, acknowledge Umschaltung, shift out Rückschaltung, shift in Datenübertragung, data link escape Gerätesteuerung, device control negative Rückmeldung, negative acknowledge Synchronisierung, synchronous idle Ende des Datenblocks, end of transmission block ungültig, cancel Ende der Aufzeichnung, end of medium Zeichensubstitution, substitute character Umschaltung, escape Dateitrennzeichen, file separator Gruppentrennzeichen, group separator Satzendezeichen, record separator Gerätetrennzeichen, unit separator Bei der Arbeit unter UNIX bzw. LINUX wird grundsätzlich der ASCII–Zeichensatz verwendet, wobei die Steuerzeichen nicht immer ganz einheitlich Verwendung finden. Die Abarbeitung eines unter UNIX/LINUX laufenden Programmes kann durch Eingabe von Strg+C abgebrochen werden. Terminalausgaben lassen sich mit Strg+S stoppen (nicht abbrechen) und mit Strg+Q wieder fortsetzen. Als logout–Kommando kann auch Strg+D benutzt werden. Andere Betriebssysteme verwenden einen erweiterten 8–Bit–ASCII–Zeichensatz, bei dem aber die ersten 128 Zeichen mit dem 7–Bit–ASCII–Code übereinstimmen. 20 Zeichenketten (strings) Eine Zeichenkette bzw. ein Wort über einem Alphabet A ist eine endliche (einschließlich leere) Folge von Zeichen aus A, wobei ε die leere Zeichenkette bezeichne. Damit sind beispielsweise ”a$(KoL3x” , ”uvw=?h&&1” und ε Zeichenketten über dem ASCII–Alphabet mit den Längen (Anzahlen der besetzten Plätze) 8, 9 bzw. 0 und ”5F3AD”, ”FF” und ε Zeichenketten über dem Alphabet der Hexadezimalziffern mit den Längen 5, 2 bzw. 0. Durch Verkettung (concatenation) zweier Zeichenketten, d. h. Aneinanderfügen der Zeichenketten mit Berücksichtigung der Reihenfolge, erhält man wieder eine Zeichenkette. Man kann die Menge A∗ aller Zeichenketten über einem Alphabet A folgendermaßen induktiv definieren: A0 := {ε} A∗ := Ai+1 := Ai ◦ A S i∈N Ai für jedes i ∈ N (◦: Symbol für die Verkettung) i Es ist A die Menge aller Zeichenketten der Länge i über A. Mit A+ wird die Menge der nichtleeren Zeichenketten über A bezeichnet, d.h., es ist A+ = A∗ \ {ε}. Eine Zeichenkette Z1 ist genau dann eine Teilzeichenkette (substring) der Zeichenkette Z2, wenn es Zeichenketten U und V gibt, so dass gilt: Z2 = U ◦ Z1 ◦ V Eine Zeichenkette Z1 ist genau dann ein Anfangsstück (prefix) der Zeichenkette Z2, wenn es eine Zeichenkette V gibt, so dass gilt: Z2 = Z1 ◦ V In der Programmiersprache C werden Zeichenketten derart abgespeichert, dass jedes Zeichen unter Einhaltung der Reihenfolge des Vorkommens in der Zeichenkette durch ein Byte entsprechend seines ASCII–Codes abgespeichert und am Schluss stets zusätzlich ein Endekennzeichen, nämlich ’\0’, angefügt wird. 21 gerichteter Graph Ein gerichteter Graph G ist ein geordnetes Paar (Kn, Ka), wobei Kn eine Menge (die Menge der Knoten von G) und Ka eine binäre Relation in Kn, d.h. Ka ⊆ Kn × Kn, ist. Es heißt Ka die Kantenmenge von G. Zwei Knoten k1 und k2 , für die (k1, k2) ∈ Ka gilt, heißen benachbart (adjazent). Sie werden bei der zeichnerischen Darstellung des Graphen G durch eine von k1 nach k2 gerichtete Kante verbunden (gerichtete Kanten werden in der Literatur manchmal auch als Bögen bezeichnet). Es heißt k1 ein Vorgänger von k2 , und k2 heißt ein Nachfolger von k1. Beispiel: Kn := {1,2,3,4}, Ka := {(1,1), (1,2), (1,4), (2,4), (3,2), (4,3)} zeichnerische Darstellung: 1 2 3 4 zugehörige Adjazenzmatrix: 1 0 0 0 1 0 1 0 0 0 0 1 1 1 0 0 Ein Weg (k1, k2, · · · , kn) in einem gerichteten Graph ist eine Liste bzw. ein n–Tupel von Knoten, so dass stets ki+1 ein Nachfolger von ki , 1 ≤ i ≤ n − 1, ist. Die Länge dieses Weges ist n − 1 (Anzahl der Bögen des Weges). Ein Zyklus eines gerichteten Graphen ist ein Weg mindestens der Länge 1, der am selben Knoten beginnt und endet. Z.B. ist im obigen Graphen der Bogen (1,1) ein Zyklus der Länge 1. 22 ungerichteter Graph Ein ungerichteter Graph G ist ein geordnetes Paar (Kn, Ka), wobei Kn eine Menge (die Menge der Knoten von G) und Ka eine Menge von Zweiermengen mit Elementen aus Kn ist. Es heißt Ka die Kantenmenge von G. Zwei Knoten k1 und k2 , für die {k1 , k2} ∈ Ka gilt, heißen benachbart (adjazent). Sie werden bei der zeichnerischen Darstellung des Graphen G durch eine k1 mit k2 verbindende ungerichtete Kante verbunden. Beispiel: Kn := {1,2,3,4}, Ka := {{1}, {1,2}, {1,4}, {2,4}, {3,2}, {4,3}} (beachte: {1,1}= {1}) zeichnerische Darstellung: 1 2 3 4 die zugehörige Adjazenzmatrix ist jetzt symmetrisch: 1 1 0 1 1 0 1 1 0 1 0 1 1 1 1 0 Ein Weg in einem ungerichteten Graph ist eine Liste (bzw. n–Tupel) (k1, k2, · · · , kn) von Knoten, so dass stets ki+1 und ki , 1 ≤ i ≤ n − 1, benachbart sind. Die Länge dieses Weges ist n − 1 (Anzahl der Kanten des Weges). Ein Zyklus eines ungerichteten Graphen ist ein Weg mindestens der Länge 1, der am selben Knoten beginnt und endet. Wenn in einem (gerichteten oder ungerichteten) Graphen durch eine Funktion f: Ka → M jeder Kante ein Element aus einer bestimmten Menge M zugeordnet wird, so spricht man von einem kantenbewerteten Graphen. Analog ist der Begriff knotenbewerteter Graph definiert. 23 Variablen–Begriff Eine Variable hat in der Informatik 3 Bestandteile: ihren Namen, ihren Typ (festgelegt durch eine Typdeklaration; damit ist auch festgelegt, welche Werte die Variable überhaupt annehmen kann) und ihre Adresse (Nummer einer Speicherzelle desjenigen Speicherbereiches, in dem der aktuelle Wert der Variablen abgelegt ist). Manchmal wird als vierter Bestandteil einer Variablen auch ihr aktueller Wert mit hinzugenommen. Variable Name Typ Adresse Wert Der Name ist in der Regel eine Zeichenkette, die mit einem (lateinischen) Buchstaben oder einem Unterstreichungsstrich (underscore) beginnt und an den evtl. weiteren Positionen Buchstaben, Ziffern oder den Unterstreichungsstrich hat. Durch den Typ ist festgelegt, wieviele Bytes zur Abspeicherung des jeweiligen Wertes der Variablen bei der Typdeklaration reserviert werden und wie die Inhalte der Bytes zu interpretieren sind. Beispielsweise werden durch die Variablendefinition (Variablendeklaration plus Zuweisung eines Speicherbereichs) int i; in einem C–Programm der Variablen mit dem Namen i vier Bytes zum Abspeichern ihres jeweils aktuellen Wertes, der im Intervall [−231, 231 − 1] liegen kann, zugewiesen. Durch die Variablendefinition plus Initialisierung int i=1; erhält die Variable zusätzlich den (Anfangs–)Wert Eins (der natürlich später im Programm geändert werden kann). Siehe hierzu auch die Folien cprog09.pdf zur C–Programmierung. 24 (naiver bzw. intuitiver) Algorithmusbegriff Ein Algorithmus ist ein Verfahren, das Schritt für Schritt in diskreten Zeitabschnitten (Takten) abläuft, wobei zusätzlich gilt: der erste Schritt ist festgelegt (eindeutiger Start) immer dann, wenn ein Schritt ausgeführt worden ist, steht fest, welches der nächste Schritt bzw. ob das Verfahren beendet ist (Eindeutigkeit der Reihenfolge) das Verfahren ist durch einen endlichen Text (in einer genügend ausdrucks- fähigen natürlichen oder künstlichen Sprache) notierbar (Endlichkeit der Darstellbarkeit) man kann mit seiner Hilfe alle Aufgaben eines bestimmten Typs, etwa abhängig von Parametern, lösen (Universalität) Von den einzelnen Schritten wird verlangt, dass sie wirklich ausführbar sind von einem realen oder gedachten Rechenautomaten mit geeignetem Befehlsvorrat. lokale Determiniertheit eines Algorithmus: die in jedem Schritt auszuführende Aktion muss eindeutig sein globale Determiniertheit eines Algorithmus: für alle in Frage kommenden Eingabedaten (inputs) werden in eindeutiger Weise Ausgabedaten (outputs) erzeugt, oder der Algorithmus terminiert nicht In den einzelnen Schritten werden im einfachsten Fall Variablen Werte zugewiesen, z.B. i:=1 (Pascal– bzw. Delphi–Notation) bzw. i=1 (C–, C++–, Java– Notation); gesprochen: ”i ergibt sich zu Eins”. 25 (3n+1)–Algorithmus mit Ausgabe der Zwischenwerte und der Anzahl der durchgeführten Iterationen Start E: n n natürliche Zahl i:=0 i zählt Iterationen n>1 nein A: i i:=i+1 ja Stop A: n nein n gerade ja n:=3*n+1 n:=n/2 Bis heute konnte nicht gezeigt werden, dass der Algorithmus für jeden zugelassenen Eingabewert n terminiert, d.h. die Stop–Anweisung erreicht. (Aufpassen bei der Implementierung: Es können Zahlbereichsüberschreitungen (overflow) vorkommen, die nicht angezeigt werden.) 26 (3n+1)–Algorithmus als Struktogramm E: n /* n als natuerliche Zahl */ i := 0 while n > 1 n gerade ja n := n/2 nein n := 3n + 1 A: n i := i+1 A: i /* Anzahl der durchgefuehrten Iterationen */ Andere Notationsformen für die Ein– und Ausgabeanweisungen: statt E: n A: n notiere n n bzw. n n 27 Notation des (3n+1)–Algorithmus in einem Pseudocode (nicht genormte semiformale Notation) Eingabe: n; /* n als natürliche Zahl */ i := 0; /* weise i den Wert 0 zu */ while (n > 1) if (n gerade) n := n/2; else n := 3 ∗ n + 1; end if; /* Ende der if–Anweisung */ Ausgabe: n; /* Ausgabe des Wertes von n */ i := i + 1; /* erhöhe den Wert von i um Eins */ end while; /* Ende der while–Schleife */ Ausgabe: i; /* Anzahl der durchgeführten Iterationen */ Pseudocode: künstliche Sprache zum Entwerfen und Darstellen von Algorithmen zur Darstellung der strukturierenden Elemente bzw. des Kontrollflus- ses (z. B. Auswahl, Iteration, Blockbegrenzung) wird oft eine PASCAL– oder C–ähnliche Notation verwendet die eigentlichen Verarbeitungsschritte können verbal oder formal formu- liert sein 28 Euklidischer Algorithmus zur Bestimmung von ggt(m, n) für m, n ∈ N (Euklid, um 300 v. Chr.) E: m, n /* m, n nat. Zahlen mit n > 0 */ r := m mod n m := n n := r until n=0 A: m /* der ggt der urspruenglichen m, n */ wenn zusätzlich ggt(0, 0) := 0 vereinbart wird: E: m ,n /* m, n natuerliche Zahlen */ while n = 0 r := m mod n m := n n := r A: m /* der ggt der urspruenglichen m, n */ Siehe beispielsweise auch euclid.pdf unter http://www.imn.htwk-leipzig.de/~jahn/Cprog/Alg_Inf_Jahr_pdf Dort befinden sich viele andere grundlegende Algorithmen. 29 ein schneller Algorithmus zur Berechnung von Potenzen mit natürlichzahligen Exponenten Der folgende Algorithmus (square and multiply) liefert für jede reelle Zahl x 6= 0 und jede natürliche Zahl n als Ergebnis z den Wert der Potenz xn : x, n z:=1 while n>0 while n ist gerade n := n div 2 x := x * x n := n−1 z := z * x z Die Anzahl der durchzuführenden Multiplikationen ist nach oben beschränkt durch 2 ∗ ⌊log2 (n)⌋ + 1. Bei der ”gewöhnlichen” Potenzberechnung x, n z:=1 for i:=1 to n z := z * x z müssen dagegen n Multiplikationen durchgeführt werden. 30 Suche in Texten Ein einfacher Algorithmus, der alle Positionen der Zeichenkette ”a0 a1 · · · an−1 ” ausgibt, ab denen die Zeichenkette ”b0b1 · · · bm−1” dort als Teilzeichenkette vorkommt, ist der folgende: Eingabe: n, m; /* n, m als natürliche Zahlen */ Eingabe: a0 a1 · · · an−1, b0 b1 · · · bm−1 for i:=0 to n-m do gefunden:=1; for j:=0 to m-1 do if (ai+j 6= bj ) gefunden:=0; end if; end for; if (gefunden) Ausgabe: i; /* Ausgabe einer Position */ end if; end for; Sind beispielsweise n = 8, m = 2, ”a0 a1 · · · an−1 ”= ”adabcaab”und ”b0b1 · · · bm−1” = ”ab”, so werden duch den Algorithmus die folgenden Werte von i ausgegeben: 2, 6. 31 String–Matching–Problem String–Matching–Problem: Kommt eine bestimmte Zeichenkette als Teilzeichenkette in einer anderen Zeichenkette vor? (Entscheidungsproblem) erweitertes String–Matching–Problem: Bestimme alle Positionen in einer Zeichenkette, ab denen eine andere Zeichenkette dort vorkommt! (Berechnungsproblem) Beispiel für ein Entscheidungsproblem: Es seien A ein Alphabet mit {′ M ′ ,′ S ′ } ⊆ A und S := s0 · · · sn−1 eine Zeichenkette über A. Kommt die Zeichenkette ”MMS” als Teilzeichenkette in S vor? Modellierung dieses Problems mit Hilfe eines Akzeptors: ’M’ A\{’M’} z0 ’M’ z1 ’M’ ’S’ z2 z3 A A\{’M’} A \{’M’,’S’} Dieser Automat befindet sich anfangs im Zustand z0 (Anfangszustand) und arbeitet getaktet. Als erstes Zeichen von S wird s0 eingegeben. Je nachdem, um welches Zeichen des Alphabetes es sich handelt, geht der Automat in den Zustand z1 über (falls s0 =′ M ′ ist), oder er bleibt im Zustand z0 (falls s0 ∈ A \ {′M ′ } ist). Im zweiten Takt wird s1 betrachtet und ein entsprechender Zustandsübergang durchgeführt usw. Ausschlaggebend ist, ob der Automat sich unmittelbar nach Verarbeitung von sn−1 im Zustand z3 (Endzustand) befindet oder nicht. Genau dann, wenn er sich zuletzt in diesem Zustand befindet, wird die Zeichenkette S akzeptiert, was hier bedeutet, dass sie die Zeichenkette ′′ MMS ′′ (mindestens einmal) als Teilzeichenkette enthält. 32 ein aus dem Akzeptor resultierender Algorithmus Der folgende Programmablaufplan entstand durch Übersetzung des Akzeptors, der genau diejenigen Zeichenketten akzeptiert, die ”MMS” als eine Teilzeichenkette von sich enthalten, in einen Algorithmus. Die Zustände des Akzeptors werden dabei durch Werte der Variablen z repräsentiert. Eingegeben werden hier n (die Länge der Zeichenkette) sowie die Zeichenkette ”s0s1 · · · sn−1”. (Bezogen auf die Programmiersprache C könnte man den Test, ob i < n ist, auch ersetzen durch den Test, ob si 6=’\0’ ist.) START E: n, s o , ... , s n−1 z := 0 i := 0 i<n and z < 3 nein z=3 nein ja i := i+1 ja z=0 A: "ZK akzept." nein ja z=1 nein s i = ’M’ ja z := 1 A: " ZK nicht akzept." nein ja si = ’M’ nein ja z := 2 s i = ’S’ z := 0 STOP nein ja s i = ’M’ z := 3 nein z := 0 33 ja Algorithmus als Struktogramm Das folgende Struktogramm entstand durch Übersetzung des obigen Programmablaufplanes in die Sprache der Struktogramme. Bezüglich des Tests, ob i < n ist oder nicht, gilt die gleiche Aussage wie auf der vorhergehenden Folie. n, so, ... , s n−1 z := 0 ; i := 0 while i<n and z < 3 z=0 j j s i = ’M’ n n z=1 j j s i = ’M’ n n s = ’S’ i j n s i = ’M’ n j z := 1 z := 2 z := 3 z := 0 z := 0 i := i+1 z=3 j "Zeichenkette akzeptieren" n "Zeichenkette nicht akzeptieren" 34 Prüfungsplanung Es sollen Prüfungen in verschiedenen Fächern zeitlich so geplant werden, dass kein Student mehr als eine Prüfung pro Tag hat und zusätzlich die Anzahl der Prüfungstage möglichst klein ist. Modellierung durch einen (ungerichteten) Fächerkonfliktgraphen: Die einzelnen zur Prüfung anstehenden Fächer werden als Knoten des Graphen dargestellt. Es werden dann alle Paare von Knoten betrachtet. Genau dann, wenn die beiden zu einem gerade betrachteten Paar von Knoten gehörenden Prüfungen von mindestens ein und demselben Studenten zu absolvieren sind, werden diese beiden Knoten durch eine Kante miteinander verbunden (sie stehen in Konflikt zueinander). Beispiel: Es seien vier Studenten S1, · · · , S4 in fünf Fächern F 1, · · · , F 5 zu prüfen. Die folgende Tabelle gibt an, wer in welchem Fach zu prüfen ist (Markierung durch ′′ ∗′′ ): S1 S2 S3 S4 F1 F2 F3 F4 F5 ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ Zugehöriger Fächerkonfliktgraph: F1 F5 F2 F3 F4 Ebenfalls durch einen Konfliktgraphen kann beispielsweise die folgende Problemstellung modelliert werden: Ein Unternehmen benötigt zur Produktion verschiedene Chemikalien, von denen nicht alle im gleichen Raum gelagert werden dürfen. Von je zwei Chemikalien ist aber bekannt, ob sie gefahrlos im gleichen Raum gelagert werden können oder nicht. 35 Prüfungsplanung auf der Grundlage des Fächerkonfliktgraphen (Kn, Ka) Es werden nacheinander maximale unabhängige Teilmengen der Knotenmenge Kn bestimmt, wobei jede dieser Mengen die Fächer eines weiteren Prüfungstages festlegt. Dabei heiße eine nichtleere Teilmenge der Knotenmenge Kn genau dann unabhängig, wenn keine zwei Knoten dieser Menge durch eine Kante aus der Kantenmenge Ka des Graphen miteinander verbunden sind. Alle Fächer einer unabhängigen Knotenmenge können somit an ein und demselben Tag geprüft werden. Eine unabhängige Teilmenge der Knotenmenge heiße genau dann maximal unabhängig, wenn die Hinzunahme irgendeines weiteren Knotens die Unabhängigkeit zerstören würde. Somit heißt eine Menge M genau dann maximale unabhängige Teilmenge der Knotenmenge Kn des Graphen (Kn, Ka), wenn gilt: M ⊆ Kn ∧ M 6= ∅ ∧ ∀a∀b(a ∈ M ∧ b ∈ M ∧ a 6= b → {a, b} ∈ / Ka) ∧ ∀a(a ∈ Kn \ M → ∃b(b ∈ M ∧ {a, b} ∈ Ka)) Dieser prädikatenlogische Ausdruck in Worten: M ist eine Teilmenge der Knotenmenge und M ist nicht leer und keine zwei Elemente aus M sind durch eine Kante miteinander verbunden und das Hinzunehmen eines weiteren Knotens zu M zerstört die Unabhängigkeit von M Beginnt man anhand des obigen Fächerkonfliktgraphen die Prüfungsplanung mit dem Fach F 1 am ersten Prüfungstag, so können beispielsweise auch noch die Fächer F 3 und F 4 an diesem Tag geprüft werden, mehr allerdings nicht. Es ist also {F 1, F 3, F 4} eine maximale unabhängige Teilmenge der Knotenmenge Kn. Die beiden übrigbleibenden Fächer F 2 und F 5 sind abhängig voneinander, so dass sich zwei weitere Prüfungstage mit jeweils nur einem Prüfungsfach ergeben. Somit könnten die Fächer wie folgt auf drei Prüfungstage pt(1), pt(2), pt(3) verteilt werden: pt(1) pt(2) pt(3) F 1, F 3, F 4 F 2 F5 (Die eben angegebene Verteilung der Fächer ist nicht die einzig mögliche.) 36 ein Pseudocode für die Prüfungsplanung auf der Grundlage des Fächerkonfliktgraphen (Kn, Ka) Sukzessive Bestimmung maximaler unabhängiger Teilmengen von Kn, wobei diese Mengen pt(1), pt(2), · · · die Mengen der Fächer, die an paarweise verschiedenen Prüfungstagen geprüft werden sollen, sind: Eingabe: Kn, Ka i := 0; while (Kn 6= ∅) { i := i + 1; /* neuer Prüfungstag */ f := bel(Kn); pt(i) := {f }; /* nimm beliebiges Element f aus Kn */ /* Fach f wird am i–ten Tag geprüft */ Kn := Kn \ {f }; /* nimm f aus der Knotenmenge heraus */ forall (s ∈ Kn) /* überprüfe, ob s unabhängig ist vom bisherigen pt(i) */ if (∀t(t ∈ pt(i) → {s, t} 6∈ Ka)) { } pt(i) := pt(i) ∪ {s}; /* auch Fach s wird am i–ten Tag geprüft */ Kn := Kn \ {s}; /* die folgende geschachtelte Schleife kann auch weggelassen werden */ forall (s ∈ pt(i)) forall (e ∈ Ka) if (s ∈ e) } Ka := Ka \ {e}; /* Ausgabe: */ for (j := 1; j <= i; j + +) print(j, pt(j)); 37 Zahlendarstellungen in Stellenwert– bzw. Positionssystemen Ein Stellenwert– bzw. Positionssystem wird festgelegt durch eine Basis b als natürliche Zahl größer als Eins und die Menge Z seiner Ziffern, wobei Z = {0, 1, · · · , b − 1} ist. Eine natürliche Zahl kann dann dargestellt werden als Zeichenkette über Z, wobei üblicherweise führende Nullen weggelassen werden (außer es handelt sich um die Zahl Null selbst). Eine solche Zeichenkette zn zn−1 · · · z0 wird als b–adische Darstellung der natürlichen Zahl mit dem Wert zn ∗ bn + zn−1 ∗ bn−1 + · · · + z1 ∗ b + z0 bzw. n X i=0 zi ∗ bi bezeichnet. b = 2 : Dualsystem (von Gottfried Wilhelm Leibniz (1646–1716) eingeführt) b = 8 : Oktalsystem b = 10 : Dezimalsystem b = 16 mit Z = {0, 1, · · · , 9, A, B, C, D, E, F } : Hexadezimalsystem Man kann aber auch als Ziffern die zehn Tetraden 0000, 0001, 0010, 0011, 0100, 0101, 0110, 0111, 1000, 1001, die Dualdarstellungen der Zahlen von Null bis Neun sind, und als Basis b = 10 verwenden und erhält damit das BCD–System (BCD: binary coded decimal) Eine echt gebrochene positive reelle Zahl mit endlich vielen Ziffern in b–adischer Darstellung wird in Festpunktschreibweise in der Gestalt 0.z−1z−2 · · · z−n notiert und hat den Wert −1 X i=−n zi ∗ bi 38 Konvertierung einer natürlichen Zahl z von ihrer Dezimaldarstellung in ihre Darstellung zur Basis b≥2 Man geht aus von einem Ansatz z = zn ∗ bn + zn−1 ∗ bn−1 + · · · + z1 ∗ b + z0 mit zunächst unbekannten n, zn , · · · , z0 und erkennt, dass gilt: z0 = z mod b z1 = ((z − z0 ) div b) mod b z2 = (((z − z0 ) div b) − z1 ) div b mod b usw. Durch fortgesetzte Division mit Rest erhält man also die gesuchten Ziffern, was auf folgenden Algorithmus führt: START z, b natürliche Zahlen, b>=2 E: z, b i:=0 Position der zu berech− nenden Ziffer q:=z div b r:=z mod b r: i−te Ziffer A: r, i q=0 ja nein STOP i:=i+1 r:=q mod b q:=q div b Länge der b–adischen Darstellung einer natürlichen Zahl z > 0: ⌊logb (z)⌋ + 1 bzw. ⌊log10(z)/ log10 (b)⌋ + 1 39 Konvertierung einer natürlichen Zahl zn zn−1 · · · z0 zur Basis b≥2 in ihre Dezimaldarstellung mittels HORNER–Schema Hier nutzt man aus, dass zn ∗ bn + zn−1 ∗ bn−1 + · · · + z1 ∗ b + z0 bzw. auch folgendermaßen geklammert werden kann: Pn i=0 zi ∗ bi (· · · (((zn ∗ b + zn−1 ) ∗ b + zn−2 ) ∗ b + zn−3) ∗ b + · · · + z1 ) ∗ b + z0 Wertet man diesen Term entsprechend seiner Klammerung von innen nach außen aus, so führt das auf das Horner–Schema, das algorithmisch wie folgt dargestellt werden kann: START zn , ... , z o Ziffern der Zahl zur Basis b, b>=2 E: n, z n , ... , z o i := n z := z i i>0 nein ja i := i−1 z := z*b + z i A: z STOP 40 Zahl zur Basis 10 Konvertierung einer echt gebrochenen positiven Dezimalzahl q in ihre Darstellung zur Basis b, b≥2 Um die Ziffern der b–adischen Darstellung von q zu erhalten, macht man den Ansatz q = z−1 ∗ b−1 + z−2 ∗ b−2 + · · · mit zunächst unbekannten z−1, z−2 · · · und unbekannter Anzahl der Ziffern. Man erkennt, dass gilt: z−1 = ⌊q ∗ b⌋ z−2 = ⌊(q ∗ b − z−1 ) ∗ b⌋ usw. Dies führt auf den folgenden Algorithmus: START q: echt gebrochene Zahl >0 in Dezimaldarstellung E: q, b Position der zu bestimmenden Ziffer nach dem Punkt i := 1 q=0 ja nein q := q * b STOP z:=floor(q) A: z, i q := q − z i := i + 1 Eine rationale Zahl hat stets eine endliche oder periodische unendliche b–adische Darstellung. 41 Interne Darstellungen ganzer Zahlen Falls n Bit für die interne Darstellung zur Verfügung stehen, so werden Konstanten und die Werte von Variablen von unsigned–Typen (unsigned short, unsigned int, unsigned long und unsi- gned long long) durch ihr duales Äquivalent intern abgespeichert, so dass also genau die Werte von 0 bis 2n − 1 möglich sind signed–Typen (short, int, long und long long) derart intern abgespei- chert, dass die Werte von 0 bis 2n−1 − 1 durch ihr duales Äquivalent (genau hier hat das höchstwertige Bit den Wert 0) dargestellt und die übrigen möglichen internen Darstellungen (das sind diejenigen, bei denen das höchstwertige Bit den Wert 1 hat) als negative Werte interpretiert werden, deren Betrag das duale Äquivalent des Zweierkomplements der internen Darstellung ist (siehe dazu beispielweise auch die entsprechenden Folien von cprog09.pdf) Das Zweierkomplement einer natürlichen Zahl k < 2n bzgl. eines Datenformats von n Bit hat den Wert 2n − k. Es gilt für jedes k mit 0 ≤ k < 2n : (Zweierkomplement von k bzgl. n Bit) = (Einerkomplement von k bzgl. n Bit) + 1, wobei das Einer– bzw. Stellenkomplement von k bzgl. n Bit als bitweise Negation der dualen Darstellung von k, vorher durch führende Nullen auf die Länge n ergänzt, ist. So sind also beispielsweise als Werte von Variablen des Typs short (intern 16 Bit) die Zahlen von −215 bis 215 − 1 möglich mit folgenden internen Darstellungen: dezimaler Wert −32768 −32767 .. . −2 −1 0 1 2 .. . 32767 interne Darstellung 10000000 00000000 10000000 00000001 .. . 11111111 11111110 11111111 11111111 00000000 00000000 00000000 00000001 00000000 00000010 .. . 01111111 11111111 (siehe z. B. auch die Programme int dar1.c und limit int1.c) 42 reelle Datentypen Reelle Zahlen werden intern als Gleitpunktzahlen (Fließkommazahlen, floating point numbers) abgespeichert. Über die Tastatur werden sie als Festpunktzahlen (z. B. 3.1415) oder als Gleitpunktzahlen (z. B. 314.15e-2 oder 314.15E-2) eingegeben und dann in ihre internen Darstellungen konvertiert. Beachte: Schon beispielsweise die Dezimalzahl 0.1 kann intern nicht exakt abgespeichert werden, da ihre duale Darstellung periodisch unendlich ist. Von der Gleitpunktzahl 314.15e-2 ist 314.15 ihre Mantisse, e das Exponentenkennzeichen (für die Basis 10) und -2 der Exponent (zur Basis 10). Intern werden Gleitpunktzahlen im ANSI/IEEE–Format abgespeichert, und zwar in der Regel als normalisierte Gleitpunktzahlen, wobei anstelle des Exponenten die Charakteristik verwendet wird. Eine normalisiert duale Mantisse beginnt mit einer 1, gefolgt vom Dualpunkt und weiteren Dualziffern. Dabei wird die 1 vor dem Punkt nicht mit abgespeichert, aber natürlich bei Berechnungen mit berücksichtigt. Die folgende Tabelle zeigt für die reellen Datentypen float, double und long double die Anzahlen der für ihre internen Darstellungen verwendeten Bytes und wieviele Bits davon für die Mantissen (ohne das Normalisierungsbit), die Charakteristiken und das Vorzeichen der Zahl benutzt werden: Anz. Bytes Anz. Bits Mant. Anz. Bits Char. Anz. Bits Vorz. float 4 23 8 1 double 8 52 11 1 long double 10 (Intel) 63 16 1 16 (Sparc) 112 15 1 (siehe insbesondere auch das Programm limit float1.c) Je größer die Anzahl der Mantissenstellen ist, um so dichter liegen die Maschinenzahlen und umso genauer können Zahlen intern dargestellt werden. Die Lücken zwischen benachbarten Maschinenzahlen werden nach außen hin (von der Null weg) immer größer. Je größer die Anzahl der Bits für die Charakteristik ist, umso größer ist die jeweils größte intern darstellbare und umso kleiner ist die kleinste positive intern darstellbare Maschinenzahl. Die intern darstellbaren Gleitpunktzahlen liegen symmetrisch zur Null. 43 Interne Darstellung von 4–Byte–Gleitpunktzahlen nach ANSI/IEEE 754 ANSI: American Standard Institute IEEE: Institute of Electrical and Electronics Engineers Vorzeichen VZ Charakteristik c Mantisse m 1 Bit 8 Bit 23 Bit Wert einer solchen internen Darstellung: c 6= 0000 0000 (c 6= 0) und c 6= 1111 1111 (c 6= 255): (−1)V Z ∗ 2c−127 ∗ (1.m)2 (normalisierte Darstellung) größte darstellbare normalisierte Zahl: (−1)0 ∗ 2127 ∗ (1.7F F F F F )16 = 2127 ∗ (2 − 2−23) ≈ 3.4 ∗ 1038 kleinste darstellbare normalisierte positive Zahl: (−1)0 ∗ 21−127 ∗ (1.0 · · · 0)2 = 2−126 ≈ 1.1754 ∗ 10−38 Sonderfälle: c = 0, m = 0: 0 c = 0, m 6= 0: (−1)V Z ∗ 2−126 ∗ (0.m)2 (bei sehr kleinen Werten wird die Normalisierung aufgehoben, so dass 2−126 ∗ 2−23 = 2−149 ≈ 1.4013 ∗ 10−45 die kleinste intern darstellbare positive Zahl ist; im Falle c=0 ist -126 als zugehöriger Exponent festgelegt, diese Zahl ist auch für normalisierte interne Darstellungen der kleinstmögliche Exponent) c = 255, m = 0: ”(−1)V Z ∗ ∞” √ c = 255, m = 6 0: NaN (Not a Number, z.B. für ” −2”, ”0/0”, ”0 ∗ ∞”) Beispiel: interne Darstellung der Dezimalzahl -19.625 im float–Format Da (−19.625)10 = (−10011.101)2 = −24 ∗ (1.00111010 · · · 0)2 ist, ergibt sich 1 1000 0011 001 1101 0000 0000 0000 0000 (die ”1” vor dem Dualpunkt wird nicht mit abgespeichert). Die internen Darstellungen von Gleitpunktzahlen behandeln z. B. die Programme int float1.c und int float2.c. siehe dazu beispielsweise Blieberger, J., Burgstaller, B. und G.–H. Schildt: Informatik Grundlagen. Springer–Verlag Wien 2005 44 Gleitpunktarithmetik Wenn im Computer mit Gleitpunktzahlen gerechnet wird, so werden laufend Rundungsfehler gemacht. Diese werden nicht angezeigt, nur wenn die größte intern darstellbare Zahl (z. B. ≈ 3.4 ∗ 1038 für den Datentyp float) überschritten oder die kleinste (negative) intern darstellbare Zahl (z. B. ≈ −3.4 ∗ 1038 für den Datentyp float) unterschritten werden, so werden eine overflow–Fehlermeldung ausgegeben und die Berechnung abgebrochen. Addition bzw. Subtraktion zweier Gleitpunktzahlen im Computer: falls die beiden Exponenten unterschiedlich groß sind, so werden der klei- nere der beiden Exponenten an den größeren angepasst und der Dualpunkt entsprechend verschoben; jetzt stehen die Mantissenstellen zu gleichen Zweierpotenzen untereinander Addition bzw. Subtraktion der Mantissen Normalisierung des Ergebnisses (falls notwendig) Rundung auf das vorgegebene interne Format (falls Anzahl der Mantissen- stellen zu groß) Multiplikation bzw. Division zweier Gleitpunktzahlen im Computer: Multiplikation bzw. Division der Mantissen Addition bzw. Subtraktion der Exponenten Normalisierung des Ergebnisses (falls notwendig) Rundung auf das vorgegebene interne Format (falls Anzahl der Mantissen- stellen zu groß) Bezüglich dieser Gleitpunktarithmetik gelten aufgrund der Rundungsfehler manche der Rechengesetze, wie sie bei exakter Rechnung gelten, nicht mehr. Auch ist der Test zweier Gleitpunktzahlen im Computer auf Gleichheit nur mit Vorsicht zu verwenden, da die exakten Werte i. A. nicht bekannt sind. 45 Rundungsfehler und Maschinengenauigkeit Rundung einer normalisierten dualen Gleitpunktzahl x mit mehr als t Mantissenstellen nach dem Dualpunkt auf t Stellen nach dem Dualpunkt: Es sei x := 1.z1z2 · · · zt zt+1 · · · ∗ 2e Dann sei rd(x) := 1.z1z2 · · · zt ∗ 2e (1.z1z2 · · · zt zt+1 + 2−t−1) ∗ 2e falls zt+1 = 0 falls zt+1 = 1 Damit gilt für den relativen Fehler von rd(x), falls x 6= 0 ist: rd(x) − x x ≤ 2−t−1 = 1 ∗ 2−t 2 Werden anstelle des Rundens die Mantissenstellen nach zt einfach abgeschnitten (”chopping”) und heißt der dabei erhaltene Wert chp(x), so gilt für dessen relativen Fehler chp(x) − x ≤ 2−t (x 6= 0) x Diese Zahl 2−t heißt Maschinengenauigkeit und hat also beispielsweise für den Datentyp float den Wert 2−23 (die Maschinengenauigkeiten sind in float.h definiert und haben dort die Namen FLT EPSILON, DBL EPSILON und LDBL EPSILON, siehe auch limit float1.c und masch gen.c). Pseudocode zur Berechnung der Maschinengenauigkeit masch eps: masch_eps := 0.5; x := 1.0; while (masch_eps + x > x) masch_eps := masch_eps * 0.5; (fortgesetzte Halbierung) masch_eps := masch_eps * 2.0; (letzte Halbierung rueckgaengig) Ausgabe: masch_eps Es ist masch eps die kleinste positive Zweierpotenz mit negativem Exponenten, so dass die Gleitpunktzahl 1.0 + masch eps im Computer gerade noch als Zahl größer als 1.0 erkannt wird. Zahlen x, deren exakte Werte betragsmäßig zwischen 0 und der kleinsten (nichtnormalisierten) internen Gleitpunktzahl liegen, befinden sich im underflow– Bereich und werden auf den Wert 0 gerundet. Hier ergibt sich ein relativer Fehler von 1 bzw. von 100%, sämtliche in x enthaltenen Informationen gehen verloren! 46 Numbercruncher Top 3 der Supercomputer (Juni 2009) nach LINPACK-Benchmark (Quelle: www.top500.org/lists/2009/06) LINPACK: Numerische Programmbibliothek zum Lösen von linearen Gleichungssystemen, wird aber auch als Name für ein Programm zum Messen der Geschwindigkeit eines Computers verwendet. LINPACK-Programme sind normalerweise in Fortran verfasst. Rang Hersteller Rechner Standort Leistung (Petaflops) 1 IBM Roadrunner PowerXCell (12.8 GFlops) Los Alamos National Labs, USA 1.105 2 Cray Inc. Jaguar Cray XT5 AMD x86 64 Opteron Quad Core (9.2 GFlops) Oak Ridge National Labs, Oak Ridge, USA 1.059 3 IBM JUGENE, Blue Gene/P PowerPC 450 (3.4 GFlops) Forschungszentrum Jülich Deutschland 0.826 JUGENE ist der erste europäische Supercomputer mit einer Leistung von rund einem Petaflops, also einer Billiarde Rechenoperationen pro Sekunde. Dies entspricht der Rechenleistung von mehr als 25 000 handelsüblichen PCs, vorausgesetzt, sie würden über ein genau so starkes Datennetzwerk verbunden wie die Prozessoren in einem Supercomputer. Er besitzt 73728 Prozessoren, die in 72 wassergekühlten Schränken untergebracht sind und hat eine Leistungsaufnahme von 2.2 Megawatt. Der Arbeitsspeicher ist rund 144 Terabyte groß. JUGENE wird benutzt für Berechnungen und Simulationen in den Bereichen Physik, Chemie, Biologie, Astronomie sowie Material– und Umweltwissenschaften. Im Jahr 2016 soll die 100–Petaflops–Marke geknackt werden. Teraflops und Rundungsfehler: Schon ein Teraflops–Computer kann in jeder Sekunde soviele Zahlen erzeugen, wie auf einen Stapel von 100 Kilometern Höhe dünner Papierblätter passen, wenn auf jedes Blatt 1000 Zahlen geschrieben würden und 100 Blatt Papier einen Stapel von 1 cm Höhe ergeben. So drängt sich die Frage auf, wie genau ein berechnetes Ergebnis ist, denn bei Durchführung jeder Gleitpunktoperation werden Rundungsfehler gemacht (siehe auch die Programme double1.c bis double9.c). 47 Begriff der Grammatik Eine Grammatik G ist ein Viertupel (T, N, P, S) mit (a) T und N sind endliche Mengen mit T ∩ N = ∅; T heißt Menge der Terminalsymbole, N heißt Menge der Nichtterminalsymbole (Metasymbole) (b) P ist eine endliche Menge von Produktionsregeln (Ableitungsregeln) der Gestalt u → v, wobei u, v ∈ (T ∪ N )∗ sind und u wenigstens ein Nichtterminalsymbol enthält Anwendung einer solchen Regel: u darf in einer Ableitung durch v ersetzt werden (c) S ist das Startsymbol (S ∈ N ) Eine Grammatik heißt genau dann kontextfrei, falls für alle Produktionsregeln gilt, dass die linke Seite u ein Nichtterminalsymbol ist. Die durch eine Grammatik G erzeugte Sprache L(G) bestehe aus genau denjenigen Zeichenketten, die höchstens Terminalsymbole enthalten und aus dem Startsymbol in endlich vielen Schritten abgeleitet werden können. Zwei Grammatiken G1 und G2 heißen genau dann äquivalent, wenn L(G1) = L(G2) ist. Kontextfreie Grammatiken spielen eine herausragende Rolle bei der Definition der Syntax von Programmen. 48 Backus–Naur–Grammatiken Bei den Erweiterten Backus–Naur–Form–Grammatiken (EBNF–Grammatiken) handelt es sich um eine spezielle Notationsform für kontextfreie Grammatiken. Dabei wird anstelle von ”u → v” meist ”< u > ::= v” geschrieben, und u muss ein Nichtterminalsymbol sein. Weiterhin werden folgende Vereinbarungen getroffen: die endlich vielen Regeln < u > ::= v1, < u > ::= v2, · · · , < u > ::= vn können zusammengefasst werden in < u > ::= v1|v2 | · · · |vn (Alternative) die beiden Regeln < u > ::= αγ, < u > ::= αβγ können zusammengefasst werden in < u > ::= α[β]γ (Option) die drei Regeln < u > ::= αγ, < u > ::= α < u1 > γ, < u1 > ::= β | < u1 > β können mit u als Startsymbol zusammengefasst werden in < u > ::= αβ ∗ γ (manchmal auch < u > ::= α{β}γ) (Wiederholung) die zwei Regeln < u > ::= α < u1 > γ, < u1 > ::= β | < u1 > β können mit u als Startsymbol zusammengefasst werden in < u > ::= αβ + γ (Wiederholung, mindestens einmal) Bei dieser Notationsform muss man sicherstellen, dass die spitzen, eckigen und geschweiften Klammern sowie der senkrechte Strich nicht zu N ∪ T gehören. Definition der Menge aller Zeichenketten über {a, b} der Gestalt an bn mit n ∈ N: T := {a, b}, N := {S}, P : < S > ::= ε | a < S > b Mit Hilfe der ersten Ableitungsregel < S > ::= ε wird erreicht, dass die leere Zeichenkette zur Sprache gehört. Davon ausgehend können mit Hilfe der zweiten Regel nacheinander die Zeichenkette ′′ ab′′ , ′′ aabb′′ , ′′ aaabbb′′ usw. abgeleitet werden. 49 Syntax–Diagramme EBNF–Grammatiken können auch mit Hilfe von Syntaxdiagrammen dargestellt werden. Das sind spezielle gerichtete Graphen mit je einer zusätzlichen Eingangs– und Ausgangskante und zwei Arten von Knoten: Ein runder bzw. ovaler Knoten enthält ein Terminalsymbol, ein rechteckiger ein Nichtterminalsymbol. Ist α1 · · · αn eine Zeichenkette aus (T ∪ N )∗ und < u >::= α1 · · · αn eine Regel der Grammatik, so wird dieser Regel das folgende Syntaxdiagramm zur Definition von u zugeordnet: u: α1 αn Dabei ist hier und in den folgenden Diagrammen das Sechseck als ein Metasymbol zu betrachten. Je nachdem, ob der Inhalt im konkreten Fall ein Terminal– oder ein Nichtterminalsymbol ist, ist das Sechseck durch einen Kreis (bzw. eine Ellipse) oder durch ein Rechteck zu ersetzen. Der Ableitungsregel < u >::= ε (aus u ist das leere Wort ableitbar) soll dabei das Sytaxdiagramm ohne Knoten u: entsprechen. Transformation der übrigen Regeln: < u >::= v1|v2 | · · · |vn : v1 u: v2 vn < u >::= α[β]γ: u: α γ β 50 < u >::= αβ ∗γ: u: α γ β < u >::= αβ + γ: u: α β γ Die Zeichenketten an bn mit n ∈ N können somit durch ein Syntaxdiagramm wie folgt definiert werden: S: a S b Um alle Zeichenketten über {a, b} zu erhalten, kann man folgendes Syntaxdiagramm verwenden: S: a b Syntaxdiagramme werden insbesondere auch zur Definition syntaktischer Konstrukte von Programmiersprachen eingesetzt. Genau diejenigen Zeichenketten, die höchstens Terminalsymbole enthalten und die man erhält, wenn man das Syntaxdiagramm beginnend mit der Eingangskante bis zur Ausgangskante stets in Pfeilrichtung durchläuft, sind die Zeichenketten der durch das Diagramm definierten Sprache. Trifft man dabei ein Nichtterminalsymbol an, so darf dieses ersetzt werden durch eine Zeichenkette, die schon vorher als aus diesem Symbol ableitbare Zeichenkette erhalten wurde. 51 PEANOsches Axiomensystem (Giuseppe Peano, 1858 - 1932) Die Menge N der natürlichen Zahlen erfüllt zusammen mit der Zahl 0 und der Nachfolgerfunktion ν die folgenden Axiome: (a) die Zahl 0 ist eine natürliche Zahl, d.h., 0 gehört zu N (b) ν ist eine Funktion von der Menge N in die Menge N (c) für jede natürliche Zahl n ist ν(n) 6= 0 (d) für zwei beliebige natürliche Zahlen m und n folgt aus ν(n) = ν(m), dass n = m ist (e) jede Menge M, die die 0 und mit jedem n auch ν(n) enthält, umfasst die Menge N, d.h. ist eine Obermenge von N (Induktionsaxiom) Die Axiome in formaler Notation: (a) 0 ∈ N (b) ν : N → N (c) ∀n(n ∈ N → ν(n) 6= 0) (d) ∀m∀n(n, m ∈ N ∧ ν(n) = ν(m) → n = m) (e) ∀M(0 ∈ M ∧ ∀n(n ∈ M → ν(n) ∈ M) → N ⊆ M) Bemerkung: Bei manchen Autoren ist die Zahl 0 keine natürliche Zahl, so dass dort N = {1, 2, 3, · · ·} ist. Sie definieren meist noch eine Menge N0 mit N0 := N ∪ {0}. 52 Modelle der natürlichen Zahlen Aufgrund des Peano’schen Axiomensystems kann man die natürlichen Zahlen dadurch repräsentieren, dass man ein ausgezeichnetes Element hat, welches die Null darstellt und weiterhin eine Nachfolgerfunktion ν, so dass die Axiome alle erfüllt sind. Beispiele dazu: die natürlichen Zahlen als Zeichenketten über dem einelementigen Alpha- bet {|} Als Repräsentant der Zahl Null nimm die leere Zeichenkette ε und als Nachfolgerfunktion ν die Verkettung mit dem Zeichen ’|’, also ν(n) := n ◦ |. Dann ist N = {ε, |, ||, |||, · · ·}, und alle Axiome sind erfüllt. die natürlichen Zahlen als spezielle Mengen, konstruiert ausgehend von der leeren Menge Als Repräsentant der Zahl Null nimm die leere Menge ∅ und als Nachfolgerfunktion ν die Vereinigung der ursprünglichen Menge mit der Einermenge davon: für jede Menge M sei ν(M) := M ∪ {M}. Dann ist N = {∅, {∅}, {∅, {∅}}, {∅, {∅}, {∅, {∅}}}, · · ·}, und alle Axiome sind ebenfalls erfüllt. Der Prozess der Erzeugung der natürlichen Zahlen als Strichfolgen kann auch mit Hilfe eines Syntaxdiagramms veranschaulicht werden: nat_zahl: nat_zahl | Hierbei handelt es sich um ein rekursives Syntaxdiagramm, denn das zu definierende Objekt nat zahl kommt selbst im Syntaxdiagramm vor. Das folgende Syntaxdiagramm ist eine nicht–rekursive, iterative Variante zur Definition von nat zahl: nat_zahl: | 53 induktive Beweise Es soll eine Aussage ”für alle natürlichen Zahlen n ≥ n0 gilt A(n)” induktiv bewiesen werden; es soll also gezeigt werden, dass A(n) für alle n ≥ n0 gilt (oft ist n0 = 0 oder n0 = 1). Dann kann man folgendermaßen vorgehen (s. auch Aho/Ullman, S. 44): 1. lege die Fälle für den Induktions–Anfang fest, d.h., lege eine zweite natürliche Zahl m0 ≥ n0 fest; 2. Induktions–Anfang: beweise, dass A(n0), A(n0 + 1), · · · , A(m0) alle gelten (oft ist m0 = n0, so dass dann nur A(n0) zu zeigen ist); 3. Induktions–Schritt: (a) Induktions–Annahme: nimm an, dass A(n0), A(n0 + 1), · · · , A(n) alle für irgendein beliebiges n ≥ m0 gelten; (b) Induktions–Schluss: zeige, dass dann A(n + 1) gilt Anders formuliert: Es ist im Induktions–Schritt zu zeigen, dass immer dann, wenn für irgendein n ≥ m0 alle Aussagen A(n0), A(n0 + 1), · · · , A(n) gelten, dann auch A(n + 1) gilt. Wenn der Beweis gelingt, so ist gezeigt, dass A(n) für alle n ≥ n0 gilt. Wie n0 und m0 festgelegt werden, hängt von der Problemstellung ab. Wie A(n0), A(n0 + 1), · · · , A(m0) und im Induktions–Schritt A(n + 1) bewiesen werden können, hängt vom konkreten Problem ab (man benötigt u. a. problemspezifisches Wissen) und kann nicht weiter schematisiert werden. Das obige Verfahren heißt auch Prinzip der ordnungstheoretischen bzw. verallgemeinerten vollständigen bzw. starken Induktion (strong induction). Beim Prinzip der vollständigen Induktion ist n0 = m0 , und die Induktions– Annahme sieht folgendermaßen aus: nimm an, dass A(n) für irgendein beliebiges n ≥ m0 gilt Rechtfertigen lassen sich diese Beweisprinzipien mit Hilfe des Axiomensystems von Peano. 54 ein paar Beispiele immer wieder benötigter Aussagen, die induktiv bewiesen werden können 1. Für alle natürlichen Zahlen n ≥ 0 hat die Potenzmenge PM einer Menge M mit n Elementen genau 2n Elemente. 2. Für alle natürlichen Zahlen n ≥ 0 gibt es über einem Alphabet mit k > 1 Elementen genau k n+1 − 1 k−1 Zeichenketten mit Längen von 0 bis n. n 3. Für alle natürlichen Zahlen n ≥ 0 gibt es genau 2(2 ) n–stellige Funktionen f: {0, 1}n → {0, 1}. 4. für alle natürlichen Zahlen n ≥ 1 ist n−1 X i i=0 2 = 2n − 1 5. für alle natürlichen Zahlen n ≥ 1 ist n X i= i=1 n(n + 1) 2 6. für alle natürlichen Zahlen n ≥ 1 ist n X 2 i = i=1 n ∗ (n + 1) ∗ (2n + 1) 6 7. für alle natürlichen Zahlen n ≥ 1 ist n2 ∗ (n + 1)2 i = 4 i=1 n X 3 8. für alle natürlichen Zahlen n ≥ 0 ist n X i=0 n = 2n i 9. für alle natürlichen Zahlen n ≥ 1 ist n X (2i − 1) = n2 i=1 55 induktive bzw. rekursive Definition einer Menge M Rekursions–Anfang es werden explizit Objekte genannt, die zu M gehören sollen Rekursions–Schritt ausgehend von Objekten, von denen man bereits weiß, dass sie zu M gehören, werden nach bestimmten Vorschriften weitere Objekte konstruiert, die zu M gehören sollen Abschluss bzw. Minimalbedingung nur solche Objekte sollen zu M gehören, die aufgrund des Rekursions– Anfanges und des Rekursions–Schrittes erhalten werden können induktiver Beweis dafür, dass alle Elemente einer rekursiv definierten Menge M eine bestimmte Eigenschaft haben: man zeigt, dass Induktions–Anfang die durch den Rekursions–Anfang definierten Elemente von M die fragliche Eigenschaft haben Induktions–Schritt daraus, dass Elemente von M die Eigenschaft haben, folgt, dass die aus ihnen mittels des Rekursions–Schrittes konstruierten Elemente von M ebenfalls die Eigenschaft haben Dieses Beweisverfahren wird auch als Methode der strukturellen Induktion bezeichnet. 56 Signatur eines Datentyps Ein Datentyp besteht aus einer Menge (der Trägermenge des Datentyps) sowie darauf erklärten Funktionen. Auf der Basis der Signatur eines Datentyps können auf die bekannte Weise rekursiv oder durch eine EBNF–Grammatik oder durch Syntax–Diagramme die zu dem Datentyp gehörigen Terme definiert werden. Hier soll als Beispiel die Signatur des Datentyps int angegeben werden. sorts: Zint (Trägermenge, d.h. {i ∈ Z | IN T MIN ≤ i ≤ IN T MAX}) (Menge der int–Konstanten bzw. der möglichen Werte von Variablen des Typs int) ops: INT MIN , INT MAX : → Zint (int–Konstanten können als nullstellige Operationen in C für den Datentyp int aufgefasst werden.) + , - , ++ , - - , ∼ , ! : Zint → Zint (einstellige Operationen in C für den Datentyp int) + , - , * , / , % , << , >> , & , | , ∧ : Zint × Zint (zweistellige Operationen in C für den Datentyp int) rels: == , != , < , <= , > , >= (zweistellige Relationen in Zint) → Zint Zint × Zint : Andere Variante (hier werden die zweistelligen Relationen als zweistellige Funktionen von Zint ×Zint in die Menge {true, f alse} der Wahrheitswerte aufgefasst): sorts: Zint functs: INT MIN , INT MAX : + , - , ++ , - - , ∼ , ! : → Zint Zint → + , - , * , / , % , << , >> , & , | , == , != , < , <= , > , >= ∧ Zint : Zint × Zint : Zint × Zint → Zint {true, f alse} Analog können die Signaturen anderer Datentypen angegeben werden. 57 → int–Terme Auf der Basis der vorhergehenden Folie wird als Beispiel eine rekursive Definition des Begriffs des int–Termes für die Programmiersprache C angegeben: (a) Rekursions–Anfang Jedes Element aus Zint und jede Variable vom Typ int sei ein int–Term. Für jede Variable var vom Typ int seien ++var , - -var , +var , -var , ∼ var , !var , var++ und var- int–Terme. (b) Rekursions–Schritt Es seien T , T1 und T2 int–Terme; (α) Ist f ein einstelliges Operationssymbol, das zur Signatur von int gehört und verschieden von ”++” und von ”- -” ist, so sei auch f (T ) ein int–Term (besitzt T bereits ein äußeres Klammernpaar, so f T ). (β) Ist g ein zweistelliges Operationssymbol, das zur Signatur von int gehört, so sei auch (T1 g T2 ) ein int–Term. (c) Minimalbedingung Nur aufgrund von (a) und (b) sollen int–Terme erhalten werden können. Sind also beispielsweise i, j und k Variablen vom Typ int, so sind in C u. a. folgende int–Terme möglich: INT MIN , 34 , i , ++i , ∼ k , -j , !i , j−− , · · · (aufgrund des Rekursions– Anfangs) (i+j) , ((i << j) % k) , ((k ∧ i) / (j - i)) , · · · (aufgrund des Rekursions–Schritts) 58 rekursive Definition einer Menge natürlichzahliger Terme Es soll die Menge arith term aller derjenigen arithmetischen Terme konstruiert werden, die an Konstanten nur natürliche Zahlen, an Variablen (für natürliche Zahlen) nur i, j, k, m, n und an Operationssymbolen nur die zweistelligen ”+” und ”*” enthalten dürfen; die Terme sind vollständig zu klammern. Rekursions–Anfang: jede natürliche Zahl gehöre zu arith term; außerdem gelte i,j,k,m,n ∈ arith term; Rekursions–Schritt: es seien T1 und T2 bereits vorhandene Terme, d.h. T1, T2 ∈ arith term; dann seien auch (T1 + T2) und (T1 * T2) arithmetische Terme, d.h., (T1 + T2), (T1 * T2) ∈ arith term; Abschluss: nur solche Zeichenketten sollen zu arith term gehören, die aufgrund des Rek.– Anfangs oder Rek.–Schritts erhalten werden können; Beispiele für Elemente von arith term: 3 , n , k , (3+n) , ((3+n)*k) , (i+((3+n)*k)) Syntaxbaum des letzten Termes: + i * + 3 k n Die hier definierten Terme heißen auch Terme in infix–Notation oder einfach infix–Terme, weil die binären Operationssymbole zwischen den Operanden notiert werden. Daneben können Terme auch klammernfrei in präfix– und postfix–Notation angegeben werden. 59 Bäume (trees) Ein Baum ist ein spezieller gerichteter Graph. Ein gerichteter Graph B:=(Ka, Kn) heißt genau dann (gerichteter) Baum, wenn 1. es genau einen Knoten w ∈ Kn gibt, der keinen Vorgänger in B hat (dieser Knoten heißt die Wurzel von B) oder wenn Kn = ∅ (leerer Baum) ist 2. es von der Wurzel zu jedem anderen Knoten jeweils genau einen gerichteten Weg (in Pfeilrichtung zu durchlaufen) gibt (Bäume im Sinne dieser Definition werden manchmal auch als Wurzelbäume bzw. rooted trees bezeichnet.) Somit ist ein Baum zusammenhängend und zyklenfrei. Diejenigen Knoten, die keinen Nachfolger in B haben, heißen die Blätter von B. Übliche zeichnerische Darstellung bei endlichen Knotenmengen: Die Wurzel wird normalerweise oben gezeichnet, und ein Knoten, der Nachfolger eines anderen ist, wird unterhalb seines Vorgängers angeordnet (und mit ihm verbunden). Dadurch können die Pfeilspitzen eingespart werden. Die Höhe eines Knotens in einem Baum ist die Länge des längsten Weges von diesem Knoten bis zu einem Blatt. Als Höhe des Baumes ist die Höhe der Wurzel definiert. Die Tiefe eines Knotens ist gegeben durch die Länge des Weges von der Wurzel bis zu diesem Knoten. Ein Baum heißt genau dann binär, wenn jeder Knoten höchstens zwei Nachfolgerknoten hat, wobei zusätzlich zwischen dem linken und dem rechten Sohn eines Knotens (falls vorhanden) unterschieden wird. Damit können zwei voneinander verschiedene binäre Bäume denselben Graphen darstellen. Binäre Bäume über einer Knotenmenge Kn können auch rekursiv definiert werden: Rekursions–Anfang: Der leere Graph, wieder mit ε bezeichnet, sei ein binärer Baum über Kn. Rekursions–Schritt: Sind T 1 und T 2 binäre Bäume über Kn und ist K ∈ Kn, so sei das Tripel (K, T 1, T 2) ein binärer Baum (mit der Wurzel K, dem linken Teilbaum T 1 und dem rechten Teilbaum T 2). Ein binärer Baum heißt genau dann gesättigt, wenn jeder Knoten des Baumes, der kein Blatt ist, genau zwei Nachfolgerknoten besitzt. Ein ungerichteter, zusammenhängender und zyklenfreier Graph wird dadurch zu einem Baum, dass man einen beliebigen Knoten des Graphen als Wurzel festlegt. 60 binäre Bäume (binary trees) Außer als Syntaxbäume für Terme, die an Operationssymbolen höchstens ein– bzw. zweistellige enthalten, kommen binäre Bäume beispielsweise auch als Suchbäume vor. Dabei ist ein Suchbaum ein binärer Baum mit Knotenbewertungen aus einer geordneten Menge, wobei für jeden Knoten kn des Baumes gilt: falls kn einen linken Sohn hat, so ist dessen Bewertung kleiner als die von kn; falls kn einen rechten Sohn hat, so ist dessen Bewertung größer als die von kn. Nimmt man M := {1, 2, 3, 4, 6, 7, 8, 9, 13, 14, 17, 20} als eine solche Menge mit der gewöhnlichen kleinergleich–Relation als Ordnung, so ist zum Beispiel der folgende Binärbaum 8 17 4 6 2 1 3 20 13 9 7 14 ein Suchbaum mit Knotenbewertungen aus M. Dieser Baum ist sogar balanciert, weil für jeden Knoten kn dieses Baumes gilt: Die Höhen des linken und des rechten Teilbaumes von kn unterscheiden sich höchstens um 1. Die drei wichtigsten Reihenfolgen, in denen man sämtliche Knoten eines Binärbaumes systematisch durchlaufen kann, sind der inorder–, der präorder– und der postorder–Durchlauf. Rekursiver inorder–Durchlauf eines binären Baumes B mit Ausgabe der Knotenbewertungen bzw. Knoteninhalte: inorder(B): if (B = 6 ε) /* falls der Baum nicht leer ist*/ inorder(linker Teilbaum von B) Ausgabe: Inhalt von Wurzel(B) inorder(rechter Teilbaum von B) end if; Für den obigen Baum ergibt sich bei Anwendung dieses Algorithmus’ die Ausgabereihenfolge 1,2,3,4,6,7,8,9,13,14,17,20 für die Knoteninhalte. (siehe auch die C–Programme zur Baumverarbeitung in cprog09.pdf) 61 Kellerspeicher Ein Kellerspeicher (stack, LIFO–Speicher) K ist eine lineare Datenstruktur, bei der ein Zugriff auf ihre Elemente immer nur am gleichen Ende (top– bzw. oberstes Element) erfolgen kann. Diese lineare Datenstruktur kann mit Hilfe eines Arrays oder einer einfach verketteten Liste realisiert werden. Zu einem Kellerspeicher gehören immer auch die folgenden fünf Funktionen: 1. push; durch push(K, x) wird ein (dann oberstes) Element x hinzugefügt 2. pop; durch pop(K) wird das oberste Element aus dem Keller entnommen, falls der Keller nicht leer ist; falls vorher mindestens zwei Elemente im Keller waren, so wird nach Ausführen von pop(K) das ursprünglich zweitoberste zum obersten Element 3. top; durch top(K) kann das oberste Kellerelement angesehen werden, ohne den Kellerinhalt zu verändern 4. empty; empty ist eine nullstellige Funktion; durch empty() wird ein leerer Kellerspeicher erzeugt 5. isempty; durch isempty(K) kann getestet werden, ob der Keller leer ist oder nicht Kellerspeicher werden beispielsweise benötigt, um rekursive Programmaufrufe möglich zu machen. 62 Abstrakte Datentypen (ADT) grundlegend für objektorientierte Programmierung im Gegensatz zu einem konkreten Datentyp wird bei einem ADT der Typ der Elemente der Trägermenge(n) offengelassen bzw. hängt von Typparametern ab zusätzlich zur Signatur werden Axiome angegeben, denen die Funktionen genügen müssen (diese Axiome sind Forderungen an mögliche Implementierungen) Implementierungen selbst spielen bei der Spezifizierung abstrakter Daten- typen keine Rolle Kurzformel: ADT = (abstrakte) Datenmenge plus (darauf erklärten) Funktionen Beispiel: ADT stack uses: types boolean, T {T: Typ der Kellerelemente, nicht weiter spezifiziert} sorts: S functs: empty : → S isempty : S → boolean top : S 7→ T pop : S 7→ S push : S × T → S (7→: partielle Funktion) vars: σ: {Menge der Stacks mit Elementen vom Typ T} S, τ: T axioms: isempty(empty) = true isempty(push(σ, τ )) = f alse top(push(σ, τ )) = τ pop(push(σ, τ )) = σ top(empty), pop(empty): nicht definiert bzw. Fehlermeldung 63 rekursive Funktionen Eine Funktion f heißt rekursiv, wenn f bei der Funktionswertberechnung sich selbst wieder (unmittelbar oder mittelbar) aufruft. einfaches Beispiel: Fakultätsfunktion f : N → N mit f (n) := n! rekursive Definition von f : f (n) := 1 , falls n = 0 oder n = 1 n ∗ f (n − 1) , falls n > 1 direkte Umsetzung in eine rekursive C–Funktion fakrek: unsigned int fakrek(unsigned int n) { if (n <= 1) return 1; /* Rek.–Anfang */ else return n*fakrek(n-1); /* Rek.–Schritt */ } iterative Version als eine C–Funktion fakit: unsigned int fakit(unsigned int n) { unsigned int i, fak=1; for (i=1; i<=n; i++) fak = fak*i; return fak; } 64 weitere Beispiele rekursiver Definitionen von Funktionen 1. Fibonacci–Funktion f ib : N → N (Fibonacci bzw. Leonardo von Pisa, 1180-1250) f ib(0) := 0; f ib(1) := 1; f ib(n) := f ib(n − 1) + f ib(n − 2) (n ≥ 2) (es ist beispielsweise f ib(100) = 354 224 848 179 261 915 075) 2. Addition add : N × N → N add(n, 0) := n; add(n, ν(m)) := ν(add(n, m)) 3. Multiplikation mul(n, 0) := 0; mul : N × N → N mul(n, ν(m)) := add(mul(n, m), n) 4. Potenzierung pot : (N \ {0}) × N → N pot(n, 0) := 1; pot(n, ν(m)) := mul(pot(n, m), n) 5. modulo–Funktion mod : N × (N \ {0}) → N m, falls m < n mod(m, n) := mod(m − n, n), falls m ≥ n 6. größter gemeinsamer Teiler ggt : (N \ {0}) × N → N m, falls n = 0 ∨ m = n ggt(m, n) := ggt(m − n, n), falls m > n ggt(n − m, m), falls n > m m 7. Binomialkoeffizient n binomial : N × N → N 1, m 0, n := m−1 + m−1 , n n−1 bzw. binomial(m, n); falls n = 0 ∨ n = m falls m < n sonst 8. Ackermann–Funktion ack : N × N → N (Wilhelm Ackermann, 18961962) n + 1, falls m = 0 ack(m, n) := ack(m − 1, 1), falls m > 0 ∧ n = 0 ack(m − 1, ack(m, n − 1)) sonst Es ist beispielsweise ack(1, n) = n + 2, ack(2, n) = 2 ∗ n + 3, ack(3, n) = 2n+3 − 3, ack(4, n) = 2ack(4,n−1)+3 − 3, ack(4, 1) = 65533, ack(4, 2) > 1019726 Diese Funktion ist nicht mit Hilfe eines loop–Programmes (siehe Folie 110) berechenbar. 65 rekursive Definitionen von Pm i=n ai und von Qm i=n ai Sind an , an+1, · · · , am beliebige Zahlen, so sei i=n ai := 0, falls n > m Pm−1 ( i=n ai ) + am , falls n ≤ m m Y 1, falls n > m Qm−1 ( i=n ai ) ∗ am , falls n ≤ m m X und i=n ai := Umsetzung in iterative Programme zur Berechnung von n–stelliger Summe und von n–stelligem Produkt: n, m, a n , . . . , am sum := 0 i := n while i <= m sum := sum + ai i := i +1 sum n, m, a n , . . . , am prod := 1 i := n while i <= m prod := prod * ai i := i + 1 prod 66 Einsatz von Zufallszahlengeneratoren Erzeugung von Schlüsseln für die Kodierung von Nachrichten Programmierung von Spielen Erzeugung zufälliger Eingabedaten für Testserien approximative Berechnungen mit Hilfe von Monte–Carlo–Methoden Simulation von Prozessen (z. B. radioaktiver Zerfall, Brownsche Bewegung, Eintreten von Kunden in ein Geschäft, · · ·) Treffen von Entscheidungen in nichtdeterministischen Programmen Einsatz in randomisierten Algorithmen zufällige Auswahl von Stichproben, wenn aus Ressource–Gründen nicht alle Fälle betrachtet werden können ··· Computererzeugte Zufallszahlenfolgen sind Ausgaben von speziellen Programmen, die deshalb auch Pseudozufallszahlen–Generatoren (pseudo random number generator, PRNG) heißen. Einer der ersten PRNG’s geht auf John v. Neumann (1903–1957) zurück und heißt Quadratmittenmethode (middle–square method). Man erzeugt damit iterativ Pseudozufallszahlen x1, x2, · · ·, indem ein Angangswert x0 quadriert und ein Mittelstück davon als x1 genommen wird. Dann wird x1 quadriert und ein Mittelstück davon als x2 verwendet usw., bis man genügend viele Zahlen erzeugt hat. Strenge Anforderungen werden an kryptographisch sichere PRNG’s gestellt. 67 lineare Kongruenz–Generatoren zur Erzeugung von Zufallszahlen Oft sind Pseudozufallszahlen–Generatoren als lineare Kongruenz–Generatoren realisiert: Es werden 3 Konstanten a, b und m > max{a,b} festgelegt; beginnend mit einem Anfangswert x0 (”seed”) wird dann folgendermaßen eine Pseudozufallszahlenfolge x0 , x1 , x2 , · · · berechnet: xi+1 := (a ∗ xi + b) mod m , (i ≥ 0) Da 0 ≤ xi < m gilt für alle i, treten nach maximal m Iterationen Wiederholungen auf, die Folge wird periodisch. Es gilt (s. z.B. Forster, O.: Algorithmische Zahlentheorie. Vieweg–Verlag 1996; Schroeder, M. R.: Number Theory in Science and Communication. Springer– Verlag 2005): Genau dann ist die Folge x0, x1, x2, · · · periodisch mit der maximal möglichen Periodenlänge m, wenn die folgenden 3 Bedingungen erfüllt sind: 1. p|(a − 1) für jeden Primteiler p von m 2. 4|(a − 1), falls 4|m 3. ggt(b,m) = 1 √ √ Der Faktor a sollte im Intervall ( m, m − m) liegen, um ein ”zufälliges” Verhalten der erzeugten Folge zu erhalten. In der Praxis wird oft ein Modul m als eine Zweierpotenz verwendet. Dann sind die Bedingungen 1 bis 3 leicht zu realisieren: a ≡ 1 mod 4 und b ungerade (siehe auch die C–Folie zu Pseudozufallszahlen–Generatoren) 68 ein randomisierter Algorithmus Für ungerade natürliche Zahlen n ≥ 3 gilt: 1. n ist genau dann eine Primzahl, wenn (a(n−1)/2 mod n) ∈ {1, n − 1} ist für alle a ∈ {1, 2, · · · , n − 1} 2. falls zusätzlich (n − 1)/2 ungerade (d.h. n ≡ 3 mod 4) ist, so gilt: wenn n keine Primzahl ist, so ist (a(n−1)/2 mod n) ∈ / {1, n − 1} für mindestens die Hälfte aller a ∈ {1, 2, · · · , n − 1} vereinfachter Solovay–Strassen–Algorithmus zum Test auf Primzahleigenschaft für große ungerade n ∈ N mit n ≡ 3 mod 4 (s. z.B. Hromkovič, J.: Randomisierte Algorithmen. Teubner–Verlag 2004): n /* n nat. Zahl, n 3 mod 4 */ a := (Zufallszahl aus {2, ... , n−1}) ja ggt(a,n) > 1 nein b := a (n−1)/2 mod n ja b=1 or b=n−1 nein "n ist keine Primzahl" "n ist Primzahl" "n ist keine Primzahl" Wenn ausgegeben wird, dass n keine Primzahl ist, so ist n garantiert keine Primzahl, weil der Algorithmus einen Zeugen (witness) dafür gefunden hat; wird dagegen ausgegeben, dass n eine Primzahl ist, so ist n mit mindestens der Wahrscheinlichkeit 0.5 eine Primzahl. Wird bei einer genügend großen Anzahl von Läufen mit stets zufälligen a ∈ {2, · · · , n − 1} immer wieder ausgegeben, dass n eine Primzahl ist, so wird die Irrtumswahrscheinlichkeit für die Aussage, dass n eine Primzahl ist, beliebig klein. 69 asymptotische Laufzeitabschätzung gesucht ist eine obere Schranke für die Laufzeit T (n) eines Algorithmus, der n Eingabedaten hat (z.B. ein Array mit n Elementen; n kann aber z.B. auch für die Anzahl der Dualstellen eines einzigen Eingabedatums stehen) die obere Schranke soll unabhängig sein vom konkreten Rechner, von der Programmiersprache und vom Compiler; es soll auf einen konstanten Faktor für die Ausführungszeit einer einzelnen Anweisung nicht ankommen; konkret soll es für jedes Programm eine positive konstante Zahl c geben, so dass die Ausführungszeiten jeder elementaren arithmetischen Operation, jeder booleschen Operation, jeder Vergleichsoperation, jeder einfachen Wertzuweisung variable := term und jedes Aufrufs einer Ein- oder Ausgabefunktion durch c nach oben beschränkt sind ausschlaggebend für die Einordnung von T (n) soll der für immer größer werdendes n dominante Teil von T (n) sein Diese Vorgaben führen auf den Begriff der asymptotischen Laufzeitkomplexität, die mit Hilfe der O–Notation (gesprochen: groß–Oh bzw. big–Oh) angegeben wird: Ist f : N → N oder f : N → R+ und ist T (n) die Laufzeit eines Algorithmus, so bedeute T (n) ∈ O(f (n)) dass es eine natürliche Zahl n0 eine eine positive reelle Zahl c gibt, so dass für alle k ∈ N mit k ≥ n0 gilt: T (k) ≤ c ∗ f (k). formalisiert: T (n) ∈ O(f (n)) ↔ ∃n0 ∃c(n0 ∈ N ∧ c ∈ R ∧ c > 0 ∧ ∀k(k ∈ N ∧ k ≥ n0 → T (k) ≤ c ∗ f (k))) andere übliche Schreibweisen: statt ”T (n) ∈ O(f (n))” findet man auch ’T (n) = O(f (n))” bzw. ’T (n) ist ein O(f (n))”. Wenn für alle n, die gößer als ein n1 sind, stets f (n) ≤ g(n) ist, und wenn T (n) ∈ O(f (n)) gilt, so ist natürlich erst recht T (n) ∈ O(g(n)). Um die asymptotische Laufzeitkomplexität also in hoher Qualität anzugeben, ist eine Funktion f in O(f (n)) gefragt, die möglichst kleine Funktionswerte hat bzw. möglichst langsam wächst. Zusätzlich sollte der f beschreibende Term von einfacher Gestalt sein. 70 wichtige Eigenschaften der O–Notation Für Funktionen f, g, h : N → R+ und Konstanten a, a0 , · · · , ak gelten die folgenden Beziehungen: ∃c(c > 0 ∧ ∀j(j ∈ N → f (j) ≤ c)) → f (n) ∈ O(1) ak ∗ nk + ak−1 ∗ nk−1 + · · · + a0 ∈ O(nk ) a ∗ f (n) ∈ O(f (n)) f (n) ∈ O(g(n)) ∧ g(n) ∈ O(h(n)) → f (n) ∈ O(h(n)) f (n) + g(n) ∈ O(max{f (n), g(n)}) f (n) ∗ g(n) ∈ O(f (n) ∗ g(n)) Für Funktionen f, g : N → R+ bedeute f ≺g (”f wächst asymptotisch langsamer als g”), dass lim n→∞ f (n) =0 g(n) ist. Dann gilt für beliebige Konstanten ε, c mit 0 < ε < 1 < c und den Logarithmus log zu irgendeiner Basis b > 1: 1 ≺ log log n ≺ log n ≺ nε ≺ n ≺ n ∗ log n ≺ nc ≺ nlog n ≺ cn ≺ n! ≺ nn ≺ c(c n ) Ist beispielsweise T (n) ∈ O(nc ) bzw. T (n) ∈ O(cn ), so spricht man von polynomieller bzw. exponentieller Laufzeitkomplexität. Das Wachstumsverhalten einiger ausgewählter Funktionen zeigt, dass Algorithmen mit exponentieller Laufzeitkomplexität schon für relativ kleine n eine nicht mehr praktikable Laufzeit haben: n ln(n) √ n n n*ln(n) n2 n3 2n n! nn 1 5 10 20 50 100 0 1.6· · · 2.3· · · 2.9· · · 3.9· · · 4.6· · · 1 2.2· · · 3.1· · · 4.4· · · 7.0· · · 10 1 5 10 20 50 100 0 8.0· · · 23.0· · · 59.9· · · 195.6· · · 460.5· · · 1 25 100 400 2500 10000 1 125 1000 8000 125000 1000000 2 32 1024 1048576 1.126e15 1.267e30 1 120 3628800 2.43· · ·e18 3.04· · ·e64 9.33· · ·e157 1 3125 1e10 1.04· · ·e26 8.88· · ·e84 1e200 71 typische asymptotische Laufzeiten O(1) (konstante Laufzeit) Die Auswertung eines arithmetischen oder booleschen Termes, der nur elementare Operationssymbole und keine Funktionsaufrufe enthält, ist in konstanter asymptotischer Laufzeit möglich. Gleiches gilt für die Laufzeit eines Programmes ohne Schleifen und ohne weitere Funktionsaufrufe. (Das Adjektiv ”asymptotisch” ist hier ohne Bedeutung.) O(log n) (logarithmische Laufzeit bzw. Komplexität) Hat ein Programm mit n Eingabedaten (bzw. mit n als Eingabedatum) diese Laufzeit, so verdoppelt sich diese erst bei n2 Eingabewerten (bzw. n als Eingabedatum). Dies ist ein günstiges Laufzeitverhalten. Beispiele: binäre Suche in einem sortierten Array der Länge n; schnelle Berechnung von xn mittels square and multiply nach Folie 30. O(n) (lineare Komplexität): Günstige Laufzeit, beispielsweise bei der Suche in einem unsortierten Array oder einer Liste der Länge n, bei der Berechnung von xn nach der ”gewöhnlichen” Methode (unteres Struktogramm von Folie 30) oder bei der Summen– bzw. Produktberechnung von n Werten nach Folie 66. O(n ∗ log n): Auch noch günstige Laufzeit, beispielsweise haben schnellste Sortieralgorithmen zum Sortieren von n Werten diese Laufzeitkomplexität. O(n2 ) (quadratische Komplexität): Nicht mehr so günstig, denn eine Verdopplung des Wertes von n bewirkt eine Vervierfachung der Laufzeit. Einfache Sortieralgorithmen (siehe die Folien zur C–Programmierung) und die Multiplikation einer (n×n)–Matrix mit einem Vektor der Länge n haben eine solche Laufzeit. O(n3 ) (kubische Komplexität): Tritt beispielsweise bei der Multiplikation zweier (n × n)–Matrizen nach der ”gewöhnlichen” Methode auf. O(2n ) (exponentielle Komplexität): Katastrophales Laufzeitverhalten, denn eine Verdopplung des Wertes von n zieht ein Quadrieren der Laufzeit nach sich. Probleme, für deren Lösung nur Algorithmen mit exponentieller oder höherer Laufzeit bekannt sind, heißen auch schwer handhabbare Probleme (intractable problems), siehe nächste Folie. 72 Beispiele für schwer handhabbare bzw. lösbare Probleme 1. Existenz eines Hamilton–Kreises (Hamiltonian circuit problem) Ein Hamilton–Kreis in einem ungerichteten Graphen mit n Knoten ist ein n–Tupel (k1, · · · , kn ) aller Knoten des Graphen derart, dass alle Zweiermengen {ki , ki+1} mit 1 ≤ i < n und {kn , k1} Kanten des Graphen sind. Es gibt also eine Rundreise in dem Graphen, bei der jeder Knoten genau einmal besucht wird (und Anfangs– gleich Endknoten ist). Wie kann man feststellen, ob der Graph einen Hamilton–Kreis besitzt (und dann einen solchen bestimmen) oder nicht ? 2. Problem des Handlungsreisenden (travelling salesman problem; auch: Rundreiseproblem) Ein Vertreter muss n Städte besuchen, deren Entfernungen untereinander bekannt sind. Er besucht jede nur einmal und kehrt am Schluss zum Ausgangsort zurück. In welcher Reihenfolge muss er die Städte besuchen, damit der gesamte von ihm zurückgelegte Weg minimal ist? 3. Erfüllbarkeit Boolescher Terme (SAT; satisfiability of Boolean expressions) Es sei B ein beliebiger Boolescher Term in den Variablen x1 , · · · , xn. Ist B erfüllbar? 4. Rucksackproblem (knapsack problem) Es seien n Gegenstände mit unterschiedlichen Werten und Gewichten gegeben. Ein Rucksack soll damit derart gefüllt werden, dass ein zulässiges Gesamtgewicht nicht überschritten wird und gleichzeitig der Gesamtwert der Gegenstände im Rucksack möglichst groß ist. Wie ist der Rucksack zu füllen? 5. Zerlegungsproblem (partitioning problem) Gegeben sei ein n–Tupel (a1 , · · · , an ) natürlicher Zahlen. Gibt es dann eine Zerlegung der Menge M := {1, · · · , n} in zwei Teilmengen M1 und M2 , so dass gilt P P ? = i∈M2 ai i∈M1 ai Für alle diese Probleme sind bis heute keine Lösungsalgorithmen mit geringerer als exponentieller Laufzeit bekannt. Algorithmen mit höherer als polynomieller Laufzeit werden auch als ineffiziente Algorithmen bezeichnet. 73 Halteproblem Gesucht ist ein Algorithmus Alg, der, angewandt auf ein beliebiges Programm P und eine beliebige zugelassene Eingabe E für P , stets entscheidet, ob P bei dieser Eingabe terminiert oder nicht. P E Alg ja nein Zugelassene Eingabe für P : Enthält P an Variablen beispielsweise genau x1 , · · · , xn als Variablen für natürliche Zahlen, so sei genau jedes n-Tupel i1, · · · , in natürlicher Zahlen eine zugelassene Eingabe für P (xj wird mit ij initialisiert). (Anstelle von ”Programm”hätte auch ”Turing–Maschine”oder ”while–Programm” (s. u.) oder auch ”partiell–rekursive zahlentheoretische Funktion”notiert werden können. Eine zugelassene Eingabe für die Turing–Maschine wäre eine beliebige anfängliche Bandbeschriftung, wobei nur endlich viele Zellen beschriftet sein dürfen.) Äquivalenzproblem für Programme Gesucht ist ein Algorithmus Alg, der, angewandt auf ein beliebiges Paar (P1 , P2 ) von Programmen P1 und P2 , stets entscheidet, ob P1 und P2 äquivalent sind oder nicht. Dabei heißen zwei Programme genau dann äquivalent, wenn beide Programme bei gleichen Eingaben sich stets gleich verhalten: Entweder beide terminieren und liefern die gleichen Resultate oder beide terminieren nicht. Sowohl das Halteproblem als auch das Äquivalenzproblem sind nicht lösbar, d. h., es gibt keine obigen Algorithmen Alg. 74 POSTsches Korrespondenzproblem Emil Leon Post (1897 - 1954) gegeben: ein beliebiges Alphabet A und eine beliebige endliche Folge ((v1, w1), · · · , (vk , wk )) von Wortpaaren (vi, wi) mit vi, wi ∈ A+ für alle i mit 1 ≤ i ≤ k gesucht: eine Folge (i1, i2 , · · · , in ) von Indizes mit n ≥ 1, 1 ≤ i1 , · · · , in ≤ k, so dass gilt v i1 ◦ v i2 ◦ · · · ◦ v in = wi1 ◦ wi2 ◦ · · · ◦ win Eine solche Folge (i1, i2, · · · , in ), falls sie existiert, heißt eine Lösung des Korrespondenzproblems ((v1, w1), · · · , (vk , wk )). Beispiel: A := {0, 1}, KP := ((1, 101), (10, 00), (011, 11)) Dann besitzt das Korrespondenzproblem KP beispielsweise die Lösung (1, 3, 2, 3). Zehntes HILBERTsches Problem David Hilbert (1862 - 1943) Gesucht ist ein Algorithmus, der, angewandt auf eine beliebige diophantische Gleichung, aussagt, ob diese lösbar ist oder nicht. Y. V. Matijasevic̆ zeigte 1970, dass es keinen solchen Algorithmus gibt. diophantische Gleichung: P (x1, · · · , xn) = 0 wobei P (x1 , · · · , xn) ein Polynom mit ganzzahligen Koeffizienten in den Variablen x1, · · · , xn ist. Als Lösungen sind nur n–Tupel ganzer Zahlen zugelassen. Beispiel: Die diophantische Gleichung x31 + 2x1x2 − x23 + x2 − 4 = 0 ist lösbar, denn beispielsweise (1,1,0) ist eine Lösung. Auch diese beiden Probleme sind nicht algorithmisch lösbar. 75 Gödelisierung Kurt Gödel (1906 - 1978) Den Zeichen eines Alphabetes A := {a1, · · · , an } sowie den nichtleeren Zeichenketten und den endlichen Folgen von Zeichenketten über diesem Alphabet werden effektiv und eineindeutig natürliche Zahlen, ihre Gödel–Nummern, zugeordnet. G(a1 ) := 1, G(a2 ) := 3, · · · , G(an ) := 2 ∗ n − 1 w := ai1 · · · aik ∈ A+ ; dann sei G(ai1 ) G(w) := p1 G(ai2 ) ∗ p2 G(aik ) ∗ · · · ∗ pk wobei pi die i–te Primzahl sei W := (w(1) , · · · , w(r) ) ∈ (A+ )r ; dann sei G(w(1) ) G(W ) := p1 G(w(2) ) ∗ p2 ∗ · · · ∗ pG(w r (r) ) Dabei sind p1, p2, · · · mit p1 := 2, p2 := 3, p3 := 5, · · · die ersten Primzahlen mit pi < pi+1 für alle i ≥ 1. Von jeder natürlichen Zahl ist effektiv entscheidbar, ob sie die Gödel–Nummer eines Zeichens, einer Zeichenkette oder einer Folge von Zeichenketten über A ist oder nicht. Hierbei spielt der Satz von der eindeutigen Primfaktorzerlegung (siehe Folie 16) eine tragende Rolle. Falls eine natürliche Zahl eine Gödel–Nummer ist, so kann ihr Urbild bzgl. G effektiv bestimmt werden. 76 generelle Fragen, die zusammen mit einem Problemlösungsprozess auftreten Nach der Formulierung eines Problems stellen sich folgende Fragen bzw. müssen folgende weitere Probleme bearbeitet werden: Ist das Problem überhaupt lösbar? Wenn es lösbar ist, gibt es genau eine oder gibt es mehrere Lösungen? Wenn es eine Lösung gibt, ist diese oder sind alle Lösungen dann algorith- misch berechenbar? (Es gibt auch reine Existenzsätze!) Wenn es eine algorithmisch berechenbare Lösung gibt, wie groß ist dann der Mindestaufwand, eine oder auch alle Lösungen zu berechnen? Wenn das Problem algorithmisch lösbar ist, so finde einen möglichst laufzeit– (und/oder speicherplatz–) optimalen Algorithmus zur Bestimmung einer oder einiger oder aller Lösungen! Wenn möglich, so beweise die Korrektheit des Algorithmus oder zumindest von ausschlaggebenden Teilen davon! Welche Programmiersprache ist geeignet zur Implementierung des Algo- rithmus? Welche Auswirkungen haben die begrenzten Ressourcen, z. B. Darstellun- gen von Daten in festen Formaten (→ Rundungsfehler, under– und overflow) sowie Beschränktheit der Speicherkapazität und der Rechenzeit auf die Genauigkeit der Ergebnisse und die Praktikabilität des Algorithmus? 77 Aussagen Aussagen sind sprachliche Gebilde, für die es sinnvoll ist zu fragen, ob sie wahr oder falsch sind. Sie können in einer natürlichen oder künstlichen Sprache formuliert sein. Eine wahre Aussage hat den Wahrheitswert wahr (auch: W bzw. true bzw. T bzw. 1), eine falsche Aussage den Wahrheitswert f alsch (auch: F bzw. f alse bzw. 0). Beispiele: ”Die Zahl 5 ist eine Primzahl.” (W ) ”Der November hat 31 Tage.” (F ) √ ” 2 ist eine rationale Zahl.” (F ) ”Es gibt unendlich viele Primzahen.” (W ) ”Es gibt unendlich viele Primzahlzwillinge.” (?) ”Jede gerade natürliche Zahl, die größer oder gleich 4 ist, lässt sich als Summe zweier Primzahlen darstellen.” (?) (Goldbachsche Vermutung von 1742) ”Es gibt unendlich viele pythagoreische Tripel (a, b, c).” (W ) ”Für jede natürliche Zahl n ≥ 3 und beliebige natürliche Zahlen a, b, c ungleich Null ist stets an + bn 6= cn .” (W ) (Großer Satz von Fermat, Pierre de Fermat 1601–1655; der Satz konnte erst 1993 bewiesen werden, und zwar von Andrew Wiles) 78 klassische (zweiwertige) Aussagenlogik Die klassische Aussagenlogik gründet sich auf den beiden folgenden Prinzipien: Prinzip vom ausgeschlossenen Dritten: Jede Aussage ist nichts als wahr oder falsch. Prinzip vom ausgeschlossenen Widerspruch: Es gibt keine Aussage, die sowohl wahr als auch falsch ist. Verknüpfungen von Aussagen: Aussagen können negiert und mit Hilfe von ”und”, ”oder”, ”wenn · · · dann · · ·”, ”· · · genau dann, wenn · · ·”, ”entweder · · · oder · · ·”, ”weder · · · noch · · ·” und weiteren Verknüpfungen zu zusammengesetzten Aussagen verbunden werden. elementare bzw. atomare Aussagen: Das sind Aussagen, die nicht auf die eben genannte Art aus einfacheren Aussagen zusammengesetzt sind. (Beispiele dazu: s. vorhergehende Folie) Prinzip der Extensionalität: Der Wahrheitswert einer zusammengesetzten Aussage hängt nur ab von den Wahrheitswerten der darin vorkommenden atomaren Aussagen und der Art ihrer Verknüpfung. Es spielt also keine Rolle, ob irgendwelche inhaltlichen Zusammenhänge zwischen den Teilaussagen bestehen oder nicht. 79 Formalisierung zusammengesetzter Aussagen Es seien p bzw. q die beiden folgenden Aussagen: p: 2 ist ein Teiler von 3. q: 13 ist eine Primzahl. Dann sind ¬p, p ∧ q, p ∨ q, p → q bzw. p ↔ q Formalisierungen der folgenden Aussagen: ¬p: Es ist nicht so, dass 2 ein Teiler von 3 ist. (Auch: 2 ist nicht Teiler von 3.) (W ) p ∧ q: 2 ist ein Teiler von 3, und 13 ist eine Primzahl. (F ) p ∨ q: 2 ist ein Teiler von 3, oder 13 ist eine Primzahl. (W ) p → q: Wenn 2 ein Teiler von 3 ist, so ist 13 eine Primzahl. (W ) p ↔ q: 2 ist genau dann ein Teiler von 3, wenn 13 eine Primzahl ist. (F ) Sei ferner r: 6 ist eine vollkommene Zahl. Dann entsprechen sich die folgenden Zeichenketten und verbal formulierten Aussagen: p → (¬q ∧ ¬r): Wenn 2 ein Teiler von 3 ist, so ist weder 13 eine Primzahl noch 6 eine vollkommene Zahl. (W ) ¬p ∧ ¬q ∧ ¬r: Es ist weder 2 ein Teiler von 3 noch ist 13 eine Primzahl noch ist 6 eine vollkommene Zahl. (F ) 80 BOOLEsche Terme (aussagenlogische Ausdrücke) (George Boole, 1815 - 1864) Es seien p, q, r, p1, p2, · · · Variablen, die nur die Werte true bzw. f alse (oder: 1 bzw. 0) annehmen können. Mit Hilfe dieser Aussagen–Variablen und der Wahrheitswerte können auf folgende Weise die booleschen Terme definiert werden: (a) Rekursions–Anfang Die Variablen p, q, r, p1, p2, · · · sowie die Konstanten true und f alse seien boolesche Terme. (b) Rekursions–Schritt Falls A und B boolesche Terme sind, so seien auch ¬A, (A ∧ B), (A ∨ B), (A −→ B) und (A ←→ B) boolesche Terme. (c) Minimalbedingung Nur aufgrund von (a) und (b) sollen boolesche Terme erhalten werden können. Damit können boolesche Terme als spezielle Zeichenketten über dem Alphabet A := {true, f alse, p, q, r, |, ¬, ∧, ∨, →, ↔, (, )} aufgefasst werden (ersetze ”p1” durch ”p|”, ”p2” durch ”p||”, usw.). Klammereinsparungsregeln: Ein äußeres Klammernpaar darf weggelassen werden. Weiterhin soll jedes der Symbole ”¬”, ”∧”,”∨”,”→” und ”↔” stärker binden als alle in dieser Reihenfolge rechts neben ihm stehenden. Bemerkung: Man kann die booleschen Terme auch zunächst ohne ”−→” und ohne ”←→” einführen und dann definieren (s. auch die Folie 84 zu den semantischen Äquivalenzen): ”A −→ B” stehe für ”¬A ∨ B” ”A ←→ B” stehe für ”(A −→ B) ∧ (B −→ A)” bzw. für ”(¬A ∨ B) ∧ (¬B ∨ A)” 81 eine EBNF–Grammatik zur Erzeugung der Booleschen Terme Die rekursive Definition boolescher Terme kann auch auf folgende Weise mit Hilfe einer EBNF–Grammatik erfolgen: T := {false, true, p, q, r, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ¬, ∧, ∨, →, ↔, (, ) } N := {bool term , variable , konstante , ziffer} S := bool term <konstante> ::= false | true <variable> ::= p{<ziffer>} | q | r <ziffer> ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 <bool term> ::= <konstante> | <variable> | ¬ <bool term> | (<bool term> ∧ <bool term>) | (<bool term> ∨ <bool term>) | (<bool term> → <bool term>) | (<bool term> ↔ <bool term>) Die booleschen Terme lassen sich natürlich auch leicht mit Hilfe eines Syntaxdiagrammes erzeugen. 82 Semantik boolescher Terme Es sei f: {p, q, r, p1 , p2 , · · · } −→ {false, true}. (Die Funktion f heißt dann eine Belegung der Aussagenvariablen mit Wahrheitswerten.) Dann sei der Wahrheitswert Ww(C,f) eines Booleschen Termes C bei der Belegung f wie folgt definiert: Rekursions–Anfang (C ist eine Konstante oder Variable) Ww(true,f) := true, Ww(false,f) := false Ww(p,f) := f(p), Ww(q,f) := f(q), Ww(r,f) := f(r), Ww(p1,f) := f(p1), · · · Rekursions–Schritt (C ist ein zusammengesetzter Term) Ww(¬A,f) true , falls Ww(A,f)=false false sonst true , falls Ww(A,f)=Ww(B,f)=true false sonst false , falls Ww(A,f)=Ww(B,f)=false true sonst := Ww((A ∧ B),f) := Ww((A ∨ B),f) := Ww((A → B),f) := Ww((A ↔ B),f) := false , falls Ww(A,f)=true und Ww(B,f)=false true sonst true , falls Ww(A,f)=Ww(B,f) false sonst (s. auch die entsprechende Folie von cprog09.pdf) 83 semantische Äquivalenz und Folgerungsbegriff für boolesche Terme Es seien A und B boolesche Terme. A und B heißen semantisch äquivalent (in Zeichen: A ≡ B oder A ⇔ B) genau dann, wenn Ww(A,f) = Ww(B,f) ist für jede Belegung f der Aussagenvariablen mit Wahrheitswerten B folgt aus A (in Zeichen: A |= B oder A ⇒ B) genau dann, wenn gilt: für jede Belegung f, für die Ww(A,f) = true ist, ist auch Ww(B,f) = true Dass B aus A folgt, kann äquivalent zu obiger Definition auch auf jede der beiden folgenden Weisen definiert werden: für jede Belegung f, für die Ww(B,f) = false ist, ist auch Ww(A,f) = false es gibt keine Belegung f mit Ww(A,f) = true und Ww(B,f) = false Beispiele: A ∨ ¬A ≡ true, A ∧ ¬A ≡ f alse A ∨ A ≡ A, A ∨ true ≡ true, A ∨ f alse ≡ A A ∧ A ≡ A, A ∧ true ≡ A, A ∧ f alse ≡ f alse A → B ≡ ¬A ∨ B, A → B ≡ ¬B → ¬A A ↔ B ≡ (¬A ∨ B) ∧ (A ∨ ¬B) A ⊕ B ≡ (A ∨ B) ∧ ¬(A ∧ B) (exklusives oder bzw. xor) A ∨ (A ∧ B) ≡ A, A ∧ (A ∨ B) ≡ A A ∨ (Ā ∧ B) ≡ A ∨ B, A ∧ (Ā ∨ B) ≡ A ∧ B A ∧ (A → B) ⇒ B Man kann auch durch jede der beiden folgenden Redeweisen ausdrücken, dass B aus A folgt: A ist hinreichend für B B ist notwendig für A 84 Ersetzbarkeitstheorem Es seien A, B, C drei boolesche Terme mit B ≡ C. Dann gilt: Kommt an irgendeiner Stelle in A der Term B als Teilzeichenkette vor und wird dieses Vorkommen von B durch den Term C ersetzt, so ist die dabei aus A entstehende Zeichenkette A′ wieder ein boolescher Term, und außerdem ist A ≡ A′ . Verallgemeinerung des Folgerungsbegriffs auf mehrere Prämissen A1 , · · · , An B folgt aus A1 , · · · , An (in Zeichen: A1 , · · · , An |= B oder A1 , · · · , An ⇒ B) genau dann, wenn gilt: für jede Belegung f, für die Ww(A1,f) = · · · = Ww(An,f) = true ist, ist auch Ww(B,f) = true Weitere grundlegende Begriffe für boolesche Terme A heißt eine Tautologie genau dann, wenn Ww(A,f) = true ist für jede Belegung f A heißt eine Kontradiktion genau dann, wenn Ww(A,f) = false ist für jede Belegung f A heißt erfüllbar genau dann, wenn Ww(A,f) = true ist für wenigstens eine Belegung f Es gilt: Genau dann, wenn A ≡ B ist, ist A ↔ B eine Tautologie; genau dann, wenn A1, · · · , An ⇒ B ist, ist A1 ∧ · · · ∧ An → B eine Tautologie; genau dann, wenn A1, · · · , An ⇒ B ist, ist A1 ∧· · ·∧An ∧¬B eine Kontradiktion 85 Gesetze einer BOOLEschen Algebra (B, 0, 1, ¯, ∧, ∨) (z.B. B = {true, false}, 0=false, 1=true,¯= ¬) Es ist 0, 1 ∈ B mit 0 6= 1, und für alle x, y, z ∈ B gilt: ¯: B −→ B, ∧, ∨ : B × B −→ B, 1. x ∧ y ≡ y ∧ x , x ∨ y ≡ y ∨ x (Kommutativgesetze) 2. x ∧ (y ∧ z) ≡ (x ∧ y) ∧ z (Assoziativgesetze) , x ∨ (y ∨ z) ≡ (x ∨ y) ∨ z 3. x ∧ (x ∨ y) ≡ x , x ∨ (x ∧ y) ≡ x (Absorptions– bzw. Verschmelzungsgesetze) 4. x ∧ (y ∨ z) ≡ (x ∧ y) ∨ (x ∧ z) x ∨ (y ∧ z) ≡ (x ∨ y) ∧ (x ∨ z) (Distributivgesetze) 5. x ∧ 0 ≡ 0 , x ∧ 1 ≡ x (Existenz von Null– und Einselement (neutrale Elemente)) 6. x ∧ x̄ ≡ 0 , x ∨ x̄ ≡ 1 (Existenz des Komplements) In einer Booleschen Algebra gilt weiterhin für alle x, y ∈ B: x∨0≡x , x∨1≡1 x ∧ y ≡ x̄ ∨ ȳ , x ∨ y ≡ x̄ ∧ ȳ (De MORGANsche Gesetze) x∧x≡x , x∨x≡x (Idempotenz) ¯≡x x̄ (Doppelkomplement– bzw. Doppelnegationsgesetz) 0̄ ≡ 1 , 1̄ ≡ 0 (Komplementarität der neutralen Elemente) gilt T1 ≡ T2 für zwei Terme einer Booleschen Algebra und ersetzt man in T1 und in T2 simultan alle Vorkommen von ”∨” durch ”∧”, von ”∧” durch ”∨”, von ”0” durch ”1” und von ”1” durch ”0”, so erhält man wieder eine Äquivalenz. (Dualitätsprinzip der Booleschen Algebra) 86 Verifikation eines Programmes zur Bestimmung von q := n div m und von r := n mod m für nat. Zahlen n, m mit m 6= 0 Zugrunde liegt der Satz von der Division mit Rest (siehe auch Folie 16): Zu zwei beliebig vorgegebenen natürlichen Zahlen n, m mit m 6= 0 gibt es genau zwei natürliche Zahlen q und r, für die gilt: n = m ∗ q + r und 0 ≤ r < m Bezeichnung für diese Zahlen: q = n div m (ganzer Teil des Quotienten bei der Division von n durch m); r = n mod m (Rest bei der Division von n durch m) Pseudocode zur Bestimmung von q und r: Eingabe: m, n ∈ N mit m 6= 0 /* Vorbedingung: n, m ∈ N ∧ 0 < m */ r = n; q = 0; /* Schleifeninvariante: 0 ≤ r ∧ 0 < m ∧ n = m ∗ q + r */ while (r >= m) { r = r − m; q = q + 1; } /* Nachbedingung: 0 ≤ r ∧ 0 < m ∧ r < m ∧ n = m ∗ q + r */ Ausgabe: q, r 87 Schleifeninvariante (loop invariant) logischer Ausdruck, der unmittelbar vor Eintritt in die Schleife wahr ist und dessen Wahrheitswert sich bei einem Schleifendurchlauf nicht ändert (falls die Schleifenbedingung erfüllt war); dass dieser Ausdruck immer dann wahr ist, ist induktiv zu beweisen, wobei der Parameter, nach dem der Induktions–Beweis durchgeführt wird, unterschiedliche Bedeutungen haben kann: er kann angeben, das wievielte Mal der Test der Schleifenbedingung erreicht wurde er kann die Laufvariable einer for–Schleife sein er kann den Wert eines arithmetischen Terms, der von den Werten der Variablen des Programms abhängt und bei jedem Schleifendurchlauf um 1 wächst, haben Falls dann die Schleife terminiert, so gelten unmittelbar nach Verlassen der Schleife sowohl die Schleifeninvariante als auch die Negation der Schleifenbedingung, woraus sich durch semantisch äquivalentes Umformen oder durch Folgern Eigenschaften des betrachteten Algorithmus beweisen lassen. 88 Struktur indirekter Beweise Es soll indirekt gezeigt werden, dass B aus A1, · · · , An folgt. Dazu kann jede der drei folgenden semantischen Äquivalenzen benutzt werden: 1. A1 ∧ · · · ∧ An → B ≡ A1 ∧ · · · ∧ An ∧ ¬B → ¬Ai 2. A1 ∧ · · · ∧ An → B ≡ A1 ∧ · · · ∧ An ∧ ¬B → B 3. A1 ∧ · · · ∧ An → B ≡ A1 ∧ · · · ∧ An ∧ ¬B → F (1 ≤ i ≤ n) Es kann also die Negation ¬B von B mit zu den Prämissen hinzugenommen werden, und wenn es dann gelingt, zu zeigen, dass einer der folgenden Ausdrücke 1. ¬Ai (1 ≤ i ≤ n) 2. B 3. F bzw. C ∧ ¬C für einen Ausdruck C aus diesen um ¬B erweiterten Prämissen folgt, so hat man gezeigt, dass B aus A1 , · · · , An folgt. Im ersten Fall hätte man einen Widerspruch zu einer Voraussetzung Ai , im zweiten Fall einen Widerspruch zur Annahme B und im dritten Fall einen logischen Widerspruch erhalten. Spezialfälle: 1. A1 → B ≡ A1 ∧ ¬B → ¬A1 A1 → B ≡ ¬B → ¬A1 (n = 1) Man hat demnach indirekt gezeigt, dass B aus A1 folgt, wenn man gezeigt hat, dass ¬A1 aus A1 und ¬B folgt oder dass ¬A1 aus ¬B folgt. 2. T → B ≡ ¬B → B bzw. B ≡ ¬B → B (n = 1 und A1 = T ) Man hat also indirekt gezeigt, dass B zutrifft, wenn man gezeigt hat, dass B aus ¬B folgt. 3. T → B ≡ ¬B → F bzw. B ≡ ¬B → F (n = 1 und A1 = T ) Man hat also indirekt gezeigt, dass B zutrifft, wenn man gezeigt hat, dass aus ¬B ein logischer Widerspruch folgt. Eine weitere Möglichkeit, indirekt zu zeigen, dass B zutrifft, erhält man daraus, dass A ∧ (¬B → ¬A) → B eine Tautologie ist. Man hat demnach gezeigt, dass B zutrifft, wenn man gezeigt hat, dass A zutrifft und dass ¬A aus ¬B folgt. 89 prädikatenlogische Terme zu den aussagenlogischen Symbolen kommen die folgenden hinzu: (Individuen–)Konstanten: (Individuen–)Variablen: Prädikatensymbole: Funktionssymbole: a1 , a2 , · · · x1 , x2 , · · · Pik mit i, k = 1, 2, · · · ϕki mit i, k = 1, 2, · · · i: Unterscheidungsindex; k: Stelligkeit Dann können auf folgende Weise die prädikatenlogischen Terme definiert werden: (a) Rekursions–Anfang Jede Konstante und jede Variable sei ein Term. (b) Rekursions–Schritt Falls ϕ ein k–stelliges Funktionssymbol ist und t1 , · · · , tk prädikatenlogische Terme sind, so sei auch ϕ(t1, · · · , tk ) ein prädikatenlogischer Term (c) Minimalbedingung Nur aufgrund von (a) und (b) sollen prädikatenlogische Terme erhalten werden können. (Manchmal werden in der Literatur nullstellige Funktionssymbole anstelle der Konstanten verwendet.) 90 prädikatenlogische Ausdrücke Mit Hilfe der prädikatenlogischen Terme können auf folgende Weise die prädikatenlogischen Ausdrücke definiert werden: (a) Rekursions–Anfang Falls P ein k–stelliges Prädikatensymbol ist und t1 , · · · , tk prädikatenlogische Terme sind, so sei P (t1 , · · · , tk ) ein prädikatenlogischer Ausdruck. (Diese prädikatenlogischen Ausdrücke heißen auch atomare prädikatenlogische Ausdrücke.) (b) Rekursions–Schritt Falls A und B prädikatenlogische Ausdrücke sind, so seien auch ¬A, (A ∧ B), (A ∨ B), (A −→ B) und (A ←→ B) prädikatenlogische Ausdrücke; falls x eine Variable ist und A ein prädikatenlogischer Ausdruck, so seien auch ∀xA und ∃xA prädikatenlogische Ausdrücke. (c) Minimalbedingung Nur aufgrund von (a) und (b) sollen prädikatenlogische Ausdrücke erhalten werden können. Jedes Vorkommen einer Variablen in einem prädikatenlogischen Ausdruck ist entweder ein freies oder ein gebundenes: Ein Vorkommen von x in A heißt genau dann gebundenes Vorkommen, wenn dieses x in einem Teilausdruck von A der Gestalt ∀xB oder ∃xB vorkommt; sonst heiße ein Vorkommen von x freies Vorkommen Ein prädikatenlogischer Ausdruck ohne freie Vorkommen von Variablen heißt auch geschlossener Ausdruck bzw. Aussage. 91 Semantik der Prädikatenlogik 1. Termwertbestimmung Um den Termen und Ausdrücken eine Bedeutung zuordnen zu können, benötigt man zunächst eine nichtleere Menge IB (den Individuenbereich) sowie eine Funktion F mit Db(F ) := {ai |i ≥ 1} ∪ {Pik |i, k ≥ 1} ∪ {ϕki |i, k ≥ 1}, so dass gilt: F (ai ) ist ein Element von IB (d.h. F (ai ) ∈ IB) F (Pik ) ist eine k–stellige Relation in IB (d.h. F (Pik ) ⊆ IB k ) F (ϕki ) ist eine k–stellige Funktion von IB in IB (d.h. F (ϕki ) : IB k → IB) (Kommt unter den zweistelligen Prädikatensymbolen das Gleichheitszeichen vor, so ist es stets durch die identische Relation in IB zu interpretieren.) Durch eine solche Interpretation (IB, F ) wird zusammen mit einer Belegung f der Individuen–Variablen mit Individuen (d.h., f : {xi|i ≥ 1} → IB) jedem Term T ein Wert (Individuum) W (T, F, f ) zugeordnet: nämlich W (ai , F, f ) := F (ai) für jede Konstante ai W (xi, F, f ) := f (xi) für jede Variable xi W (ϕ(t1 , · · · , tk ), F, f ) := F (ϕ)(W (t1, F, f ), · · · , W (tk , F, f )) für jedes k– stellige Funktionssymbol ϕ und Terme t1 , · · · , tk (natürlich reicht es für einen vorgegebenen Term aus, nur die Interpretation bzw. Belegung der wirklich in ihm vorkommenden Symbole zu kennen, um ihm einen Wert zuordnen zu können). 92 2. Interpretation der Ausdrücke Jetzt kann mittels (IB, F ) und f jedem prädikatenlogischen Ausdruck A ein Wahrheitswert zugeordnet werden (abhängig von (IB, F ) und von f ): 1. falls A = P (t1 , · · · , tk ) ist für ein k–stelliges Prädikatensymbol P und Terme t1 , · · · , tk , so sei true, falls ((W (t1, F, f ), · · · , W (tk , F, f )) ∈ F (P ) W w(A, F, f ) := f alse, sonst 2. A = ¬B: true, falls W w(B, F, f ) = f alse f alse, sonst true, falls W w(B, F, f ) = true und W w(C, F, f ) = true f alse, sonst f alse, falls W w(B, F, f ) = f alse und W w(C, F, f ) = f alse true, sonst f alse, falls W w(B, F, f ) = true und W w(C, F, f ) = f alse true, sonst true, falls W w(B, F, f ) = W w(C, F, f ) f alse, sonst true, falls für alle i ∈ IB gilt: W w(B[x/i], F, f ) = true f alse, sonst true, falls es ein i ∈ IB gibt mit W w(B[x/i], F, f ) = true f alse, sonst W w(A, F, f ) := A = (B ∧ C): W w(A, F, f ) := A = (B ∨ C): W w(A, F, f ) := A = (B → C): W w(A, F, f ) := A = (B ↔ C): W w(A, F, f ) := 3. A = ∀xB: W w(A, F, f ) := A = ∃xB: W w(A, F, f ) := Dabei sei B[x/i] derjenige Ausdruck, den man aus B dadurch erhält, dass alle freien Vorkommen von ”x” in B simultan durch das Individuum i ∈ IB ersetzt werden. Kommt in B die Variable x nicht frei vor, so ist W w(∀xB, F, f ) = W w(∃xB, F, f ) = W w(B, F, f ). Insbesondere gilt für jeden Ausdruck A ohne freie Vorkommen von Variablen (d.h. für jede Aussage A): Der Wahrheitswert von A hängt nur von der Interpretation (IB, F ) ab, nicht aber von Belegungen f der Individuen–Variablen mit Individuen. 93 Modellbegriff, Erfüllbarkeit, Allgemeingültigkeit, Folgerungsbegriff, semantische Äquivalenz Eine Interpretation I = (IB, F ) ist zusammen mit einer Belegung f genau dann ein Modell für A (bzw. A gilt bzgl. I, f ), wenn W w(A, F, f ) = true ist. Es heißt A genau dann allgemeingültig, symbolisch |= A, falls A bzgl. jeder Interpretation und jeder zugehörigen Belegung gilt. Es heißt A genau dann erfüllbar, wenn es ein Modell für A gibt. Ein prädikatenlogischer Ausdruck B folgt genau dann aus einer Men- ge M von prädikatenlogischen Ausdrücken, symbolisch M |= B, wenn bei jeder Interpretation und jeder zugehörigen Belegung, bei denen alle Ausdrücke aus M true sind, auch B true ist (d.h., jedes Modell aller Ausdrücke aus M ist auch ein Modell für B); B folgt aus A, symbolisch A |= B, wenn bei jeder Interpretation und jeder zugehörigen Belegung, bei denen A true ist, auch B true ist (d.h., jedes Modell für A ist auch ein Modell für B). Zwei prädikatenlogische Ausdrücke A und B heißen genau dann seman- tisch äquivalent, symbolisch A ≡ B, wenn jedes Modell für A auch ein Modell für B ist und umgekehrt. Beziehungen zu entsprechenden aussagenlogischen Begriffen: Falls A eine aussagenlogische Tautologie ist und man für jede in A vorkommende Aussagenvariable einen prädikatenlogischen Ausdruck einsetzt (für gleiche Variablen natürlich gleiche Ausdrücke), so erhält man einen allgemeingültigen prädikatenlogischen Ausdruck. (Auf diese Weise lassen sich jedoch nicht alle allgemeingültigen prädikatenlogischen Ausdrücke konstruieren.) Analoges gilt bezüglich des Folgerungsbegriffs und bezüglich des Begriffs der semantischen Äquivalenz. 94 ein paar wichtige Eigenschaften der semantischen Begriffe Für prädikatenlogische Ausdrücke A, B, C und Mengen M solcher Ausdrücke gilt: Negationsregeln ¬∀x A ¬∃x A ≡ ≡ ∃x ¬A ∀x ¬A Vertauschungsregeln: ∀x∀y A ∃x∃y A ≡ ≡ ∀y∀x A ∃y∃x A Ausklammerungsregeln: (∀x A) ∧ (∀x B) (∃x A) ∨ (∃x B) ≡ ≡ ∀x(A ∧ B) ∃x(A ∨ B) (∀x A) ∨ (∀x B) |= ∀x(A ∨ B) (aber nicht umgekehrt) ∃x(A ∧ B) |= (∃x A) ∧ (∃x B) (aber nicht umgekehrt) ∃x∀y A |= ∀y∃x A (aber nicht umgekehrt) falls A |= B gilt und A abgeschlossen ist, so ist (A → B) allgemeingültig es ist A genau dann allgemeingültig, wenn ¬A nicht erfüllbar ist falls A |= B und B |= C gilt, so ist A |= C falls A allgemeingültig ist, so ist stets M |= A falls M |= A und M |= A → B gilt, so gilt M |= B falls M nur allgemeingültige Ausdrücke enthält und M |= A gilt, so ist A allgemeingültig mögen in A genau die Variablen x1 , · · · , xn frei vorkommen; dann gilt: A ist genau dann allgemeingültig, wenn ∀x1 · · · ∀xn A allgemeingültig ist; A ist genau dann erfüllbar, wenn ∃x1 · · · ∃xn A erfüllbar ist 95 programmiertechnische Umsetzung Pseudocodes für die programmiertechnische Realisierung zweier prädikatenlogischer Ausdrücke, wenn dabei ein endlicher Individuenbereich M vorgegeben ist: ∀x(x ∈ M → P (x)) Pseudocode: flag := true; forall x ∈ M if (¬P (x)) { flag := false; break; } return flag; ∃x(x ∈ M ∧ P (x)) Pseudocode: flag := false; forall x ∈ M if (P (x)) { flag := true; break; } return flag; Je nach der Art der Abspeicherung der Elemente von M sind die entsprechenden Schleifenkonstrukte zur Simulation von forall x ∈ M heranzuziehen. 96 Deterministischer endlicher Automat A ohne Ausgabe (deterministischer endlicher Akzeptor) A = (E, Z, f, z0, F ) wobei E Eingabealphabet Z Zustandsmenge f :Z×E →Z (Zustands–)Überführungsfunktion z0 ∈ Z F ⊆Z Startzustand Menge der Endzustände E, Z: endliche Mengen Akzeptanz einer Zeichenkette w ∈ E ∗ durch A: w wird genau dann akzeptiert, wenn der Automat unmittelbar vor der Verarbeitung von w im Startzustand z0 ist und sich unmittelbar nach dem Verarbeiten von w in einen Endzustand befindet. (Es spielt dabei keine Rolle, ob der Automat zwischenzeitlich schon ein– oder mehrmals in einem Endzustand war.) Nichtdeterministischer endlicher Automat A ohne Ausgabe (nichtdeterministischer endlicher Akzeptor) A = (E, Z, f, Z0, F ) wobei E Eingabealphabet Z Zustandsmenge f : Z × E → PZ (Zustands–)Überführungsfunktion (PZ: Potenzmenge von Z) Z0 ⊆ Z F ⊆Z Menge der Startzustände Menge der Endzustände E, Z: endliche Mengen Akzeptanz einer Zeichenkette w ∈ E ∗ durch A: Der Automat befinde sich anfangs in irgendeinem Startzustand z0 ∈ Z0. Genau dann, wenn es möglich ist (wenn also f es zulässt), dass der Automat sich unmittelbar nach Verarbeitung von w in einem Endzustand befindet, akzeptiert er w. 97 Nichtdeterministischer endlicher Akzeptor A mit Epsilon–Übergängen A = (E, Z, f, Z0, F ) wobei E Eingabealphabet Z Zustandsmenge f : Z × (E ∪ {ε}) → PZ Z0 ⊆ Z F ⊆Z (Zustands–)Überführungsfunktion Menge der Startzustände Menge der Endzustände E, Z: endliche Mengen Interpretation der Epsilon–Übergänge: Falls f (z, ε) = Z1 ist für z ∈ Z, Z1 ⊆ Z, so soll der Automat, falls er sich in einem bestimmten Takt im Zustand z befindet, spontan in irgendeinen Zustand z ′ ∈ Z1 übergehen können, ohne dass die Position des Lesekopfes auf dem Eingabeband geändert wird (es wird in einer solchen Situation kein Eingabesymbol gelesen bzw. verarbeitet). Es gilt für jede Sprache L ⊆ E ∗: es gibt einen deterministischen endlichen Akzeptor mit E als Eingabealphabet, der L akzeptiert ←→ es gibt einen nichtdeterministischen endlichen Akzeptor mit E als Eingabealphabet, der L akzeptiert ←→ es gibt einen nichtdeterministischen endlichen Akzeptor mit Epsilon–Übergängen und mit E als Eingabealphabet, der L akzeptiert 98 Reguläre Ausdrücke (regular expressions) Die Menge der regulären Ausdrücke zur Charakterisierung der regulären Sprachen über einem endlichen Alphabet E ist rekursiv wie folgt definiert: (a) Rekursions–Anfang ∅, ε und jedes a ∈ E seien reguläre Ausdrücke. (b) Rekursions–Schritt Falls α und β reguläre Ausdrücke sind, so seien auch (α | β), (α ◦ β) und (α∗) reguläre Ausdrücke. (c) Minimalbedingung Nur aufgrund von (a) und (b) sollen reguläre Ausdrücke zur Charakterisierung regulärer Sprachen über E erhalten werden können. Vorrangregeln, auf deren Grundlage Klammern eingespart werden können: Von den drei Operatoren soll ’|’ am stärksten trennen, dann ’◦’, dann ’∗’, und ein äußeres Klammernpaar darf weggelassen werden. Außerdem wird der Operator ’◦’ auch manchmal als Punkt notiert oder ganz weggelassen. Beispiel: Der reguläre Ausdruck (a | ((b ◦ (c∗ )) ◦ d)) über dem Alphabet {a, b, c, d} kann somit auch folgendermaßen notiert werden: a | bc∗ d Parallel zur rekursiven Definition der regulären Ausdrücke wird, ebenfalls rekursiv, jedem solchen Ausdruck α eine Sprache L(α) über dem Alphabet E zugeordnet: (a) Rekursions–Anfang Es seien L(∅) := ∅ (die leere Sprache), L(ε) := {ε} (Einermenge leeres Wort) und für jedes a ∈ E sei L(a) := {a} (Einermenge von a, wobei a hier als Zeichenkette der Länge 1 interpretiert wird). (b) Rekursions–Schritt Falls α und β reguläre Ausdrücke sind, so sei L(α | β) := L(α) ∪ L(β) (Vereinigung von L(α)und L(β)), L(αβ) := L(α)L(β) (Verkettung bzw. Produkt der Sprachen L(α) und L(β)), L(α∗) := (L(α))∗ (Menge aller endlichen Verkettungen von Zeichenketten aus L(α); Sternoperation, KLEENE’sche Hülle). 99 Reguläre Ausdrücke in UNIX–Notation Die UNIX–Kommandos grep, fgrep, egrep (grep: globally search for regular expression and print), awk (awk: Aho/Weinberger/Kernighan) und lex benutzen reguläre Ausdrücke als Argumente, um Textzeilen in Dateien nach Zeichenketten zu durchsuchen, die bestimmte Muster haben (grep, fgrep, egrep), um darüberhinausgehend Textdateien zu formatieren und zu transformieren (awk) und um lexikalische Analysen von Programmtexten durchzuführen (lex). Dabei gilt für Textsuchmuster zunächst die Regel: Kein Suchmuster (d.h. regulärer Ausdruck) wirkt über eine Zeile hinaus. Beispiele: grep ’^[0-9][0-9]*$’ datei.txt Es werden alle Zeilen von datei.txt ausgegeben, die nur Dezimalziffern (mindestens eine) enthalten. Dabei kennzeichnen ’∧’ den Zeilenanfang und ’$’ das Zeilenende. grep ’^[sS]’ datei.txt Es werden alle Zeilen von datei.txt ausgegeben, die mit ’s’ oder mit ’S’ beginnen. grep ’a.c’ datei.txt Es werden alle Zeilen von datei.txt ausgegeben, die eine Teilzeichenkette der Länge drei haben mit erstem Zeichen ’a’, beliebigem zweiten Zeichen und drittem Zeichen ’c’. Der Punkt hat also eine wild card–Funktion. egrep ’ab?c’ datei.txt Es werden alle Zeilen von datei.txt ausgegeben, die eine Teilzeichenkette der Gestalt ”ac” oder ”abc” enthalten. Das Fragezeichen stellt also das Vorkommen von ’b’ in Frage. egrep ’[0-9]+\.[0-9]* | \.[0-9]+’ datei.txt Es werden alle Zeilen von datei.txt ausgegeben, die eine dezimale Gleitpunktzahl x.y enthalten, wobei x aus mindestens einer Ziffer besteht und y leer sein kann, oder x ist leer und y besteht aus mindestens einer Ziffer. 100 Chomsky–Hierarchie von Grammatiken (Noam Chomsky, *1928) Ausgangspunkt ist der Begriff der Grammatik G = (T, N, P, S). Definition: 1. Jede Grammatik ist vom Typ 0. 2. Eine Grammatik ist genau dann vom Typ 1 oder kontextsensitiv, wenn für alle Regeln u → v aus P gilt: length(u) ≤ length(v) 3. Eine Grammatik ist genau dann vom Typ 2 oder kontextfrei, falls sie vom Typ 1 ist und zusätzlich für alle Regeln u → v aus P gilt: u ist ein Metasymbol 4. Eine Grammatik ist genau dann vom Typ 3 oder regulär, falls sie vom Typ 2 ist und zusätzlich für alle Regeln u → v aus P gilt: v = ε oder v ist ein einzelnes Terminalsymbol oder v ist ein Terminalsymbol gefolgt von einem Metasymbol. Eine solche Grammatik heißt auch rechtslinear. Eine Sprache L über dem Alphabet T , d.h. L ⊆ T ∗, heiße genau dann vom Typ 0 bzw. 1 bzw. 2 bzw. 3, falls es eine Typ–0– bzw. Typ–1– bzw. Typ–2– bzw. Typ–3–Grammatik G gibt, so dass L = L(G) ist. kontextsensitiv: Es sind Regeln der Gestalt ”u1Au2 → u1xu2”mit A ∈ N erlaubt, und eine solche Regel besagt, dass ’A’ zwischen ’u1’ und ’u2’, also im Kontext von ’u1’ und ’u2’, durch ’x’ ersetzt werden darf. Bemerkungen: Bei Typ–1–Grammatiken soll bei Bedarf die Ableitungsregel ”S → ε” zu- gelassen sein, dann darf ’S’ aber nicht auf der rechten Seite einer Ableitungsregel vorkommen. Bei kontextfreien Grammatiken darf man beliebig Ableitungsregeln der Gestalt ”A → ε” mit A ∈ N verwenden, also auch ”S → ε” für das Startsymbol ’S’, falls man ε ∈ L(G) haben möchte. 101 Pumping–Lemma Zunächst kann man beweisen, dass für jede Sprache L ⊆ E ∗ gilt: es gibt einen (deterministischen oder nichtdeterministischen) endlichen Akzeptor mit E als Eingabealphabet, der L akzeptiert ←→ es gibt einen regulären Ausdruck α über dem Alphabet E mit L = L(α) ←→ es gibt eine reguläre Grammatik G mit E als Terminalalphabet, so dass L = L(G) ist Um zeigen zu können, dass eine bestimmte Sprache nicht regulär ist, ist oft das Pumping–Lemma hilfreich. Pumping–Lemma: Wenn L eine reguläre Sprache ist, so gibt es eine natürliche Zahl n, so dass sich alle Wörter w ∈ L mit length(w) ≥ n zerlegen lassen in w = w1w2w3 , wobei length(w2 ) ≥ 1, length(w1w2 ) ≤ n und w1w2i w3 ∈ L gilt für alle i ∈ N. Mit Hilfe des Pumping–Lemmas kann beispielsweise bewiesen werden, dass jede der drei folgenden Sprachen L1, L2 und L3 nicht regulär ist: L1 := {an bn | n ≥ 1} L2 := {0n | n ist eine Quadratzahl} L3 := {0p | p ist eine Primzahl} 102 Deterministischer endlicher Automat A mit Ausgabe (Mealy–Automat) A = (E, A, Z, f, g, z0) wobei E Eingabealphabet A Ausgabealphabet Z Zustandsmenge f :Z×E →Z Überführungsfunktion g : Z × E → A Ausgabefunktion z0 ∈ Z Startzustand E, A, Z: endliche Mengen 103 Nichtdeterministischer endlicher Kellerautomat A (nondeterministic finite pushdown automaton) A = (E, Z, K, f, z0, k0, F ) wobei E Eingabealphabet Z Zustandsmenge K Menge der Kellersymbole f : Z × (E ∪ {ε}) × K → P(Z × K ∗ ) Überführungsfunktion z0 ∈ Z Startzustand k0 ∈ K: Kellerstartsymbol F ⊆Z Menge der Endzustände E, Z, K: endliche Mengen Arbeitsweise: Anfangs befindet sich der Automat im Startzustand z0 , im Keller befindet sich nur das Kellerstartsymbol k0 und das Eingabewort befindet sich auf dem Eingabeband, wobei der Lesekopf unter dem ersten Symbol des Eingabewortes steht, falls dieses nicht leer ist. Der Automat arbeitet wieder getaktet (wie alle Automaten), und sein Verhalten in den folgenden Takten wird durch die Überführungsfunktion f bestimmt: Befindet sich der Automat gerade im Zustand z, ist k das oberste Kellersymbol und ist a das aktuelle Eingabesymbol, so wird ein Paar (z ′ , w) ∈ (f (z, ε, k) ∪ f (z, a, k)) zufällig ausgewählt, und im Folgetakt befindet sich der Automat im Zustand z ′ , das vorher oberste Kellersymbol ist durch die Zeichenkette w von Kellersymbolen ersetzt worden (so dass jetzt das erste Zeichen von w oberstes Kellersymbol ist). Dabei ist der Lesekopf des Eingabebandes nur dann um einen Schritt nach rechts gerückt worden, wenn (z ′ , w) ∈ f (z, a, k) galt. Falls w die leere Zeichenkette war, so wurde also bzgl. des Kellers eine pop– Operation durchgeführt. Ist ein (z ′ , w) ∈ f (z, ε, k) gewählt worden, so dass also der Lesekopf des Eingabewortes sich in diesem Takt nicht weiter bewegt, so spricht man wieder von einem Epsilon–Übergang. 104 Akzeptanz von Sprachen durch nichtdeterministische endliche Kellerautomaten Es gibt hier zwei Arten der Akzeptanz: Akzeptanz durch Endzustand: Ein Eingabewort wird hier genau dan akzeptiert, wenn sich der Automat unmittelbar nach Verarbeitung des Wortes in einem Endzustand befindet. Der Inhalt des Stacks zu diesem Zeitpunkt ist völlig irrelevant, so dass man durch Hinzufügen geeigneter Epsilon–Übergänge auch erreichen könnte, dass sich im Keller nur noch das Kellerstartsymbol befindet oder dass der Keller leer ist. Akzeptanz durch leeren Keller: Ein Eingabewort wird hier genau dan akzeptiert, wenn der Stack des Automaten unmittelbar nach Verarbeitung des Wortes leer ist (zwischenzeitlich darf er natürlich nicht leer sein, weil dann ein Weiterarbeiten des Automaten nicht möglich wäre). Der Zustand des Automaten unmittelbar nach Verarbeitung des Wortes ist dabei irrelevant. Es gilt für eine beliebige Sprache L ⊆ E ∗ : es gibt einen nichtdeterministischen Kellerautomaten (E, Z, K, f, z0, k0, F ) der L per Akzeptanz durch Endzustand akzeptiert ←→ es gibt einen nichtdeterministischen Kellerautomaten (E, Z ′, K ′, f ′, z0′ , k0′ , F ′) der L per Akzeptanz durch leeren Keller akzeptiert Genau dann, wenn L auf eine dieser Arten akzeptiert wird, ist L eine kontextfreie Sprache über E. 105 Deterministischer endlicher Kellerautomat A Ein deterministischer Kellerautomat A = (E, Z, K, f, z0, k0, F ) ist ein spezieller nichtdeterministischer Kellerautomat, für den zusätzlich gilt: für alle (z, a, k) ∈ Z × (E ∪ {ε}) × K ist card(f (z, a, k)) ≤ 1 wenn f (z, a, k) 6= ∅ ist für ein a ∈ E, so muss f (z, ε, k) = ∅ sein Die Sprache L ⊆ E ∗ eines solchen Kellerautomaten bestehe aus genau denjenigen Zeichenketten, die von dem Automaten durch Akzeptanz durch Endzustand akzeptiert werden. Jede solche Sprache heiße deterministisch kontextfrei. Es gilt für jedes Alphabet E: Jede reguläre Sprache über E ist auch eine deterministisch kontextfreie Sprache über E. Jede deterministisch kontextfreie Sprache über E ist auch eine kontextfreie Sprache über E. 106 Deterministische Turing–Maschine T Alan Turing (1911 - 1954) T = (Z, E, Γ, f, z0, 2, F ) wobei Z Zustandsmenge E Eingabealphabet Γ Menge der Bandsymbole mit E ⊂ Γ f : Z × Γ → Z × Γ × {L, R, N } Überführungsfunktion z0 ∈ Z Startzustand 2 Blank– bzw. Trennsymbol mit 2 ∈ Γ \ E F ⊆Z Menge der Endzustände Z, Γ: endliche Mengen Falls beispielsweise f (z, a) = (z ′ , b, L) ist und die Turing–Maschine sich im Takt t im Zustand z befindet sowie der Lese/Schreib–Kopf auf dem Bandsymbol a steht, so geht sie in den Zustand z ′ über, überschreibt a mit b und der Lese/Schreib–Kopf rückt um eine Position nach links. Das ist dann die Ausgangssituation im Takt t+1. Akzeptanz eines Wortes w ∈ E ∗ durch T : Ausgangssituation: Die Turing–Maschine befindet sich im Startzustand z0 , w befindet sich auf dem Band, beidseitig durch je mindestens ein Blank begrenzt, und der L/S–Kopf befindet sich unmittelbar unter dem ersten Zeichen von w (oder, äquivalent dazu, unmittelbar links von w). Wie durch die Funktion f gegeben, nimmt die Maschine getaktet ihre Arbeit auf. Genau dann, wenn sie dabei (nach endlich vielen Takten) in einen Endzustand kommt, wird w akzeptiert, und die Maschine beendet ihre Arbeit. (Dabei muss w nicht unbedingt vollständig inspiziert worden sein.) Es gilt dann für jedes Alphabet E und jede Sprache L ⊆ E ∗ : L ist vom Typ 0 ←→ es gibt eine (deterministische) Turingmaschine mit dem Eingabealphabet E, die genau die Zeichenketten aus L akzeptiert. (Analoges gilt für nichtdeterministische Turing–Maschinen.) 107 Turing–Berechenbarkeit Turing–Maschinen können nicht nur als Akzeptoren benutzt werden, sondern sie sind auch einsetzbar zur Berechnung von Zeichenketten. Konfiguration bzw. Momentaufnahme einer deterministische Turing–Maschine T: (γ1, z, γ2) ∈ Γ∗ × Z × Γ∗ wobei z der aktuelle Zustand ist, der L/S–Kopf sich unter dem ersten Symbol von γ2 befindet (bzw. unmittelbar rechts von γ1 , falls γ2 = ε) , γ2 sich unmittelbar rechts an γ1 anschließt und links von γ1 und rechts von γ2 auf dem Band nur Blank–Symbole stehen. Anfangskonfiguration bzw. Startkonfiguration von T für ein Eingabewort w := e1 · · · en ∈ E ∗: (ε, z0, w) bzw. (ε, z0, e1 · · · en ) Außer w stehen auf dem Band nur Blanks. Übergangsrelation ⊢ in der Menge der Konfigurationen: es sei genau dann (α, z, β) ⊢ (α′ , z ′ , β ′), wenn die Konfiguration (α, z, β) durch genau einen Rechenschritt (genau eine Anwendung von f ) in die Konfiguration (α′ , z ′ , β ′) übergeführt wird erweiterte Übergangsrelation ⊢∗ in der Menge der Konfigurationen: es sei genau dann (α, z, β) ⊢∗ (α′ , z ′ , β ′), wenn es ein n ∈ N und Konfigurationen (αi , ζi, βi) mit 0 ≤ i ≤ n und (α, z, β) = (α0 , ζ0, β0), (αn , ζn, βn ) = (α′, z ′ , β ′) gibt, so dass gilt (α0 , ζ0, β0) ⊢ (α1 , ζ1, β1) ⊢ · · · ⊢ (αn , ζn, βn ) Eine Berechnung mit dem Eingabewort w ∈ E ∗ ist eine Folge (ε, z0, w) ⊢ (α1, z1 , β1) ⊢ (α2 , z2, β2) ⊢ · · · von Konfigurationen mit folgender Eigenschaft: Die Berechnung wird beim ersten Erreichen eines Endzustandes gestoppt; das Ergebnis steht dann auf dem Eingabeband ab der Position des L/S–Kopfes nach rechts bis unmittelbar vor dem ersten Blank. Falls nie ein Endzustand erreicht wird, so wird kein Ergebnis berechnet (Realisierung einer partiellen Funktion f ). 108 Turing–Programm zur Addition zweier durch Strichfolgen gegebener positiver natürlicher Zahlen Ausgangsposition (”Standardlage”) des L/S–Kopfes: unter dem ersten Zeichen des ersten Operanden (entspricht dem Startzustand zo ) Die zu verarbeitenden Zahlen seien durch genau ein Feld mit dem Inhalt ’#’ (# ∈ Γ \ E) voneinander und durch je mindestens ein Feld mit dem Inhalt ’2’ von ihrer Umgebung getrennt. Endposition des L/S–Kopfes: unter dem ersten Zeichen des Ergebnisses while (<Arbeitsfeld> = ’|’) R; write(’|’) ; while (<Arbeitsfeld> = ’|’) L; R; write(’2’) ; R; These von Church (1936) (Alonzo Church, 1903–1995) Genau die im intuitiven Sinne berechenbaren zahlentheoretischen Funktionen (bzw. Wortfunktionen) sind die Turing–berechenbaren zahlentheoretischen Funktionen (bzw. Wortfunktionen). (”Turing–berechenbar” wird mit ”algorithmisch berechenbar” gleichgesetzt) Mit Hilfe einer Gödelisierung kann jeder Wortfunktion eineindeutig und effektiv eine zahlentheoretische Funktion zugeordnet werden. Es sind dann entweder beide berechenbar oder beide nicht berechenbar. 109 Loop– und While–Berechenbarkeit Loop–Programm: Es seien x0, x1, · · · Variablen für natürliche Zahlen und c eine natürlichzahlige Konstante. Dann seien loop–Programme wie folgt rekursiv definiert: (a) jede Wertzuweisung xi := xj + c bzw. xi := xj − c sei ein loop–Programm (i, j ∈ N) (b) falls P, P1 , P2 bereits loop–Programme sind, so seien auch P1 ; P2 und loop xi do P end loop–Programme (i ∈ N) (c) Nur aufgrund von (a) und (b) sollen loop–Programme erhalten werden können. Loop–Berechenbarkeit: Eine Funktion f : Nk → N heiße genau dann loop–berechenbar, wenn es ein loop–Programm P gibt, das, gestartet mit den Werten n1 , · · · , nk in den Variablen x1, · · · , xk (und 0 in den anderen Variablen), nach endlich vielen Schritten stoppt mit dem Wert f (n1, · · · , nk ) in der Variablen x0. Dabei habe n−m für den Fall m > n den Wert 0 und werde sonst durch die übliche Subtraktion interpretiert, während in loop xi do P end das Programm P genau so oft ausgeführt werden möge, wie es der Wert von xi beim Eintritt in diese Schleife angibt (unabhängig davon, ob der Wert von xi innerhalb der Schleife geändert wird oder nicht). 110 Definitionen: Für beliebige i, j, r ∈ N werde vereinbart xi := xj bedeute xi := xj + 0 xi := c bedeute xi := xj + c für eine sonst nicht benutzte Variable xj (die also den Wert 0 hat) if xi = 0 then P end bedeute xj := 1; loop xi do xj := 0 end; wobei i 6= j sei loop xj do P end xr := xi + xj bedeute xr := xi; loop xj do xr := xr + 1 end wobei r = 6 j sei xr := xi ∗ xj bedeute xr := 0; loop xj do xr := xr + xi end wobei r 6= i und r 6= j sei Es existieren loop–Programme für die Berechnungen der Werte weiterer bekannter zahlentheoretischer Funktionen wie div, mod, ggt, kgV , min, max, Potenz mit natürlichzahliger Basis und natürlichzahligem Exponenten, Betrag der Differenz, Fakultätsfunktion, Anzahl der Primzahlen kleinergleich einer vorgegebenen Zahl, i–te Primzahl für ein vorgegebenes i ∈ N usw. Nicht durch ein loop–Programm lässt sich die Ackermann–Funktion ack von Folie 65 simulieren. Die übrigen Funktionen von Folie 65 sind loop–berechenbar. 111 While–Programm: (a) jedes loop–Programm sei auch ein while–Programm (b) falls P ein while–Programme ist, so sei auch while xi 6= 0 do P end ein while–Programm (i ∈ N) (c) Nur aufgrund von (a) und (b) sollen while–Programme erhalten werden können. Nachträglich könnte die loop–Schleife wieder eliminiert werden, denn loop xi do P end kann simuliert werden durch xj := xi ; while xj 6= 0 do xj := xj − 1; P end mit i 6= j, xj kommt nicht in P vor While–Berechenbarkeit: Eine partielle Funktion f : Nk 7→ N heiße genau dann while–berechenbar, wenn es ein while–Programm P gibt, das, gestartet mit den Werten n1, · · · , nk in den Variablen x1, · · · , xk (und 0 in den anderen Variablen), nach endlich vielen Schritten stoppt mit dem Wert f (n1, · · · , nk ) in der Variablen x0, sofern f (n1, · · · , nk ) definiert ist; sonst soll P nicht stoppen. Damit ist jede loop–berechenbare Funktion auch while–berechenbar. Es gibt while–berechenbare Funktionen, die nicht loop–berechenbar sind, beispielsweise die Ackermann–Funktion. Es gilt: Eine partielle Funktion f : Nk 7→ N ist genau dann while–berechenbar, wenn es eine (deterministische oder nichtdeterministische) Turing–Maschine gibt, die den Funktionswert f (x1, · · · , xk ) berechnet, falls er existiert, und die sonst bei der Eingabe von (x1, · · · , xk ) nicht anhält. 112