Einführung in die Informatik 4 Benjamin Gufler Erstellt mit LATEX II Inhaltsverzeichnis IV 1 1 Formale Sprachen 1.1 Relation, Graphen . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.1.1 Zweistellige Relationen . . . . . . . . . . . . . . . . . . . . . . 1.1.2 Wege in Graphen, Hüllenbildung . . . . . . . . . . . . . . . . 1.2 Grammatiken . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.2.1 Reduktive und generative Grammatiken . . . . . . . . . . . . 1.2.2 Die Sprachhierarchie nach Chomsky . . . . . . . . . . . . . . 1.2.3 Strukturelle Äquivalenz von Ableitungen . . . . . . . . . . . . 1.2.4 Sackgassen, unendliche Ableitungen . . . . . . . . . . . . . . 1.3 Chomsky – 3 – Sprachen, endliche Automaten, reguläre Ausdrücke . 1.3.1 Reguläre Ausdrücke . . . . . . . . . . . . . . . . . . . . . . . 1.3.2 Endliche Automaten . . . . . . . . . . . . . . . . . . . . . . . 1.3.3 Äquivalenz der Darstellungsformen . . . . . . . . . . . . . . . 1.3.4 Äquivalenz von regulären Ausdrücken, endlichen Automaten und Chomsky – 3 – Grammatiken . . . . . . . . . . . . . . . 1.3.5 Minimale Automaten . . . . . . . . . . . . . . . . . . . . . . 1.4 Kontextfreie Sprachen und Kellerautomaten . . . . . . . . . . . . . . 1.4.1 BNF – Notation . . . . . . . . . . . . . . . . . . . . . . . . . 1.4.2 Kellerautomaten . . . . . . . . . . . . . . . . . . . . . . . . . 1.4.3 Äquivalenz von Kellerautomaten und kontextfreien Sprachen 1.4.4 Greibach – Normalform . . . . . . . . . . . . . . . . . . . . . 1.4.5 LR(k) – Sprachen . . . . . . . . . . . . . . . . . . . . . . . . 1.4.6 LL(k) – Grammatiken . . . . . . . . . . . . . . . . . . . . . . 1.4.7 Rekursiver Abstieg . . . . . . . . . . . . . . . . . . . . . . . . 1.4.8 Das Pumping – Lemma für kontextfreie Sprachen . . . . . . . 1.5 Kontextsensitive Grammatiken . . . . . . . . . . . . . . . . . . . . . 5 5 5 7 8 9 10 11 12 14 14 15 16 2 Berechenbarkeit 2.1 Hypothetische Maschinen . . . . . . . . . . . . . . . . . . . . . . . . 2.1.1 Turing – Maschinen . . . . . . . . . . . . . . . . . . . . . . . 2.1.2 Registermaschinen . . . . . . . . . . . . . . . . . . . . . . . . 2.2 Rekursive Funktionen . . . . . . . . . . . . . . . . . . . . . . . . . . 2.2.1 Primitiv rekursive Funktionen . . . . . . . . . . . . . . . . . . 2.2.2 µ – rekursive Funktionen . . . . . . . . . . . . . . . . . . . . 2.2.3 Allgemeine Bemerkungen zur Rekursion . . . . . . . . . . . . 2.3 Äquivalenz der Berechenbarkeitsbegriffe . . . . . . . . . . . . . . . . 2.3.1 Äquivalenz von µ – Berechenbarkeit und Turing – Berechenbarkeit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.3.2 Äquivalenz von Registermaschinen- und Turing – Berechenbarkeit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.3.3 Churchs These . . . . . . . . . . . . . . . . . . . . . . . . . . 31 31 32 35 36 36 38 40 40 III 16 21 22 22 23 24 25 26 28 28 29 30 40 42 42 IV INHALTSVERZEICHNIS 2.4 Entscheidbarkeit . . . . . . . . . . . . . . . . . . . 2.4.1 Nicht berechenbare Funktionen . . . . . . . 2.4.2 (Nicht) entscheidbare Prädikate . . . . . . . 2.4.3 Rekursion und rekursiv aufzählbare Mengen . . . . . . . . 42 42 43 44 3 Komplexitätstheorie 3.1 Komplexitätsmaße . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.1.1 Zeitkomplexität . . . . . . . . . . . . . . . . . . . . . . . . . . 3.1.2 Bandkomplexität . . . . . . . . . . . . . . . . . . . . . . . . . 3.1.3 Zeit- und Bandkomplexitätsklassen . . . . . . . . . . . . . . . 3.1.4 Polynomiale und nichtdeterministisch polynomiale Zeitkomplexität . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.1.5 Nichtdeterminismus in Algorithmen — Backtracking . . . . . 3.2 N P – Vollständigkeit . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.2.1 Das Erfüllungsproblem . . . . . . . . . . . . . . . . . . . . . . 3.2.2 N P – vollständige Probleme . . . . . . . . . . . . . . . . . . 3.3 Effiziente Algorithmen für N P – vollständige Probleme . . . . . . . 3.3.1 Geschicktes Durchlaufen von Baumstrukturen . . . . . . . . . 3.3.2 Alpha / Beta – Suche . . . . . . . . . . . . . . . . . . . . . . 3.3.3 Dynamisches Programmieren . . . . . . . . . . . . . . . . . . 3.3.4 Greedy – Algorithmen . . . . . . . . . . . . . . . . . . . . . . 47 47 47 48 49 4 Effiziente Algorithmen und Datenstrukturen 4.1 Diskussion ausgewählter Algorithmen . . . . . 4.1.1 Komplexität von Sortierverfahren . . . . 4.1.2 Wege in Graphen . . . . . . . . . . . . . 4.2 Bäume . . . . . . . . . . . . . . . . . . . . . . . 4.2.1 Geordnete, orientierte, sortierte Bäume 4.2.2 Darstellung von Bäumen durch Felder . 4.2.3 AVL – Bäume . . . . . . . . . . . . . . 4.2.4 B – Bäume . . . . . . . . . . . . . . . . 4.3 Effiziente Speicherung großer Datenmengen . . 4.3.1 Rechenstruktur der Mengen (mit Zugriff 4.3.2 Mengendarstellung durch AVL – Bäume 4.3.3 Streuspeicherverfahren . . . . . . . . . . 63 63 63 64 65 65 65 66 66 66 67 67 70 . . . . . . . . . . . . . . . . . . . . . . . . . . . über . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Schlüssel) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50 51 53 53 54 55 56 57 59 60 Teil IV 1 3 • formale Sprache ↔ Beschreibungsmächtigkeit • Berechenbarkeit ↔ Grenze der Algorithmik • Komplexität ↔ Aufwand in Berechnungen für Probleme • effiziente Algorithmen ↔ aufwändige Algorithmen optimieren • (Logikprogrammierung) • effiziente Datenstrukturen • Datenbankmodelle 4 Kapitel 1 Formale Sprachen Zeichensatz A A∗ Menge der Wörter über A L ⊆ A∗ formale Sprache Frage: Mit welchen formalen Mitteln können wir Sprachen L ⊂ A∗ beschreiben? 1.1 1.1.1 Relation, Graphen Zweistellige Relationen Gegeben: Grundmenge M zweistellige (binäre, dyadische) Relation: R ⊆ M × M zur Notation: (x, y) ∈ R gleichwertig xRy Beispiel: ≤⊆ N × N (1, 3) ∈≤ 1≤3 Operationen auf Relationen: R, R1 , R2 ⊆ M × M • Durchschnitt: R1 ∩ R 2 • Vereinigung: R1 ∪ R 2 • Komplement: R− = (M × M ) \ R • konverse Relation: RT = {(x, y) ∈ M × M : (y, x) ∈ R} • Relationenprodukt: R1 ◦ R2 = {(x, z) ∈ M × M : ∃y ∈ M : (x, y) ∈ R1 ∧ (y, z) ∈ R2 } Monotonie: R1 ⊆ R10 ∧ R2 ⊆ R20 ⇒ R1 ◦ R2 ⊆ R10 ◦ R20 Frage: Wie Relationen definieren? • durch Aufzählung: {(3, 7), (7, 25), . . . } 5 6 KAPITEL 1. FORMALE SPRACHEN 7 25 3 32 Abbildung 1.1: graphische Relationsdefinition • logisch: {(x, y) : P (x, y)} (P . . . Prädikat) • graphisch (vgl. Abb. 1.1) • boolesche 3 3 0 7 0 25 0 32 0 Matrizen: 7 25 32 L 0 L 0 L 0 0 0 L L 0 0 Spezielle Relationen über M : • Nullrelation: 0M = ∅ • vollständige Relation: LM = (M × M ) • Identität: IM = {(x, x) ∈ M × M } Eigenschaften von Relationen R ⊆ M × M : • reflexiv: IM ⊆ R • symmetrisch: R = RT • antisymmetrisch: R ∩ RT ⊆ IM • asymmetrisch: R ∩ RT = 0M • transitiv: R ◦ R ⊆ R • irreflexiv. IM ∩ R = 0M • linkseindeutig R ◦ RT ⊆ IM (injektiv) • rechtseindeutig RT ◦ R ⊆ IM • linkstotal IM ⊆ R ◦ RT • rechtstotal IM ⊆ RT ◦ R (surjektiv) Relationsarten: Quasiordnung, Präordnung transitiv, reflexiv Striktordnung transitiv, asymmetrisch partielle Ordnung transitiv, antisymmetrisch, reflexiv lineare Ordnung transitiv, antisymmetrisch, reflexiv, R ∪ RT = LM Äquivalenzrelation transitiv, reflexiv, symmetrisch partielle Funktion rechtseindeutig totale Funktion rechtseindeutig, linkstotal Für eine gegebene Relation R ⊂ M × M heißt ein Element x ∈ M : 1.1. RELATION, GRAPHEN 7 • maximal ⇔ ∀y ∈ M : xRy ⇒ x = y • größtes Element ⇔ ∀y ∈ M : yRx Eine zweistellige Relation nennen wir auch gerichteten Graph, eine symmetrische zweistellige Relation auch ungerichteten Graph. Wir können für einen Graph R • Kantenmarkierungen: β : R → S • Knotenmarkierungen: α : M → S 0 einführen. 1.1.2 Wege in Graphen, Hüllenbildung Iteriertes Relationenprodukt: R ⊆ M × M R0 = IM Ri+1 = Ri ◦ R Ein Weg im Graph R von x0 nach xn der Länge n ist eine Folge von Knoten x0 , . . . , xn ∈ M mit ∀i, 1 ≤ i ≤ n : xi−1 Rxi Satz: Ein Weg von x nach y der Länge n existiert genau dann, wenn xRn y. Beweis: durch Induktion über n. Ist R eine partielle Ordnung, dann heißen Wege auch Ketten. Wir lassen dann auch unendliche Wege zu. R heißt Noethersch, wenn es keine unendlichen Wege gibt, in denen sich Knoten nicht wiederholen (streng aufsteigende Ketten). Beispiel: Graphen über N: a. 0 → 1 → 2 → 3 → . . . nicht Noethersch b. · · · → 3 → 2 → 1 → 0 Noethersch Hüllenbildung Gegeben sei eine Relation R ⊆ M × M . • reflexive Hülle Rrefl = R ∪ IM = \ {T ⊆ M × M : R ⊆ T ∧ T reflexiv} • transitive Hülle R+ = \ {T ⊆ M × M : R ⊆ T ∧ T transitiv} • reflexiv – transitive Hülle \ R∗ = {T ⊂ M × M : R ⊆ T ∧ T transitv ∧ T reflexiv} = (R ∪ IM )+ • symmetrische Hülle Rsym = R ∪ RT = \ {T ⊆ M × M : R ⊆ T ∧ T symmetrisch} • symmtrisch – reflexiv – transitive Hülle \ R∗ = {T ⊂ M × M : R ⊆ T ∧ T transitv ∧ T reflexiv ∧ T symmetrisch} 8 KAPITEL 1. FORMALE SPRACHEN a b x d f e c z g h Abbildung 1.2: Berechnungswege Zur Notation: Relation →, wir schreiben: ∗ → für die transitiv – reflexive Hülle ∗ ↔ für die symmetrisch – reflexiv – transitive Hülle. Eine Relation → heißt konfluent, wenn gilt: ∗ ∗ ∗ ∗ ∀x, y1 , y2 ∈ M : (x → y1 ∧ x → y2 ) ⇒ ∃z ∈ M : y1 → z ∧ y2 → z ∗ ∗ Relationenalgebra: RT ◦ R∗ ⊆ R∗ ◦ RT Sei R konfluent und Noethersch, dann gilt: jeder Weg (jede Berechnung) ausgehend von x endet in einem Punkt z (vgl. Abb. 1.2). 1.2 Grammatiken Formale Sprachen sind in der Regel unendlich und können deshalb nicht durch einfaches Aufzählen beschrieben werden. Deshalb brauchen wir Regeln (endliches Regelsystem), die die Wörter einer Sprache charakterisieren. Wir verwenden Textersetzungsregeln x, y ∈ V ∗ × V ∗ (V Zeichenmenge). x→y →⊆ V ∗ × V ∗ → ist eine endliche Menge von Textersetzungsregeln. Wir gewinnen aus → eine in der Regel unendliche Relation durch Regelanwendung (α, ω ∈ V ∗ ): x→y ⇒α◦x◦ω α◦y◦ω : algebraische Hülle, Halbgruppenhülle ∗ ω1 ω2 : ω1 wird in ω2 durch eine Folge von Ersetzungsschritten überführt. (V ∗ , ) heißt Semi – Thue – System (ist → symmetrisch, dann auch Thue – System). Beispiel: V = {a, b, c} 1. ba → ab ca → ac cb → bc + Noethersch? ja + konfluent? ja Jedes Wort wird duch eine terminierende Berechnung auf die Form ai bk cj gebracht. ∗ ist eine Äquivalenzrelation 1.2. GRAMMATIKEN 9 2. zusätzliche Regeln zu (1): aa → a bb → b cc → c 3. zusätzliche Regeln zu (1): aa → a bc → a Normalformen: abi ack a bi ck ab → b ac → c Normalformen: a bi ck ∗ Für jedes Semi – Thue – System (V ∗ , ) erhalten wir durch eine Äquivalenzrelation. [ω] bezeichnet die Äquivalenzklasse. ∗ V ∗ / bildet mit Konkatenation als Verknüpfung eine Halbgruppe mit neutralem Element [ε], genannt Regelgrammatikmonoid. 1.2.1 Reduktive und generative Grammatiken Eine Grammatik über einer (endlichen) Zeichenmenge M ist ein Tripel (M, →, z), wobei →∈ M ∗ × M ∗ endlich ist. z ∈ M heißt Axiom (Wurzel). G = (M, →, z) heißt auch Semi – Thue – Grammatik. Grammatiken beschreiben formale Sprachen. generativ: Ein Wort w ∈ M ∗ ist in der durch G beschriebenen formalen Sprache, ∗ wenn hzi w gilt. Generativer Sprachschatz: n o ∗ Lg (G) = w ∈ M ∗ : hzi w reduktiv: Ein Wort w ∈ M ∗ ist in der durch G beschriebenen formalen Sprache, ∗ wenn w hzi gilt. Reduktiver Sprachschatz: n o ∗ Lr (G) = w ∈ M ∗ : w hzi Sprechweise: w ∈ Lg (G): w wird durch G generiert (erzeugt). w ∈ Lr (G): w wird durch G erkannt. Eine generative Grammatik heißt ε – frei oder ε – produktionsfrei, wenn die rechte Seite jeder Regel nicht ε ist. Analog heißt eine reduktive Grammatik ε – frei oder ε – produktionsfrei, wenn die linke Seite jeder Regel nicht ε ist. Semi – Thue – Grammatiken haben enge Grenzen in der Ausdrucksmächtigkeit. 10 KAPITEL 1. FORMALE SPRACHEN Beispiel: Mn= {L} o k Sprache: S = L2 : k ∈ N ⊆ M ∗ Behauptung: Diese Sprache ist durch Semi – Thue – Grammatiken nicht beschreibbar. Beweis: Jede Regel hat die Form Li → Lj . Wir betrachen den reduktiven Fall. Wir benötigen mindestens eine Regel der Form Li → L mit i > 1. L muss also Axiom sein. S 63 Li Li−1 Li L Chomsky – Grammatiken strukturieren den Zeichensatz: M = T ∪N mit T ∩N = ∅. T : Terminalzeichen — Zeichen in Wörtern der formalen Sprache N : Nichtterminalzeichen — Hilfszeichen für die Ableitung Eine Chomsky – Grammatik ist ein Quadrupel G = (T, N, →, z) mit (T ∪ N, → , z) Semi – Thue – Grammatik, z ∈ N und T ∩ N = ∅. Lg (G) = Lg (T ∪ N, →, z) ∩ T ∗ Lr (G) = Lr (T ∪ N, →, z) ∩ T ∗ Beispiel: Reduktive Chomsky – Grammatik zur Beschreibung der Sprache S = k {L2 : k ∈ N}. T = {L} N = {z, A, B, C} z Axiom Regeln: L→A ε→B AAB → CB AAC → CA BC → BA BAB → z Wir betrachen den Sprachschatz der Semi – Thue – Grammatik, die wir erhalten, indem wir die ersten zwei Regeln weglassen. hBABi ist im Sprachschatz. 1. Jedes Wort im Sprachschatz hat die Form hBi ◦ x ◦ hBi mit x ∈ {AC}∗ . 2. In Rückwärtsableitung entstehen Cs immer am linken Rand. Um die Cs wieder zu entfernen, müssen sie nach rechts geschoben werden und verdoppeln dabei die Anzahl der As, d.h. die Wörter aus {AB}∗ im Sprachschatz haben die k Form BA2 B. Zwei reduktive (bzw. generative) Grammatiken heißen äquivalent, wenn sie die gleiche formale Sprache beschreiben. Eine reduktive Grammatik heißt wortlängenmonoton, wenn für jede ihrer Regeln w → w0 gilt |w| ≥ |w0 |. Gilt sogar |w| > |w0 |, so heißt die Grammatik strikt wortlängenmonoton. Konsequenz für Ableitungen: w0 w1 . . . wn |w0 | ≥ |w1 | ≥ . . . ≥ |wn | Eine Ersetzungsregel heißt separiert, wenn w0 ∈ N + ist. 1.2.2 Die Sprachhierarchie nach Chomsky Chomsky – Grammatiken lassen sich nach der äußeren Form ihrer Regeln klassifizieren. 1.2. GRAMMATIKEN 11 Chomsky – 0 – Sprachen sind formale Sprachen, die durch Chomsky – Grammatiken beschrieben werden können. Wir beschränken uns im Folgenden auf reduktive Grammatiken (generative analog). Eine Ersetzungsregel der Form u ◦ a ◦ v → u ◦ hbi ◦ v mit u, v ∈ (T ∪ N )∗ , a ∈ (T ∪ N )+ , b ∈ N heißt kontextsensitiv. Eine Chomsky – Grammatik heißt kontextsensitiv oder ε – produktionsfreie Chomsky – 1 – Grammatik, wenn alle ihrer Regeln kontextsensitiv sind. Trivialerweise gilt für alle Chomsky – 1 – Grammatiken: Alle Regeln sind wortlängenmonoton. Eine formale Sprache heißt kontextsensitiv oder Chomsky – 1 – Sprache, wenn es eine Chomsky – 1 – Grammatik gibt, die die Sprache beschreibt. Ist eine Sprache S kontextsensitiv, so wird S ∪ {ε} auch kontextsensitiv genannt. Eine Regel a → hbi mit a ∈ (T ∪ N )∗ und b ∈ N heißt kontextfrei. Sind alle Regeln der Grammatik kontextfrei, so heißt die Grammatik Chomsky – 2 – Grammatik oder kontextfrei. Jede Sprache, die durch eine Chomsky – 2 – Grammatik beschreibbar ist, heißt kontextfrei. Hinweis: BNF entspricht Chomsky – 2 – Grammatiken. Eine kontextfreie Regel der Form m ◦ hai ◦ n → hbi mit a, b ∈ N und m, n ∈ T + heißt beidseitig linear. Gilt jedoch n = ε, so heißt die Regel rechtslinear bzw. für m = ε linkslinear. Eine Regel der Form w → hbi mit w ∈ T + und b ∈ N heißt terminal. Eine Chomsky – 2 – Grammatik, deren sämtliche Regeln terminal oder rechtslinear (bzw. terminal oder linkslinear) sind, heißt Chomsky – 3 – Grammatik oder regulär. Eine Sprache heißt regulär oder Chomsky – 3 – Sprache, wenn sie durch eine reguläre Grammatik beschreibbar ist. C0 L Menge der Chomsky – 0 – Sprachen CSL Menge der Chomsky – 1 – Sprachen CF L Menge der Chomsky – 2 – Sprachen REG Menge der Chomsky – 3 – Sprachen Satz: REG ( CF L ( CSL ( C0 L 1.2.3 Strukturelle Äquivalenz von Ableitungen Ableitung über einer Grammatik: w0 w1 w2 . . . wn Fragen von Interesse: ∗ 1. Was ist ableitbar? () 2. Welche Struktur hat eine Ableitung? hBAAAABi → hBAACBi → hBCABi → hBAABi → . . . Die Anwendung von Regeln t1 → t01 und t2 → t02 in einem Wort α ◦ t1 ◦ β ◦ t2 ◦ γ α ◦ t01 ◦ β ◦ t2 ◦ γ α ◦ t01 ◦ β ◦ t02 ◦ γ mit α, β, γ ∈ (T ∪ N )∗ heißt vertauschbar. Bemerkung: Formal ist jede Ableitung ein Wort aus (T ∪ N ∪ {})∗ . Vertauschbarkeit definiert eine Relation auf der Menge der Ableitungen. Wir können zu dieser Relation die transitive symmetrische reflexive Hülle bilden. Stehen zwei Ableitungen in dieser Relation, dann heißen sie strukturell äquivalent. Bei Chomsky – 2 – Grammatiken können wir aufgrund der einfachen Struktur ihrer Regeln jeder Ableitung auf das Axiom einen Strukturbaum zuordnen. 12 KAPITEL 1. FORMALE SPRACHEN z z z z z z z z (a * a) + a + a Abbildung 1.3: Strukturbaum Beispiel Regeln: N = {z}, T = {(, ), +, ∗, a}, Axiom z. z+z →z z∗z →z a→z (z) → z Strukturbaum: s. Abb. (1.3). Wichtig: Zwei Ableitungen sind genau dann strukturell äquivalent, wenn sie den gleichen Strukturbaum darstellen, d.h. Strukturbäume sind Normalformen in der Klasse der strukturell äquivalenten Ableitungen. Ein Strukturbaum für eine Chomsky – 2 – Grammatik ist ein endlich verzweigender Baum: 1. Die Wurzel ist das Axiom. 2. Jeder Teilbaum hat folgende Eigenschaft: • Er ist genau ein Terminalzeichen und hat keine Teilbäume oder • er hat als Wurzel ein Nichtterminalzeichen x; dann gilt: die Wurzeln seiner Teilbäume können zu dem Wort w konkateniert werden und w → x ist eine Regel der Grammatik. ∗ Eine Grammatik heißt eindeutig, wenn für jedes Wort w ∈ T ∗ mit w z (z Wurzel) alle Ableitungen auf z strukturell äquivalent sind. Dann existiert genau ein Strukturbaum für jedes Wort im Sprachschatz. Feststellung: Die Grammatik aus obigem Beispiel ist nicht eindeutig. 1.2.4 Sackgassen, unendliche Ableitungen Chomsky – 2 – Grammatiken werden praktisch eingesetzt, um: 1. eine formale Sprache zu definieren 2. jedem Wort im Sprachschatz einen Strukturbaum (am besten eindeutig) zuzuordnen. 1.2. GRAMMATIKEN 13 ? E E E R R R R a * b + c E E R R R a * b + c Abbildung 1.4: Ableitung mit Sackgasse Ein Wort w heißt im Sprachschatz bzw. ∗ Grammatik akzeptiert, wenn w z gilt. Aufgaben für eine gegebene Grammatik: reduzierbar oder auch von der 1. Stelle fest, ob ein gegebenes Wort im Sprachschatz ist. 2. Ermittle (falls es im Sprachschatz ist) den Strukturbaum. Naive Idee: Wende auf das gegebene Wort Regeln an. Wir erhalten eine Ableitung w → w1 → w2 → . . . . Wir erhalten zwei Möglichkeiten: 1. Die Ableitung führt zu einem Wort, auf das keine Regel mehr anwendbar ist. 2. Die Ableitung bricht nicht ab und kann unendlich fortgesetzt werden. Falls (1) gilt und das Wort, auf das keine Regel mehr anwendbar ist, das Axiom ist, so ist w im Sprachschatz. Falls (1) gilt und das Wort, auf das keine Regel mehr anwendbar ist, nicht das Axiom ist, dann können wir daraus nicht schließen, dass s nicht im Sprachschatz ist. Wir sprechen von einer Sackgasse. Eine unendliche Ableitung (Fall (2)) sagt auch nichts darüber aus, ob das Wort im Sprachschatz ist. Beispiel: Einfache arithmetische Ausdrücke als Chomsky – 2 – Grammatik T = {a, . . . , z, +, ∗, (, )}, N = {R, E}, Wurzel: E Regeln: a→R .. . z→R R∗R→R E+R→R R→E (E) → R Ableitungen: s. Abb. (1.4). 14 KAPITEL 1. FORMALE SPRACHEN Neue Grammatik: T wie vorher, N = {F, R, A, E}, Axiom E. Regeln: a→F .. . z→F F ∗R→R R+A→A F →R R→A A→E (E) → F Diese Grammatik ist eindeutig, aber wieder nicht sackgassenfrei. In der Menge aller Ableitungen für ein Wort über einer Grammatik können wir eine sogenannte Linksableitung (analog Rechtsableitung) auszeichnen. Dies ist eine Ableitung, in der jeweils so weit links wie möglich eine Regel angewandt wird. Achtung: 1. In der Regel sind Linksableitungen nicht eindeutig. 2. In der Menge der strukturell äquivalenten Ableitungen sind Linksableitungen eindeutig. Eine Linksableitung führt für ein Wort im Sprachschatz nicht unbedingt zum Axiom. Akzeptierende Linksableitung: Regeln werden so weit links wie möglich angewendet, Regeln, die nicht zum Axiom (also in Sackgassen) führen, werden übersprungen. 1.3 Chomsky – 3 – Sprachen, endliche Automaten, reguläre Ausdrücke Wir betrachten drei Beschreibungsmittel für formale Sprachen: • reguläre Ausdrücke • endliche Automaten • Chomsky – 3 – Grammatiken Wir werden zeigen: Alle drei Beschreibungsmittel haben die gleiche Beschreibungsmächtigkeit. Sie beschreiben die gleiche Klasse formaler Sprachen, genannt reguläre Sprachen. 1.3.1 Reguläre Ausdrücke Gegeben sei eine Zeichenmenge T . Die Syntax und Semantik der regulären Ausdrücke ist wie folgt gegeben: 1. einfache reguläre Ausdrücke • x mit x ∈ T ist regulärer Ausdruck mit Sprache {hxi}. • ε ist regulärer Ausdruck mit Sprache {ε}. • {} ist regulärer Ausdruck mit Sprache ∅. 2. zusammengesetzte reguläre Ausdrücke (seien X, Y reguläre Ausdrücke) 1.3. CHOMSKY – 3 – SPRACHEN, ENDLICHE AUTOMATEN, . . . 15 • [X|Y ] ist regulärer Ausdruck mit Sprache LX ∪ LY . • [XY ] ist regulärer Ausdruck mit Sprache {x ◦ y : x ∈ LX ∧ y ∈ LY }. • X ∗ ist regulärer Ausdruck mit Sprache {x1 ◦· · ·◦xn : n ∈ N∧x1 , . . . , xn ∈ LX }. (wobei LX die formale Sprache zum regulären Ausdruck X und LY die formale Sprache zum regulären Ausdruck Y sei). X + = XX ∗ Gesetze der regulären Ausdrücke: • [[X|Y ]|Z] = [X|[Y |Z]] • [[XY ]Z] = [X[Y Z]] • [X|Y ] = [Y |X] • [[X|Y ]Z] = [[XZ]|[Y Z]] • [X[Y |Z]] = [[XY ]|[XZ]] • {}∗ = ε • Xε = X • X{} = {} • X ∗ = XX ∗ |ε • X ∗ = [X|ε]∗ 1.3.2 Endliche Automaten Endlicher Automat A = (S, T, s0 , SZ , δ): S endliche Menge von Zuständen T endliche Menge von Eingangszeichen s0 ∈ S Anfangszustand SZ ⊆ S Menge der Endzustände δ : S × (T ∪ {ε}) → 2S (= ℘(S) [Potenzmenge von S]) δ(s, t) Menge der Nachfolgezustände zu s ∈ S, t ∈ T ∪ {ε} Deterministischer Automat: δ(s, ε) ⊆ {s} |δ(s, t)| ≤ 1 ∀s ∈ S, t ∈ T Partieller Automat: ∃t ∈ T, s ∈ S : δ(s, t) = ∅ ε – freier Automat: δ(s, ε) = ∅ ∀s ∈ S 16 KAPITEL 1. FORMALE SPRACHEN a a s1 s0 s2 b Abbildung 1.5: endlicher Automat Beispiel: A = ({s0 , s1 , s2 } , {a, b} , s0 , {s1 } , δ) (s. Abb. 1.5) δ ε a b s0 ∅ {s1 } ∅ ∅ s1 ∅ {s2 } ∅ {s1 } s2 ∅ a Notation: Wir schreiben s1 → s2 , falls s2 ∈ δ(s1 , a). w∗ Jedem Wort w ∈ T ∗ ordnen wir eine Relation → auf Zuständen zu: ∗ ε ∗ ε → = → hai ∗ ε ∗ a ε ∗ → =→ ◦ → ◦ → w1 ◦w2 ∗ → w ∗ w ∗ =→1 ◦ →2 w1 , w2 ∈ T ∗ w∗ Wir schreiben δ ∗ (s, w) für {s0 : s → s0 }. Damit können wir die Sprache charakterisieren, die durch einen Automaten A beschrieben wird: n o w∗ L(A) = w ∈ T ∗ : ∃z ∈ SZ : s0 → z 1.3.3 Äquivalenz der Darstellungsformen Zunächst beantworten wir die Frage, ob Automaten mit spontanen Übergängen beschreibungsmächtiger sind als Automaten ohne spontane Übergänge. Satz: Zu jedem endlichen Automaten existiert ein ε – freier endlicher Automat, der die gleiche Sprache akzeptiert. Beweisidee: Wir konstruieren zu einem gegebenen endlichen Automaten einen ε – freien Automaten mit gleicher Sprache: ε ∗ ε ∗ 1. → – Zyklen: Wir fassen Knoten, die durch → – Zyklen verbunden sind, zu ε ∗ einem Knoten zusammen. Es entsteht ein Automat ohne → – Zyklen (ε – Schlingen können ohnehin weggelassen werden). ε ∗ 2. → – Wege (zyklenfrei): Durchschalten von ε – Übergängen (vgl. Abb. 1.6). Satz: Zu jedem ε – freien nichtdeterministischen Automaten existiert ein ε – freier deterministischer Automat mit gleichem Sprachschatz. Beweisidee: Wir konstruieren zu dem Automaten mit Zustandsmenge S einen Automaten mit Zustandsmenge 2S (vgl. Abb. 1.7). 1.3.4 Äquivalenz von regulären Ausdrücken, endlichen Automaten und Chomsky – 3 – Grammatiken Satz: Zu jedem endlichen Automaten existiert ein regulärer Ausdruck, der die gleiche Sprache beschreibt. Beweis: (konstruktiv) 1.3. CHOMSKY – 3 – SPRACHEN, ENDLICHE AUTOMATEN, . . . 17 a a a Abbildung 1.6: ε – Übergänge durchschalten a s1 s0 a s2 a s0 {s 1, s2} Abbildung 1.7: Übergang von nichtdeterministischen zu deterministischen Automaten Wir geben zu einem gegebenen endlichen Automaten einen regulären Ausdruck mit gleichem Sprachschatz an. Zustandsmenge: S = {s1 , . . . , sn } O.B.d.A. sei der endliche Automat ε – frei. Wir konstruieren eine Familie formaler Sprachen L(i, j, k) ⊆ T ∗ , wobei i, j ∈ {1, . . . , n}, k ∈ {0, . . . , n}, und die dazugehörigen regulären Ausdrücke E(i, j, k), die jeweils die formale Sprache L(i, j, k) beschreiben. Dabei sei w L(i, j, k) = w ∈ T ∗ : si → sj k w In → betrachten wir dabei nur Pfade in Übergangsgraphen mit Zwischenzuständen k sl mit l ≤ k. L(i, j, 0) = {hai : sj ∈ δ(si , a)} a E(i, j, 0) = a1 | . . . |aq (Menge aller a ∈ T mit si → sj ) L(i, j, k + 1) = L(i, j, k) ∪ {u ◦ v ◦ w : u ∈ L(i, k + 1, k)∧ v ∈ L(k + 1, k + 1, k)∗ ∧ w ∈ L(k + 1, j, k)} E(i, j, k + 1) = E(i, j, k)|E(i, k + 1, k)E(k + 1, k + 1, k)∗ E(k + 1, j, k) Die Sprache der endlichen Automaten sind die Wege vom Anfangszustand s1 zu den Endzuständen SZ = {sZ1 , . . . , sZp }. Dies entspricht den regulären Ausdrücken E(1, Z1 , n)| . . . |E(1, Zp , n). Satz: Zu jedem regulären Ausdruck lässt sich ein endlicher Automat angeben, der die gleiche Sprache beschreibt. Beweis: Wir geben die Konstruktionsprinzipien an. 18 KAPITEL 1. FORMALE SPRACHEN x (a) (b) (c) Abbildung 1.8: Endliche Automaten zu elementaren regulären Ausdrücken X Y Abbildung 1.9: 1. Für elementare reguläre Ausdrücke: Sei x ∈ T , dann ist x regulärer Ausdruck mit einer Sprache, die durch den endlichen Automaten aus Abb. (1.8(a)) beschrieben wird. ε ist regulärer Ausdruck mit endlichem Automaten (1.8(b)). (1.8(c)) ist Automat für die leere Sprache. 2. Seien X und Y reguläre Ausdrücke, die durch die endlichen Automaten aus Abb. (1.9) beschrieben werden. Für [X|Y ] akzeptiert (1.10(a)) die gleiche Sprache. Für XY akzeptiert der endliche Automat (1.10(b)) die gleiche Sprache. Automat zu X ∗ ist (1.10(c)). Die Überlegungen zeigen: Zu jedem regulären Ausdruck können wir einen endlichen Automaten angeben, der die gleiche formale Sprache beschreibt — vorausgesetzt, wir finden bei zusammengesetzten regulären Ausdrücken endliche Automaten für die Unterausdrücke. Vollständige Induktion über die Anzahl der Symbole in einem regulären Ausdruck liefert die Behauptung. Satz: Zu jedem deterministischen ε – freien endlichen Automaten können wir eine Chomsky – 3 – Grammatik angeben, die die gleiche Sprache beschreibt. Beweis: Gegeben sei der endliche Automat A = (S, T, s0 , SZ , δ). OBdA gehe keine Kante nach s0 . Jeder Kante (d.h. jedem Übergang δ(s, a) = s0 ) ordnen wir eine Regel der Grammatik zu. Wir verwenden die Zustände als Nichtterminalzeichen. Für Übergänge vom Anfangszustand s0 aus: δ(s0 , a) = si verwenden wir die Regel a → si Für Übergänge δ(si , a) = sj führen wir die Regel si a → sj ein. Zu den Zuständen als Nichtterminalzeichen nehmen wir noch ein Sonderzeichen, die Wurzel Z (oBdA sei Z ∈ / S) hinzu. Für jeden Zustand si ∈ SZ nehmen wir die Regel si → Z hinzu. Die entstehende Grammatik akzeptiert die gleiche Sprache. 1.3. CHOMSKY – 3 – SPRACHEN, ENDLICHE AUTOMATEN, . . . X Y 19 (a) X Y (b) X (c) Abbildung 1.10: Endliche Automaten zu zusammengesetzten regulären Ausdrücken 20 KAPITEL 1. FORMALE SPRACHEN Satz: Zu jeder Chomsky – 3 – Grammatik existiert ein endlicher Automat, der die gleiche Sprache akzeptiert. Beweis: OBdA sei die Chomsky – 3 – Grammatik linkslinear und alle linkslinearen Regeln von der Form sa → s0 mit a ∈ T . Wir geben einen endlichen Automaten an: Zustände S = N ∪ {s0 } Eingabezeichen T Anfangszustand s0 Endzustand {Z} Übergangsfunktion ∀s ∈ N, a ∈ T : δ(s, a) = {s0 ∈ S : sa → s0 ist Regel der Grammatik} ∀s ∈ N : δ(s, ε) = {s0 ∈ S : s → s0 ist Regel der Grammatik} ∀a ∈ T : δ(s0 , a) = {s0 ∈ S : a → s0 ist Regel der Grammatik} Es entsteht ein Automat, der die gleiche Sprache wie die Grammatik beschreibt: Jede Ableitung in der Grammatik entspricht einer Folge von Zustandsübergängen im Automaten und umgekehrt. Fazit • Die Beschreibungsmittel – endliche Automaten – reguläre Ausdrücke – Chomsky – 3 – Grammatiken sind gleichmächtig (beschreiben die gleiche Klasse formaler Sprachen). • Die Übergänge sind konstruktiv: Zu jedem endlichen Automaten (regulären Ausdruck, Chomsky – 3 – Grammatik) können wir systematisch (durch einen Algorithmus) einen regulären Ausdruck (Chomksy – 3 – Grammatik, endlichen Automaten) angeben, der die gleiche formale Sprache beschreibt. Solche Sprache heißen regulär. Wir wenden uns nun der Frage zu, wie wir für eine vorgegebene formale Sprache erkennen können, dass sie regulär ist. Wir beginnen mit einem einfachen Beispiel für eine nichtreguläre Sprache: Lab = {an bn : n ∈ N \ {0}} Diese Sprache können wir ohne Probleme durch eine Chomksy – 2 – Grammatik beschreiben: T = {a, b}, N = {Z}, Z Wurzel ab → Z aZb → Z Behauptung: Die Sprache Lab ist nicht regulär. Beweis: Wir zeigen, dass kein endlicher Automat existiert, der Lab akzeptiert. 1. Der endliche Automat hat nur endlich viele Zustände (sei h die Anzahl der Zustände). 1.3. CHOMSKY – 3 – SPRACHEN, ENDLICHE AUTOMATEN, . . . 21 2. an bn und am bm sind im Sprachschatz, d.h. es existieren Zustände sn , sm und sz , so dass gilt: an ∗ bn ∗ s0 → sn ∧ sn → sz am ∗ bm ∗ s0 → sm ∧ sm → sz Falls sn = sm gilt, werden auch Wörter an bm bzw. am bn akzeptiert. Da nur endlich viele Zustände (genaugenommen k) existieren, muss es Zahlen n und m geben mit n 6= m und sn = sm . Widerspruch. Problem bei endlichen Automaten: Endliche Automaten können nur eine endliche Information speichern, können nicht unbeschränkt zählen. Sprachen mit Klammerstrukturen (beliebiges Nesten von Öffnenden und schließenden Klammern) sind nicht regulär. Satz (Pumping – Lemma für reguläre Sprachen): Zu jeder regulären Sprache L existiert eine Zahl n ∈ N, so dass für alle Wörter w ∈ L mit |w| ≥ n gilt: w lässt sich in Wörter x, y, z ∈ T ∗ zerlegen: w = x ◦ y ◦ z mit |y| ≥ 1, |x ◦ y| ≤ n und x ◦ y i ◦ z ∈ L für alle i ∈ N \ {0}. Beweis: L ist nach Voraussetzung regulär. Es existiert ein deterministischer endlicher ε – freier Automat, der L akzeptiert. Sei n die Anzahl seiner Zustände und w∗ w ∈ L. Es gilt s0 → sz . Dabei durchläuft der Automat |w| + 1 Zustände. Gilt |w| ≥ n, so tritt ein Zustand mindestens zwei Mal auf, d.h. x∗ y ∗ x∗ yi z ∗ s0 → sh ∧ sh → sh ∧ sh → sz Dann gilt auch ∗ z ∗ s0 → sh ∧ sh → sh ∧ sh → sz Damit lässt sich auch zeigen: Lab ist nicht regulär. 1.3.5 Minimale Automaten Wir zeigen nun, wie wir für eine reguläre Sprache einen minimalen Automaten (d.h. einen Automaten mit einer minimalen Anzahl von Zuständen) angeben können. Sei also L ⊆ T ∗ . Wir definieren eine Äquivalenzrelation ∼L durch (seien v, w ∈ L): v ∼L w ⇔def ∀u ∈ T ∗ : v ◦ u ∈ L ⇔ w ◦ u ∈ L Dadurch erhalten wir Äquivalenzklassen [v]L = {w ∈ T ∗ : w ∼L v} Satz (Myhill – Nerode): L ist genau dann regulär, wenn die Menge der Äquivalenzklassen endlich ist. Beweisidee: Jede Äquivalenzklasse definiert einen endlichen Automaten, der die Sprache akzeptiert. Umgekehrt: Jeder Zustand in einem endlichen Automaten entspricht einer Äquivalenzklasse. Die Beweisidee liefert bereits einen Hinweis, wie wir einen ε – freien deterministischen endlichen Automaten mit totaler Übergangsfunktion konstruieren können, der die Sprache L akzeptiert und eine minimale Anzahl von Zuständen hat. Wir zeigen nun, wie wir aus einem gegebenen endlichen ε – freien deterministischen Automaten einen minimalen Automaten konstruieren, indem wir bestimmte Zustände zusammenlegen. • 1. Schritt: Wir entfernen alle Zustände, die vom Anfangszustand aus nicht erreichbar sind. 22 KAPITEL 1. FORMALE SPRACHEN • 2. Schritt: Wir konstruieren eine Folge von Prädikaten auf Zuständen: pi : S 0 × S 0 → B: w∗ w∗ pi (s1 , s2 ) = ∀w ∈ T ∗ : |w| ≤ i ⇒ [(∃z ∈ SZ : s1 z) ⇔ (∃z ∈ SZ : s2 z)] pi lässt sich induktiv definieren: p0 (s1 , s2 ) = (s1 ∈ SZ ⇔ s2 ∈ SZ ) pi+1 (s1 , s2 ) = pi (s1 , s2 ) ∧ ∀x ∈ T : pi (δ(s1 , x), δ(s2 , x)) Da der Automat endlich ist, existiert ein k mit pk = pk+1 . Das Prädikat pk definiert uns dann, welche Zustände des Automaten äquivalent sind und zu einem Zustand im minimalen Automaten verschmolzen werden können. Anwedung von regulären Ausdrücken: z.B. in grep, sed, vi, vim, emacs, lex, flex 1.4 Kontextfreie Sprachen und Kellerautomaten Chomsky – 3 – Grammatiken genügen im Allgemeinen nicht, um komplexe formale Sprachen wie Programmiersprachen zu beschreiben (s. z.B. Klammerstrukturen wie in Lab = {an bn : n ∈ N\{0}}). Es gilt REG ( CF L, d.h. die Klasse der kontextfreien Sprachen ist mächtiger als die der regulären Sprachen. Chomsky – 3 – Grammatiken, reguläre Ausdrücke und endliche Automaten eignen sich nicht, um kontextfreie Sprachen zu beschreiben. Analog zu regulären Ausdrücken, endlichen Automaten und Chomsky – 3 – Grammatiken bei regulären Sprachen sind in der Klasse der kontextfreien Sprachen die folgenden Beschreibungsformen gleichmächtig: • Chomsky – 2 – Grammatiken • BNF – Ausdrücke • Kellerautomaten Diese Mittel werden bei der Spezifikation und der Syntaxanalyse von Programmiersprachen in Übersetzern und Interpretern eingesetzt. 1.4.1 BNF – Notation BNF ist eine Erweiterung der regulären Ausdrücke um: 1. Hilfszeichen (Nichtterminale) 2. (rekursive) Gleichungen für Hilfszeichen Sei T eine Menge von Terminalzeichen und N eine Menge von Nichtterminalzeichen. Eine BNF – Beschreibung einer Sprache L hat die Form x1 ::= E1 .. . xn ::= En mit x1 , . . . , xn ∈ N und E1 , . . . , En reguläre Ausdrücke über T ∪ N . 1.4. KONTEXTFREIE SPRACHEN UND KELLERAUTOMATEN 23 Beispiel: hZi ::= ab|a hZi b beschreibt die kontextfreie Sprache {an bn : n ∈ N \ {0}}. Für eine BNF – Beschreibung kann für jedes xi (1 ≤ i ≤ n) eine Chomsky – 2 – Grammatik angegeben werden. Die regulären Ausdrücke Ei werden in Chomsky – 3 – Grammatiken (mit neuem Nichtterminalzeichen Zi als Axiom) umgeformt und für die BNF – Regel xi ::= Ei wird die Regel Zi → xi in die Grammatik aufgenommen. Anmerkung: Es gibt zahlreiche Stile und Erweiterungen von BNF. Die Klammern [. . .] dienen oft der Kennzeichnung optionaler Anteile, z.B. x[4] für x|x4. {x}m n bezeichnet die n- bis m-malige Wiederholung von x. 1.4.2 Kellerautomaten Ein Kellerautomat ist ein 7-Tupel KA = (S, T, K, δ, s0 , k0 , SZ ) mit: • einer endlichen Menge von Zuständen S • einer endlichen Menge von Eingabezeichen T • einer endlichen Menge von Kellerzeichen K • einer endlichen Übergangsrelation δ : S × (T ∪ {ε}) × K → 2S×K ∗ • einem Anfangszustand s0 ∈ S • einem Kellerstartsymbol k0 ∈ K • und Endzuständen SZ ⊆ S Prinzip: Ein Kellerautomat verarbeitet ein Wort w ∈ T ∗ , indem er w von links nach rechts liest und in jedem Schritt, abhängig vom aktuellen Zustand s, vom Eingangszeichen a und vom obersten Kellerzeichen k entsprechend der Übergangsrelation δ • in einen neuen Zustand s0 ∈ S übergeht, • das oberste Zeichen k vom Keller entfernt und • eine (ggf. leere) Sequenz u ∈ K ∗ auf den Keller legt. Der Kellerautomat akzeptiert w ∈ T ∗ , wenn die vollständige Verarbeitung von w von s0 aus zu einem Endzustand s ∈ SZ führt. Darstellung: als Graphen, analog zu endlichen Automaten: Knoten s ∈ S, Kanten (a, k, u) ∈ (T ∪ {ε}) × K × K ∗ . Eine Kante (a, k, u) von s nach s0 existiert genau dann, wenn (s0 , u) ∈ δ(s, a, k) ist. Beispiel: Kellerautomat für {(n )n : n ∈ N \ {0}}: KA = ({s0 , s1 , s2 } , {(, )} , {|, 0} , δ, s0 , 0, {s2 }) δ(s0 , (, 0) = {(s0 , h|0i)} δ(s0 , (, 1) = {(s0 , h||i)} δ(s0 , ), 1) = {(s1 , ε)} δ(s1 , ), 1) = {(s1 , ε)} δ(s1 , ε, 0) = {(s2 , ε)} 24 KAPITEL 1. FORMALE SPRACHEN Arbeitsweise formal: Wir betrachten Kellerkonfigurationen (s, w, v) ∈ S × T ∗ × K ∗ mit Kontrollzustand s, (Rest-)Eingabewort w und Kellerwort v. Ein Kellerautomat induziert eine Übergangsrelation → auf Konfigurationen vermöge (s1 , w, hki ◦ v) → (s2 , w, u ◦ v) falls (s2 , u) ∈ δ(s1 , ε, k) (s1 , hai ◦ w, hki circv) → (s2 , w, u ◦ v) falls (s2 , u) ∈ δ(s1 , a, k) und Ein Wort w ∈ T ∗ wird vom Kellerautomaten akzeptiert, wenn es ein s ∈ SZ und ein v ∈ K ∗ gibt, so dass ∗ (s0 , w, hk0 i) → (s, ε, v) gilt. Die vom Kellerautomaten akzeptierte Sprache bezeichnen wir mit L(KA). Anmerkung: Akzeptieren durch leeren Keller ist gleichmächtig, d.h. w ∈ T ∗ ∗ wird vom Kellerautomaten genau dann akzeptiert, wenn (s0 , w, ε) → (s, ε, ε) mit s ∈ S gilt. Durch Kellerautomaten erhalten wir für beliebige kontextfreie Sprachen unmittelbar einen (in der Regel sehr ineffizienten) Erkennungsalgorithmus. Im Gegensatz zu endlichen Automaten sind deterministische Kellerautomaten nicht äquivalent zu nichtdeterministischen Kellerautomaten. 1.4.3 Äquivalenz von Kellerautomaten und kontextfreien Sprachen Satz: Ist L eine kontextfreie Sprache, dann gibt es einen Kellerautomaten K, so dass L(K) = L ist. Idee: Gegeben sei eine kontextfreie Grammatik G = (T, N, →, Z). Gesucht ist ein Kellerautomat K mit L(K) = L(G). K = (S, T, T ∪ N ∪ {#}, δ, ε, #, {se }) Dabei setzt sich S aus ε, se , sv und der Menge aller (Teil-)Sequenzen der linken Seiten der Regeln zusammen. Sei he1 . . . en i, ei ∈ N ∪ T , linke Seite einer Regel he1 . . . en i → A. Wir konstruieren die Übergangsrelation δ: 1. Kellern: (ε, haki) ∈ δ(ε, a, k) a ∈ T 2. Regelerkennung: (hei−1 . . . ej i , ε) ∈ δ(hei . . . ej i , ε, ei−1 ) (hei . . . ej+1 i , hki) ∈ δ(hei . . . ej i , ej+1 , k) 3. Regelanwendung: (ε, hAki) ∈ δ(he1 . . . en i , ε, k) 4. Akzeptanz: (sv , ε) ∈ δ(ε, ε, Z) (se , ε) ∈ δ(sv , ε, #) K ist nicht deterministisch und ineffizient! K kellert die Eingabe, bis er an einer beliebigen Stelle mit dem Aufbau der linken Seite einer Regel beginnt. 1.4. KONTEXTFREIE SPRACHEN UND KELLERAUTOMATEN 25 Beispiel: Chomsky – 2 – Grammatik G = ({a, b}, {Z}, {aZb → Z, ZZ → Z, ab → Z}) S = {ε, sv , se , hai , hbi , hZi , haZi , hZbi , hZZi , haZbi , habi} δ(ε, x, k) = {(hxi , hki), (ε, hxki)} k 6= # ⇒δ(ε, ε, k) = {(hki , ε)} δ(hZi , b, k) = {(hZbi , hki), (hZZi , hki)} δ(ε, ε, Z) = {(sv , ε)} Beobachtung: K ist nichtdeterministisch und hat Sackgassen (z.B. beim Ableiten von haabbi), d.h. er ist sehr ineffizient. Satz: Zu jedem Kellerautomaten K (mit ε ∈ / L(K)) existiert eine kontextfreie Grammatik G mit L(K) = L(G). Beweis: (Literatur) Vorgehensweisen bei der Reduktion von Wörtern kontextfreier Sprachen durch Kellerautomaten: 1. Top-Down: Der Keller beinhaltet zu Beginn das Axiom. Ableitung eines (Teil)Worts v ∈ T ∗ im Keller und Vergleich mit der Resteingabe. Bei Übereinstimmung Kürzung des Eingabeworts und Entfernen von v aus dem Keller. 2. Bottom-Up: Der Keller ist am Anfang leer. Schrittweises lesen / kellern der Eingabe, Reduktion von Teilwörtern auf dem Keller. Erfolg, wenn das Axiom im Keller und die Eingabe leer ist (oben angewendet). Beide Vorgehensweisen sind i.a. nichtdeterministisch und wegen der Suche im Ableitungsbaum ineffizient. Verbesserungsansatz: Eliminieren des Nichtdeterminismus durch Beschränkung der Regelauswahl. Der Erfolg ist dabei abhängig von der kontextfreien Sprache. 1.4.4 Greibach – Normalform Eine kontextfreie Grammatik G = (T, N, →, Z) ist in Greibach – Normalform, wenn jede Ersetzungsregel folgende Gestalt hat: hai ◦ w → x mit a ∈ T , w ∈ N ∗ und x ∈ N , d.h. Verarbeitung genau eines a ∈ T links bei jeder Ersetzung. Satz: Jeder von einer ε – freien, kontextfreien Grammatik erzeugte Sprachschatz kann auch durch eine kontextfreie Grammatik in Greibach – Normalform erzeugt werden. Beweis: (Literatur) Satz: Zu jeder kontextfreien Grammatik G existiert ein Kellerautomat K mit L(G) = L(K). Beweis: G = (T, N, →, Z) sei o.B.d.A. in Greibach – Normalform. K = ({s0 } , T, N, δ, s0 , Z, {s0 }) ∀k ∈ N : δ(s0 , a, k) = {(s0 , w) : hai ◦ w → k} Es gilt für u ∈ T ∗ und x ∈ N ∗ : ∗ ∗ u x ⇔ (s0 , u, x) → (s0 , ε, ε) Es genügt also eine einelementige Zustandsmenge. 26 KAPITEL 1. FORMALE SPRACHEN Beispiel: kontextfreie Grammatik G = ({a, b} , {Z, U } , {aU → Z, aZU → Z, b → U } , Z) K = ({s0 } , {a, b} , {Z, U } , δ, s0 , Z, {z0 }) n o δ(s0 , a, Z) = (s0 , hU i)[1a] , (s0 , hZU i)[1b] n o δ(s0 , b, U ) = (s0 , ε)[2] Reduktion von haabbi: [1a] (s0 , haabbi , hZi) → (s0 , habbi , hU i) Sackgasse [1b] [1a] [2] (s0 , haabbi , hZi) → (s0 , habbi , hZU i) → (s0 , hbbi , hZU i) → [2] (s0 , hbi , hU i) → (s0 , ε, ε) Dieser Kellerautomat ist also bereits stark vereinfacht, aber nach wie vor nichtdeterministisch. Benötigt aus praktischer Sicht: eingeschränkte kontextfreie Sprachen, die beschränkten Nichtdeterminismus verursachen und Sackgassen eliminieren. 1.4.5 LR(k) – Sprachen (left – rightmost: Eingabe von links nach rechts, Rechtsableitungen, k Zeichen Vorschau) Idee: Bottom-Up: Eingabewort von links nach rechts lesen und kellern, bis eine Regel anwendbar ist. Bestimmung der anzuwendenden Regel anhand von höchstens k Zeichen des Restworts (bzw. rechts der Anwendungsstelle); Rechtskontext. In einer LR(k) – Sprache kann bei jeder akzeptierenden Linksreduktion durch Betrachtung der k nächsten Zeichen rechts der Anwendungsstelle die anzuwendende Regel eindeutig bestimmt werden. Bei bestimmten Grammatiken kann die Auswahl einer Regel der Form he1 . . . en i → A für w = x ◦ he1 . . . en i ◦ y durch Betrachtung eines endlichen Präfixes von y gesteuert werden. Kontextbedingung ist die Menge von Wörtern, die die Präfixe von y festlegt, welche die Regelanwendung zulassen. Kontextfreie Grammatiken heißen LR – deterministisch, wenn es für alle Regeln endliche Kontextbedingungen gibt, so dass der von links nach rechts arbeitende Kellerautomat für alle w ∈ L(G) und alle akzeptierenden Reduktionen in jedem Schritt genau eine Regel findet, die Reduktion vollzieht und Sackgassen vermeidet. Satz: Jede LR – deterministische, kontextfreie Grammatik ist eindeutig. Beweis: Gemäß der Definition von LR – deterministischen, kontextfreien Grammatiken gibt es eine eindeutige Einschränkung des Kellerautomaten, der die Ableitung erzeugt. Definition: LR(k): w[i : k] bezeichne das Teilwort hwi . . . wk i von w = hw1 . . . wn i (i, k, n ∈ N, 1 ≤ i ≤ k ≤ n). Eine azyklische kontextfreie Grammatik (T, N, →, Z) heißt LR(k) – Grammatik, wenn für jedes Paar von Ableitungen in Linksnormalform u1 ◦ a1 ◦ x1 u1 ◦ hB1 i ◦ x1 . . . Z u2 ◦ a2 ◦ x2 u2 ◦ hB2 i ◦ x2 . . . Z 1.4. KONTEXTFREIE SPRACHEN UND KELLERAUTOMATEN 27 mit {a1 → B1 , a2 → B2 } ⊆→ gilt: u1 ◦ a1 ◦ x1 [1 : k] = u1 ◦ a2 ◦ x2 [1 : k] ⇒ a1 = a2 ∧ B1 = B2 ∧ u1 = u2 d.h. durch x1 [1 : k] ist die anzuwendende Regel eindeutig festgelegt. Beispiel: (LR(0)) G = ({a, b}, {Z, X}, {ZX → Z, X → Z, aZb → X, ab → X}, Z) Ableitung von haabbabi: haabbabi → haXbabi → haZbabi → hXabi → hZabi → hZXi → hZi Kellerautomat: Wort kellern, bis Regel anwendbar; dann Regeln anwenden, solange möglich. Keller Restwort ε aabbab abbab a aa bbab aab bab aX bab aZ bab aZb ab ab X Z ab Za b Zab ε ZX ε Z ε Der Keller beinhaltet also immer das Präfix des zu reduzierenden Wortes. Bei LR(0) ist kein Kontext notwendig, um die richtigen Regeln zu wählen. Satz: Jede LR(k) – Grammatik ist eindeutig. Beweis: LR(k) – Grammatiken sind LR – deterministisch. Beispiel: (LR(1)) G = ({Z, A, P, E}, {(, ), +, −, ∗, /, a}, →, Z) Regel A→Z E→A A+E →A A−E →A P →E E∗P →E E/P → E (A) → P a→P Kontextbedingung ε +, −, ), ε +, −, ), ε +, −, ), ε t∈T t∈T t∈T t∈T t∈T ha + a ∗ ai . . . hA + E ∗ ai hA + E ∗ P i . . . Z LR(k) – Grammatiken erlauben die Reduktion von Wörtern durch Kellerautomaten mit akzeptablem Aufwand. Insbesondere LR(1) – Grammatiken werden in der Praxis eingesetzt. Anmerkung: Es gibt keinen Algorithmus, der für eine beliebige Grammatik G entscheidet, ob sie LR(k) ist. 28 KAPITEL 1. FORMALE SPRACHEN 1.4.6 LL(k) – Grammatiken (left leftmost) Gegensatz zu LR(k): Top-Down – Produktion der Ableitungen, startend vom Axiom Z, dadurch Linksableitung (bzw. Rechtsreduktion) statt Linksreduktion. Idee: (Kellerautomat) 1. Produktion von v ∈ T ∗ ausgehend von Nichtterminalen auf dem Keller und abhängig vom Rechtskontext. 2. Vergleich des erzeugten v mit Präfix des (Rest-)Eingabeworts. 3. Bei Übereinstimmung: Entfernen von v aus dem Keller und dem Eingabewort. Definition: LL(k): Eine azyklische, kontextfreie Grammatik G = (T, N, →, Z) heißt LL(k) – Grammatik (mit k ∈ N), wenn für alle Paare von Ableitungen in Rechtsnormalform (a1 , a2 , v, u1 , u2 ∈ (T ∪ N )∗ , w ∈ T ∗ , B ∈ N ) ∗ ∗ ∗ ∗ ∗ ∗ v ◦ u1 v ◦ a1 ◦ w v ◦ hBi ◦ w Z v ◦ u2 v ◦ a2 ◦ w v ◦ hBi ◦ w Z mit {a1 → B, a2 → B} ⊆→ gilt: u1 [1 : k] = u2 [1 : k] ⇒ a1 = a2 d.h. die Zerteilung ist mit u1 [1 : k] eindeutig festgelegt. Beispiel: LL(1) – Grammatik G = ({z}, {a, +, −}, {a → z, −z → z, +zz → z}, z) Reduktion von h+-a+aai durch Kellerautomaten: LL – Technik (top-down) LR – Technik (bottom-up) Keller Resteingabe Keller Resteingabe Z +-a+aa ε +-a+aa ZZ+ +-a+aa + -a+aa ZZ -a+aa +a+aa ZZ-a+aa +-a +aa ZZ a+aa +-Z +aa .. .. .. .. . . . . ε ε Z ε Jede LL(k) – Grammatik ist auch eine LR(k) – Grammatik, d.h. u.a. jede LL(k) – Grammatik ist eindeutig. 1.4.7 Rekursiver Abstieg Der rekursive Abstieg ist ein klassisches Verfahren der Programmierung eines TopDown – Kellerautomaten zur Zerteilung von Wörtern einer kontextfreien Sprache, orientiert an BNF: • Für jedes Nichtterminal xi ∈ N der linken Seiten der Regeln xi ::= E1 | . . . |En wird eine Prozedur implementiert, die (rekursiv) eine vollständige Falluntersuchung der rechten Seite der zu xi gehörenden Regel durchführt. In der Regel erfolgt ebenfalls Steuerung über das Präfix des Restworts. 1.4. KONTEXTFREIE SPRACHEN UND KELLERAUTOMATEN 29 N0 Ni Nj u v w x y Abbildung 1.11: Zerlegung von z 1.4.8 Das Pumping – Lemma für kontextfreie Sprachen Eine kontextfreie Grammatik G = (T, N, →, Z) ist in CNF, falls alle Regeln folgende Gestalt haben: a → A oder BC → A für a ∈ T und A, B, C ∈ N . Satz: Zu jeder kontextfreien Grammatik G mit ε ∈ / L(G) gibt es eine äquivalente kontextfreie Grammatik in CNF. (ohne Beweis) Satz (Pumping – Lemma für kontextfreie Sprachen): Zu jeder kontextfreien Sprache L existiert ein n ∈ N, so dass sich alle z ∈ L mit |z| ≥ n in u, v, w, x, y ∈ T ∗ zerlegen lassen: z =u◦v◦w◦x◦y mit 1. |v ◦ x| ≥ 1 2. |v ◦ w ◦ x| ≤ n 3. ∀i ≥ 0 : u ◦ v i ◦ w ◦ xi ◦ y ∈ L Beweis: Sei G = (T, N, →, N0 ) eine CNF – Grammatik mit L(G) = L. Weiter seien k = |N |, n = 2k und z ∈ L mit |z| ≥ n. Der Ableitungsbaum T für z ist ein Binärbaum der Höhe h + 1 mit |z| ≥ n Blättern: • Knoten mit genau einem Sohn (a → A) oder genau zwei Söhnen (BC → A) • h ≥ ld |z| ≥ ld n = k Im Pfad p0 , . . . , ph in T von der Wurzel p0 bis zum Blatt ph ist die Markierung N0 , . . . , Nh der Knoten p0 , . . . , ph eine Folge von h + 1 ≥ k + 1 Nichtterminalen. Es gibt also i, j ∈ {0, . . . , h}, so dass Ni = Nj =: N , i < j und Ni+1 , . . . , Nh paarweise verschieden sind (vgl. Abb. 1.11). 1. Da G in CNF ist, ist v 6= ε oder x 6= ε, d.h. |v ◦ x| ≥ 1. ∗ 2. Der Teilbaum T 0 ab Knoten pi ist Ableitungsbaum für Ni v ◦ w ◦ x. Da Ni+1 , . . . , Nh paarweise verschieden sind, folgt: T 0 hat die Höhe h − i ≤ k, d.h. |v ◦ w ◦ x| ≤ 2h−i ≤ 2k = n 3. Die Ableitungen für u ◦ v i ◦ w ◦ xi ◦ y ergeben sich aus den Kombinationen der Möglichkeiten: ∗ N0 u ◦ N ◦ y, ∗ N0 w, ∗ N v◦N ◦x 30 1.5 KAPITEL 1. FORMALE SPRACHEN Kontextsensitive Grammatiken Kontextsensitive Grammatiken sind definitionsgemäß wortlängenmonoton, d.h. für ∗ jeden Schritt x y gilt |x| ≥ |y|. Kontextsensitive Grammatiken sind sehr allgemein; jede wortlängenmonotone Grammatik ist strukturäquivalent zu einer kontextsensitiven Grammatik. Es ist möglich, für eine kontextsensitive Sprache L einen (ineffizienten) Algorithmus anzugeben, der für Wörter w ∈ T ∗ entscheidet, ob w ∈ L ist. Begründung: Bei einer wortlängenmonotonen Grammatik ist für jedes w ∈ T ∗ die Menge der durch Reduktionen von w entstehenden Wörter endlich; mögliche unendliche Reduktionen können aufgrund der Wortlängenmonotonheit erkannt werden. Für eine Chomsky – 0 – Grammatik G0 existiert im Allgemeinen kein solcher Algorithmus. Für G0 existiert lediglich ein Algorithmus, der für jedes w ∈ L(G0 ) mit Resultat true terminiert. Für w ∈ / L(G0 ) kann die Terminierung nicht garantiert werden. Kapitel 2 Berechenbarkeit Es gibt mathematisch exakt beschreibbare Probleme, für die es keine Lösungsalgorithmen gibt. Typisches Problem: Aufgabe: Berechnung einer gegebenen Funktion f Lösung: Algorithmus, der f berechnet. Definition: Berechenbarkeit Eine Funktion f heißt berechenbar, wenn es einen Algorithmus gibt, der für jedes Argument x (Eingabe) den Wert f (x) (Ausgabe) berechnet. Nicht berechenbare Funktionen können nicht durch Programme in einer Rechenanlage beschrieben werden. Wir betrachten o.B.d.A. n-stellige Funktionen f : Nn → N, wobei lediglich Repräsentationen von N zur Verfügung stehen (siehe Teil I). Wir verwenden die Zeichenmenge T und die injektive, umkehrbare Abbildung rep : N → T ∗ Demnach existiert auch abs : {t ∈ T ∗ : ∃n ∈ N : rep(n) = t} → N so dass abs ◦ rep = id = rep ◦ abs gilt. Statt abstrakter Funktionen f : Nn → N betrachten wir ein konkretes f : (T ∗ )n → T ∗ mit f (x1 , . . . , xn ) = abs f (rep(x1 ), . . . , rep(xn )) Konkrete Algorithmen arbeiten auf Repräsentationen (von N). Für einen realistischen Berechenbarkeitsbegriff müssen diese Repräsentationen handhabbar sein. Wir setzen deshalb u.a. voraus, dass T endlich ist. 2.1 Hypothetische Maschinen Wir suchen eine Möglichkeit zur Präzisierung des Begriffs Algorithmus: hypothetische Maschinen. Dies sind mathematische Nachbildungen des Zustandsraums und der Übergangsfunktion realer Maschinen mit dem Ziel der Präzisierung und der Vereinfachung. Endliche Automaten und Kellerautomaten genügen nicht; u.a. können Kellerautomaten für kontextfreie Sprachen, aber nicht für kontextsensitive Sprachen L berechnen, ob w ∈ L ist. Wir betrachten allgemeine Modelle als Kellerautomaten mit unbeschränkten Mengen von Zuständen. Mögliche Kritik: unrealistisch weil technisch nicht realisierbar etc. 31 32 KAPITEL 2. BERECHENBARKEIT 2.1.1 Turing – Maschinen (nach A. M. Turing, 1936) Eine Turing – Maschine besteht aus: • einem unendlichen Band von Zellen zur Speicherung von Zeichen, • einem Schreib- / Lesekopf und • einer Steuereinheit Ein endlicher Abschnitt des Bandes trägt relevante Information, alle anderen Zellen enthalten das Symbol # für die leere Information (o.B.d.A. sei # ∈ / T ). Prinzip: Die Turing – Maschine liest in jedem Schritt ein Zeichen t ∈ T ∪ {#} unter dem Kopf. Abhängig vom Zustand wird das Zeichen überschrieben, ein neuer Zustand eingenommen und der Kopf um höchstens eine Position nach links oder rechts bewegt. Eine Turing – Maschine T M = (T, S, δ, s0 ) umfasst: • eine endliche Menge T von Zeichen, mit denen das Band beschrieben wird (# ∈ / T) • eine endliche Menge S von Zuständen • eine endliche Übergangsrelation δ : S × (T ∪ {#}) → 2S×(T ∪{#})×{,,↓} und bewirken dabei eine Verschiebung des Kopfes um eine Zelle nach links bzw. rechts, ↓ das Beibehalten der Position. Gilt für eine Turing – Maschine ∀t ∈ T ∪ {#}, s ∈ S : |δ(s, t)| ≤ 1, so heißt sie deterministisch. Beispiel: Prüfung, ob die Anzahl der L in einem Wort hw1 . . . wn i mit n ≥ 1 und wi ∈ {0, L} gerade ist. Zu Beginn sei . . . #w1 . . . wn # . . . auf dem Band und der Kopf auf wn positioniert. Die Turing – Maschine hält mit Bandinhalt . . . #L# . . . an, falls die Anzahl der L gerade war und mit . . . #0# . . . sonst. T M = ({0, L}, {s0 , s1 , s2 , s3 }, δ, s0 ) Übergangsfunktion: δ 0 L # s0 (s0 , 0, ) (s0 , L, ) (s1 , #, ) s1 (s1 , #, ) (s2 , #, ) (s3 , L, ↓) s2 (s2 , #, ) (s1 , #, ) (s3 , 0, ↓) s3 ∅ ∅ ∅ Das Verhalten einer Turing – Maschine kann auch graphisch durch einen Automaten dargestellt werden — s. Abb. (2.1). Die Markierung 0 # für einen Zustandsübergang von s nach s0 bedeutet, dass dieser Übergang im Zustand s ausgeführt werden kann, falls 0 unter dem Lesekopf steht; dann wird # geschrieben und der Lesekopf bewegt sich nach links. Eine Konfiguration der Turing – Maschine beschreibt den Berechnungszustand. Wir verwenden ein 4 – Tupel (s, l, a, r) ∈ S × (T ∪ {#})∗ × (T ∪ {#}) × (T ∪ {#})∗ Da das Band nach links und nach rechts unendlich fortgesetzt ist, sind folgende Konfigurationen äquivalent: • (s, l, a, r) 2.1. HYPOTHETISCHE MASCHINEN 33 # # << s0 0 0 >> L L >> 0 # << s1 #Lv L # << L # << s3 s2 #0v 0 # << Abbildung 2.1: Graphische Darstellung einer Turing – Maschine • (s, h#i ◦ l, a, r) • (s, l, a, r ◦ h#i) Die Berechnung einer Turing – Maschine (ausgehend von einer Konfiguration, d.h. einem Anfangszustand, einer (endlichen) Anfangsbandbelegung und Stellung des Lesekopfs) besteht aus einer endlichen oder unendlichen Folge von Konfigurationen K0 → K1 → K2 → · · · → . . . Wir definieren eine Relation auf Konfigurationen (s, l, a, r) → (s0 , l0 , a0 , r0 ) wie folgt: Es gilt genau eine der folgenden Aussagen: 1. z =↓ ∧l = l0 ∧ r = r0 ∧ a0 = x (Kopf bleibt stehen) 2. z = ∧l = l0 ◦ ha0 i ∧ r0 = hxi ◦ r (Kopf nach links) 3. z = ∧l0 = l ◦ hxi ∧ r = ha0 i ◦ r0 (Kopf nach rechts) wobei (s0 , x, z) ∈ δ(s, a). Eine Konfiguration k heißt terminal, wenn keine Nachfolgekonfiguration existiert. Die Turing – Maschine bleibt stehen, die Berechnung endet. Eine Berechnung heißt vollständig, wenn sie unendlich ist oder endlich ist und die letzte Konfiguration terminal ist. Bemerkung: Jede Turing – Maschine lässt sich in einer der gebräuchlichen Programmiersprachen (C, Java, . . . ) simulieren. Wir stützen nun auf Turing – Maschinen den Begriff der Berechenbarkeit ab (genauer: Turing – Berechenbarkeit). Eine partielle Funktion f : T ∗ → T ∗ heißt Turing – berechenbar, wenn es eine deterministische Turing – Maschine gibt, so dass für jedes Wort t ∈ T ∗ folgende Aussagen gelten: 1. f (t) hat einen Bildpunkt (f (t) ist definiert; genauer: der Wert von f für das Argument t ist definiert) und es existiert eine vollständige Berechnung (s0 , t, #, ε) → · · · → (se , r, #, ε) wobei f (t) = r ist. 2. f ist für das Argument t nicht definiert und es existiert eine unendliche Berechnung (s0 , t, #, ε) → · · · → . . . 34 KAPITEL 2. BERECHENBARKEIT Dieser Berechenbarkeitsbegriff lässt sich auf partielle Abbildungen f : Nn → N übertragen. Dazu müssen wir nun eine Zahldarstellung festlegen. Wir stellen natürliche Zahlen durch Strichzahlen dar und verwenden t als Trennzeichen. f heißt berechenbar, wenn eine Funktion g : {|, t}∗ → {|, t}∗ existiert, die berechenbar ist und ∀x1 , . . . , xn ∈ N gilt: f (hti ◦ |x1 ◦ hti ◦ · · · ◦ hti ◦ |xn ◦ hti) = hti ◦ |y ◦ hti genau dann, wenn f (x1 , . . . , xn ) = y ist. Beispiel: einige Turing – berechenbare Funktionen 1. Konstante: c : Nn → N, c(x1 , . . . , xn ) = k für gegebenes k ∈ N 2. Nachfolgefunktion: succ : N → N mit succ(n) = n + 1 3. Projektionen: πin : Nn → N mit πin (x1 , . . . , xn ) = xi 4. Vorgänger: ( x−1 x>0 pre : N → N total: pre(x) = 0 x=0 ( x−1 x>0 pred : N → N partiell: pred(x) = undef x = 0 Komplexere Funktionen können wir durch Komposition (Funktionskomposition) gewinnen. Wir definieren eine verallgemeinerte Komposition für partielle Funktionen: Gegeben seien g : Nn → N hi : N m → N 1≤i≤n Wir definieren f : Nm → N durch die Gleichung f (x1 , . . . , xm ) = g (h1 (x1 , . . . , xm ) , . . . , hn (x1 , . . . , xm )) falls h1 , . . . , hn für x1 , . . . , xm und f für h1 (x1 , . . . , xm ), . . . , hn (x1 , . . . , xm ) definiert ist; sonst ist f für x1 , . . . , xm undefiniert. Satz: f ist Turing – berechenbar, wenn g, h1 , . . . , hn Turing – berechenbar sind. 2.1. HYPOTHETISCHE MASCHINEN 35 Notation: Wir schreiben dann für f auch g ◦ [h1 , . . . , hn ] Es gilt: Addition, Multiplikation, Division (d.h. die übliche Arithmetik) ist Turing – berechenbar. Frage: Gibt es Funktionen, die nicht Turing – berechenbar sind? Ja — siehe später. Bemerkungen: 1. Ob wir Turing – Maschinen mit mehreren Bändern, nur einseitig unendliche Turing – Maschinen verwenden, ändert nichts am Begriff der Turing – Berechenbarkeit. 2. Andere Berechenbarkeitsbegriffe haben sich als äquivalent erwiesen. Dazu folgen zwei Beispiele. 2.1.2 Registermaschinen Eine Registermaschine ist (wie eine Turing – Maschine) eine hypothetische Maschine (d.h. mathematisch definiert), die unseren gebräuchlichen Rechnern entspricht. Eine Registermaschine mit n Registern (n – Registermaschine) besitzt n Register (Speicherplätze für natürliche Zahlen) und ein Programm (ein n – Registermaschinen – Programm). Diese Programme haben eine extrem einfache Syntax: 1. ε ist ein n – Registermaschinen – Programm (leeres Programm). 2. succi mit 1 ≤ i ≤ n ist ein n – Registermaschinen – Programm. 3. predi mit 1 ≤ i ≤ n ist ein n – Registermaschinen – Programm. 4. Sind M1 und M2 n – Registermaschinen – Programme, so ist M1 ; M2 ein n – Registermaschinen – Programm. 5. Ist M ein n – Registermaschinen – Programm, so ist whilei (M ) mit 1 ≤ i ≤ n ein n – Registermaschinen – Programm. Semantik von Registermaschinen durch Zustandsübergangsbeschreibung Konfigurationen einer n – Registermaschine: (s, p) ∈ Nn × n-PROG Dabei bezeichnet n-PROG die Menge aller Programme für n – Registermaschinen. Zustands- bzw. Konfigurationsübergangsfunktion (für n – Registermaschinen mit i ∈ N, 1 ≤ i ≤ n): (s, succi ) → ((s1 , . . . , si−1 , si + 1, si+1 , . . . , sn ), ε) (s, predi ) → ((s1 , . . . , si−1 , k, si+1 , . . . , sn ), ε) mit ( si − 1 k= 0 falls si > 0 sonst (s, (p1 ; p2 )) → (s0 , (p01 ; p2 )) falls (s, p1 ) → (s0 , p01 ) (s, (ε, p2 )) → (s, p2 ) ( (s, (p; whilei (p))) falls si > 0 (s, whilei (p)) → (s, ε) sonst 36 KAPITEL 2. BERECHENBARKEIT Bemerkung: Die Konfigurationen der Form (s, ε) sind terminal, d.h. es existiert keine Nachfolgekonfiguration (das Programm terminiert). Wie gehabt definieren wir Berechnungen (endliche / unendliche) als Folgen von Konfigurationen in der → – Relation. Beispiel: Registermaschinen – Programme 1. whilei (predi ) entspricht while si > 0 do si := si - 1 od (si wird auf 0 gesetzt). si := k entspricht whilei (predi ); succi ; . . . ; succi (mit k Aufrufen von succi ). 2. Folgende Funktionen können in Registermaschinen programmiert werden: • übliche Arithmetik • übliche boolesche Algebra Eine Funktion f : Nn → Nn heißt Registermaschinen – berechenbar, wenn es ein n – Registermaschinenprogramm p gibt, so dass gilt: falls f (s1 , . . . , sn ) = (s01 , . . . , s0n ) dann gilt ∗ (s, p) → (s0 , ε) (mit s = (s1 , . . . , sn ) und s0 = (s01 , . . . , s0n )). Auch g : Nk → N mit 1 ≤ k ≤ n und (für ein i ∈ N, 1 ≤ i ≤ n) g(x1 , . . . , xk ) = πin (f (x1 , . . . , xk , xk+1 , . . . , xn )) für gegebene xk+1 , . . . , xn heißt Registermaschinen – berechenbar. 2.2 Rekursive Funktionen Die beiden bisher betrachteten Berechnungsmodelle (Turing – Maschinen, Registermaschinen) sind hypothetische Maschinen. Jetzt betrachten wir ein stärker durch Beschreibung charakterisiertes Berechnungsmodell: rekursiv definierte Funktionen. Auch dafür können wir Algorithmen zur Auswertung angeben (vgl. Termersetzung). 2.2.1 Primitiv rekursive Funktionen Wir betrachten n – stellige (totale oder partielle) Funktionen f : Nn → N. Wir nennen folgende Funktionen Grundfunktionen: succ : N → N zero (0) mit succ(n) = n + 1 :→ N (1) zero : N → N πin : Nn → N i-te Projektion, 1 ≤ i ≤ n 2.2. REKURSIVE FUNKTIONEN 37 Aus einer gegebenen Menge von Funktionen gewinnen wir weitere durch Komposition g ◦ [k1 , . . . , kn ] oder durch Anwendung des Schemas der primitiven Rekursion: Gegeben seien Funktionen g : Nk → N h : Nk+2 → N Wir definieren f : Nk+1 → N wie folgt: ∀x1 , . . . , xk , n ∈ N: f (x1 , . . . , xk , 0) = g(x1 , . . . , xk ) f (x1 , . . . , xk , n + 1) = h(x1 , . . . , xk , n, f (x1 , . . . , xk , n)) Beispiel: add(x, 0) = x add(x, n + 1) = add(x, n) + 1 = h(x, n, add(x, n)) = succ(π33 (x, n, add(x, n))) Dies entspricht einer induktiven Definition von f . Fakt: Sind g, h totale Funktionen, so ist durch das Schema f eindeutig bestimmt und total. Beweis: Induktion Bemerkung: Sind g, h partielle Funktionen, so ist f auch eindeutig festgelegt, aber unter Umständen partiell. Die Menge der primitiv rekursiven Funktionen (P R) ist induktiv wie folgt definiert: 1. Die Grundfunktionen sind in P R. 2. Funktionen, die durch Komposition von Funktionen aus P R gewonnen werden können, sind in P R. 3. Funktionen, die durch das Schema der primitiven Rekursion über Funktionen g, h ∈ P R gewonnen werden können, sind in P R. Wir können — wie für die Komposition — auch für das Schema der primitiven Rekursion eine kompakte Notation einführen: pr : (Nk → N) × (Nk+2 → N) → (Nk+1 → N) mit f = pr(g, h) Es gilt: 1. Alle arithmetischen Funktionen (totale Division, totale Subtraktion) sind in P R. 2. Alle Funktionen in P R sind total. 3. Alle Funktionen in P R sind Turing – berechenbar. 4. Alle Funktionen in P R sind Registermaschinen – berechenbar. 38 KAPITEL 2. BERECHENBARKEIT 5. Da alle Funktionen in P R total sind, existieren trivialerweise Funktionen, die Turing – berechenbar bzw. Registermaschinen – berechenbar, aber nicht in P R sind. Wir zeigen nun, dass es eine totale Funktion gibt, die offensichtlich Turing – und Registermaschinen – berechenbar ist, aber nicht in P R: ack : N2 → N n=0 m + 1 ack(n, m) = ack(n − 1, 1) n > 0, m = 0 ack(n − 1, ack(n, m − 1)) sonst Feststellungen: 1. Durch diese Gleichung ist ack eindeutig bestimmt und total. 2. Wir definieren eine Schar von Funktionen Bn : N → N durch Bn (m) := ack(n, m). Wir erhalten: B0 (m) = m + 1 B1 (m) = m + 2 B2 (m) = 2m + 3 B3 (m) = 2m+2 − 3 .. . Alle Funktionen Bn sind primitiv rekursiv. Bn+1 kann durch das Schema der primitiven Rekursion aus Bn definiert werden. Satz: ack ∈ / PR Beweis (Skizze): Durch Induktion über die Anzahl der Anwendungen des Schemas der primitiven Rekursion können wir zeigen: Zu jeder Funktion g ∈ P R, g : Nn → N existiert eine Konstante c ∈ N, so dass gilt ! n X g(x1 , . . . , xn ) < ack c, xi i=1 für alle x1 , . . . , xn ∈ N. Anmerkung: Angenommen, ack ∈ P R. Dann wäre h : N → N mit h(n) = ack(n, n) auch in P R. Es würde eine Zahl c ∈ N geben mit ∀n ∈ N : ack(n, n) = h(n) < ack(c, n) Mit n = c erhalten wir den Widerspruch ack(c, c) = h(c) < ack(c, c) In anderen Worten: ack wächst schneller als alle Funktionen in P R. Fazit: Es existieren totale Funktionen, die Turing – berechenbar, Registermaschinen – berechenbar, aber nicht in P R sind. ack ist offensichtlich durch Rekursion definierbar, also ist der Begriff der primitiven Rekursion zu eng. 2.2.2 µ – rekursive Funktionen Jetzt betrachten wir auch partielle Funktionen, verwenden alle Funktionen aus P R (und alle Konstruktionsprinzipien) und zusätzlich eine weitere Konstruktion, die der µ – Rekursion. 2.2. REKURSIVE FUNKTIONEN Beispiel: 39 rekursive Definition ulam : N → N ( 1 n≤1 ulam(n) = ulam(g(n)) n > 1 ( falls n gerade b n2 c g(n) = 3n + 1 sonst Feststellung: g ist primitiv rekursiv. Frage: Terminiert ulam für alle Argumente n ∈ N? Antwort: unbekannt, aber Vermutung: Antwort ist ja. Falls ulam immer terminiert, gilt ulam(n) = 1; ulam ist dann primitiv rekursiv. Das Schema der µ – Rekursion: Gegeben sei eine partielle Funktion f : Nk+1 → N Wir definieren µ(f ) : Nk → N durch µ(f )(x1 , . . . , xk ) = min {y ∈ N : f (x1 , . . . , xk , y) = 0} falls ∃y ∈ N : f (x1 , . . . , xk , y) = 0∧∀z ∈ N : 0 ≤ z ≤ y ⇒ f ist definiert für (x1 , . . . , xk , z) Gilt diese Bedingung nicht, so definieren wir µ(f ) für (x1 , . . . , xk ) als nicht definiert. Verfahren zur Berechnung von y = µ(f )(x1 , . . . , xk ): (1) Berechne y0 = f (x1 , . . . , xk , 0). (a) y0 = 0 ⇒ y = 0 (b) y0 > 0: gehe zu (2) (c) Berechnung terminiert nicht: µ(f ) ist nicht definiert für (x1 , . . . , xk ). (2) analog für f (x1 , . . . , xk , 1) ... Achtung: Auch wenn f total ist, kann µ(f ) partiell sein. Die Menge der µ – rekursiven Funktionen M R definieren wir induktiv wie folgt: 1. Alle primitiv rekursiven Funktionen sind in M R. 2. Funktionen, die durch Komposition oder das Schema der primitiven Rekursion aus Funktionen in M R gebildet werden können, sind in M R. 3. Falls f ∈ M R, dann ist µ(f ) ∈ M R. Beispiel: µ – rekursive Definition der partiellen Subtraktion: ( . a−b a≥b a−b= undef sonst Wir definieren . a − b = µ(h0 )(a, b) 40 KAPITEL 2. BERECHENBARKEIT mit geeignetem h0 : N3 → N. Wir wählen h0 (a, b, y) = sub(b + y, a) + sub(a, b + y) wobei ( a−b a≥b sub(a, b) = 0 sonst sei. Fälle a≥b sub(b + y, a) =0 a≥b sub(a, b + y) =0 >0 b>a 2.2.3 + Fälle y =a−b≥0 y <a−b y+b<a >0 Allgemeine Bemerkungen zur Rekursion In der Informatik existieren viele Spielarten von Rekursion zur Definition von Funktionen, Prozeduren, Methoden, Datentypen, Mengen, formalen Sprachen usw. In der rekursiven Definition einer Funktion verwenden wir Gleichungen der Form f (x) = E, wobei f in E auftritt, oder f (D) = E mit eingeschränktem Ausdruck D. Beispiel (strukturelle Rekursion, induktive Definition) ack(0, 0) = 1 ack(0, m + 1) = ack(0, m) + 1 ack(n + 1, 0) = ack(n, 1) ack(n + 1, m + 1) = ack(n, ack(n + 1, m)) 2.3 Äquivalenz der Berechenbarkeitsbegriffe Wir haben vier Begriffe der Berechenbarkeit eingeführt: • Turing – Maschinen • Registermaschinen • primitive Rekursion • µ – Rekursion Primitive Rekursion ist schwächer als Turing – Maschinen, Registermaschinen, µ – Rekursion. Wir zeigen nun die Äquivalenz von Turing – Maschinen, Registermaschinen und µ – Rekursion. 2.3.1 Äquivalenz von µ – Berechenbarkeit und Turing – Berechenbarkeit Der Logiker Kurt Gödel hat eine Idee zur Darstellung von Zeichenfolgen durch Zahlen entwickelt → Gödelisierung (1933). Eine Gödelisierung ist eine Abbildung f : A∗ → N wobei A eine endliche Menge von Zeichen sei, mit folgenden Eigenschaften: 2.3. ÄQUIVALENZ DER BERECHENBARKEITSBEGRIFFE 41 i. f ist injektiv. ii. f ist berechenbar (Turing – berechenbar). iii. Es ist berechenbar, ob eine Zahl n ∈ N im Bildbereich von f liegt (d.h. ∃w ∈ A∗ : f (w) = n). iv. Es existiert ein Algorithmus, der zu jeder Zahl im Bildbereich das zugehörige Wort berechnet (f ist algorithmisch umkehrbar). ∼ Satz: T M = M R Beweis: 1. f ∈ M R ⇒ f ∈ T M Beweisidee: Für jede Grundfunktion in M R (bzw. P R) können wir eine Turing – Maschine angeben. Das Schema der Komposition, der primitiven Rekursion und der µ – Rekursion können wir durch Turing – Maschinen nachbauen. 2. f ∈ T M ⇒ f ∈ M R Konfigurationen von Turing – Maschinen entsprechen Wörtern aus A∗ (mit geeignetem A). Wir verwenden eine Gödelisierung rep : A∗ → N (einfach zu konstruieren). Es zeigt sich, dass die Funktion g:N→N mit k0 → k1 ⇔ g(rep(k0 )) = rep(k1 ) primitiv rekursiv ist (d.h. die Nachfolgefunktion auf Konfigurationen entspricht einer primitiv rekursiven Funktion auf der Darstellung der Konfigurationen durch natürliche Zahlen). Ferner definieren wir eine primitiv rekursive Funktion h : N2 → N durch ( 0 h(n, m) = 1 falls g m (n) eine terminalen Konfiguration entspricht sonst Die Funktion it : N2 → N sei spezifiziert durch it(n, 0) = n it(n, m + 1) = it(g(n), m) it beschreibt die Ausführung von m Schritten der Turing – Maschine. Die Funktion tm : N → N sei definiert durch tm(n) = it(n, µ(h)(n)) wobei µ(h)(n) die Anzahl der Schritte der Turing – Maschine bis zur Terminierung für die Eingangskonfiguration, dargestellt durch n, angibt, falls die Turing – Maschine terminiert. 42 KAPITEL 2. BERECHENBARKEIT 2.3.2 Äquivalenz von Registermaschinen- und Turing – Berechenbarkeit Jede Registermaschine lässt sich in eine Turing – Maschine übersetzen. Dazu brauchen wir eine Darstellung der Konfigurationen der Registermaschine durch eine Turing – Maschine: 1. Die Registerinhalte werden auf dem Band der Turing – Maschine dargestellt (z.B. als Strichzahlen). 2. Aus Registermaschinen – Programmen konstruieren wir den endlichen Automaten, der die Turing – Maschine steuert. Zu einer gegebenen Turing – Maschine kann eine Registermaschine konstruiert werden, die die Turing – Maschine simuliert. Idee: Konfigurationen der Turing – Maschine werden gödelisiert und in den Registern dargestellt. Die Fahrbewegungen und Zustandsübergänge der Turing – Maschine werden durch Programmschritte der Registermaschine simuliert. 2.3.3 Churchs These (Alazo Church, 1936): Jede intuitiv berechenbare Funktion ist µ – rekursiv. 2.4 Entscheidbarkeit Wir haben eine Reihe von Berechenbarkeitsbegriffen kennengelernt, die jeweils auf das gleiche Konzept von berechenbarer Funktion führen (Churchsche These). Wir wenden uns nun der Frage zu, welche Funktionen berechenbar / nicht berechenbar und welche Prädikate entscheidbar (d.h. durch Algorithmen eindeutig mit ja oder nein beantwortbar) sind. 2.4.1 Nicht berechenbare Funktionen Jeder Formalismus zur Berechenbarkeit (Turing – Maschinen, Registermaschinen, µ – Rekursion) definiert Programme / Algorithmen durch Wörter über einem Zeichensatz, d.h. in jedem Fall existiert eine formale Sprache P RG ⊆ A∗ mit geeignetem Zeichensatz A, so dass gilt: Jeder Algorithmus wird dargestellt durch ein Wort p ∈ P RG. Für jede berechenbare Funktion f existiert ein p ∈ P RG, das f berechnet. Die Menge der Funktionen Nn → N ist überabzählbar (Ergebnis der Mengenlehre). N ist abzählbar. A∗ ist abzählbar, falls A endlich ist. P RG ist ebenfalls abzählbar. Dies ergibt sofort: Es gibt viel mehr Funktionen Nn → N als Programme in P RG, d.h. fast jede Funktion ist nicht berechenbar. Wir können Programme durch Zahlen codieren, d.h. es gibt eine Gödelisierung code : P RG → N Satz: Es existiert eine totale Funktion g : N → N, die nicht berechenbar ist. Beweis: Wir spezifizieren g durch (∀n ∈ N): ( fp (n) + 1 falls ∃p ∈ P RG : code(p) = n und fp definiert für Argument n g(n) = 0 sonst 2.4. ENTSCHEIDBARKEIT 43 Dabei sei für p ∈ P RG die Funktion fp : N → N die durch p berechnete partielle Funktion. Achtung: Die Spezifikation von g ist konsistent und eindeutig, da code eine injektive Funktion ist. Annahme: g ist berechenbar. Dann existiert ein Programm q ∈ P RG mit fq = g. Mit code(q) = m gilt g(m) = fq (m) + 1 = g(m) + 1 Widerspruch. Bemerkung: g ist zunächst nur von theoretischem Interesse. 2.4.2 (Nicht) entscheidbare Prädikate Eine Ja – Nein – Frage entspricht logisch einem Prädikat. Ein Prädikat p:M →B kann auch als Funktion p0 : M → {0, 1} ⊂ N aufgefasst werden. Damit können wir das Konzept der Berechenbarkeit auf Prädikate übertragen. Ein berechenbares totales Prädikat heißt auch entscheidbar. Ein Prädikat p : M → B heißt: entscheidbar: wenn es einen Algorithmus gibt, der für jedes Argument m ∈ M terminiert und korrekt 1 für p(m) = true und 0 für p(m) = false ausgibt. positiv semientscheidbar: wenn es einen Algorithmus gibt, der für jedes Argument m ∈ M terminiert und korrekt 1 ausgibt, falls p(m) = true gilt und falls p(m) = false gilt entweder 0 ausgibt oder nicht terminiert. negativ semientscheidbar: falls ¬p positiv semientscheidbar ist. Beispiele 1. entscheidbare Funktionen • Gleichheit zweier Zahlen in Binärdarstellung • Primzahleigenschaft • Zugehörigkeit eines Wortes zum Sprachschatz einer Chomsky – 3 – Grammatik • Existenz der Nullstellen eines Polynoms über den ganzen Zahlen 2. positiv semientscheidbare (aber nicht allgemein entscheidbare) Funktionen • Terminierung einer Turing – Maschine für eine bestimmte Eingabe (Halteproblem) • Zugehörigkeit eines Wortes zum Sprachschatz einer Chomsky – 0 – Sprache 3. negativ semientscheidbar • Gleichheit zweier durch primitive Rekursion gegebener Funktionen 4. unentscheidbare Prädikate (weder positiv noch negativ semientscheidbar) • Terminierung einer durch µ – Rekursion beschriebenen Funktion für alle Eingaben 44 KAPITEL 2. BERECHENBARKEIT Satz: Das Terminierungsproblem für µ – rekursive Funktionen ist nicht entscheidbar. Beweis: Sei µ−P RG die Menge der Wörter, die µ – rekursive Programme darstellen. Widerspruchsannahme: Das Terminierungsproblem ist entscheidbar. Dann existiert ein µ – rekursives Programm p ∈ µ − P RG, das eine Funktion fp berechnet, so dass gilt ( 0 falls ∃q ∈ µ − P RG : code(q) = x ∧ fq (x) für x nicht definiert fp (x) = undef sonst Wir erhalten für m = code(p): ( 0 fp (m) = undef falls fp (code(p)) = undef sonst Widerspruch. 2.4.3 Rekursion und rekursiv aufzählbare Mengen Wir betrachten jetzt eine Obermenge U sowie Teilmengen M ⊆ U . Ein Prädikat auf U p:U →B (charakteristisches Prädikat zu M ⊆ U ) ist dann gegeben durch ( true p(x) = false falls x ∈ M falls x ∈ /M Eine Menge heißt rekursiv, wenn das charakteristische Prädikat entscheidbar ist. Satz: Jede Chomksy – 1 – Sprache ist rekursiv. Eine totale Abbildung e:N→U heißt Aufzählung der Menge M , falls M = {e(n) : n ∈ N} = [ {e(n)} n∈N M heißt rekursiv aufzählbar, falls e berechenbar ist. Jede rekursiv aufzählbare Menge hat ein passendes charakteristisches Prädikat. Bemerkung: In einer Aufzählung einer Menge M e(0), e(1), e(2), . . . können Elemente beliebig oft vorkommen, aber jedes Element muss mindestens einmal vorkommen. Beispiele: 1. Die Menge der primitiv rekursiven Funktionen ist rekursiv aufzählbar, aber nicht rekursiv. 2. Die Menge der Argumente, für die eine µ – rekursive Funktion (Turing – Maschine, Registermaschine) terminiert, ist rekursiv aufzählbar, aber nicht rekursiv. 2.4. ENTSCHEIDBARKEIT 45 Satz: Jede Chomsky – Sprache ist rekursiv aufzählbar. Beweis: Die Grammatik der Sprache definiert einen Baum von Ableitungen, aus dem wir ein Aufzählungsverfahren gewinnen. Satz: Die Menge der Argumente einer berechenbaren Funktion f , für die f nicht definiert ist (der Algorithmus nicht terminiert) ist i.a. nicht rekursiv aufzählbar. Beweis: Folgt direkt aus dem Halteproblem. Satz: Eine Menge S ⊆ T ∗ ist genau dann rekursiv, wenn S und T ∗ \ S rekursiv aufzählbar sind. Beweis: Wir zählen S und T ∗ \ S parallel auf. Irgendwann tritt jedes Element in einer der Aufzählungen auf. Dann liegt fest, ob es in S oder T ∗ \ S liegt. Dieses Verfahren terminiert für jedes Element aus T ∗ . 46 KAPITEL 2. BERECHENBARKEIT Kapitel 3 Komplexitätstheorie Jeder Algorithmus verbraucht bei der Ausführung gewisse Betriebsmittel, erfordert einen Berechnungsaufwand. Fragen: i. Bei einem gegebenen Algorithmusbegriff (Turing – Maschine, Registermaschine, µ – Rekursion) können wir nun für ein gegebenes Problem (z.B. Multiplikation) fragen, welcher Algorithmus den geringsten Berechnungsaufwand hat. ii. Wie hängt die Klassifizierung des Berechnungsaufwands von der Wahl des Algorithmenbegriffs ab? Wir werden die Aufwandsschätzung für Algorithmen und für Probleme an Turing – Maschinen orientieren. 3.1 Komplexitätsmaße Wir wählen zwei einfache Maßzahlen für den Berechnungsaufwand eines Algorithmus: • Anzahl der Schritte in der Berechnung (→ Zeitkomplexität) • Anzahl der benötigten (Hilfs –)Speicherzellen (→ Bandkomplexität) 3.1.1 Zeitkomplexität Wir betrachten k – Band – Turing – Maschinen. Für jedes Band existiert ein Lese- / Schreibkopf. Für einen gegebenen Eingabewert (etwa auf Band 1) führt die Turing – Maschine eine endliche oder unendliche Anzahl von Schritten aus. Terminiert die Turing – Maschine für ein Wort w, so bezeichne b(w) die Anzahl der Schritte. Gilt für eine Funktion T : N → N für alle Worter w ∈ Z ∗ der Länge n = |w| stets b(w) ≤ T (n), so heißt die Turing – Maschine (der Algorithmus) T (n) – zeitbeschränkt. Achtung: Diese Definition schließt nichtdeterministische Turing – Maschinen mit ein. Dann müssen alle Berechnungen für w durch T (n) beschränkt sein. Mehrband – Turing – Maschinen vermeiden den Aufwand von Kopfbewegungen, um zwischen Argumenten hin- und herzufahren und liefern realistischere Abschätzungen. Die Definition von Zeitbeschränktheit lässt sich von Algorithmen auf Probleme übertragen: Ein Problem1 heißt T (n) – zeitbeschränkt, wenn es einen Algorithmus gibt, der das Problem berechnet und T (n) – zeitbeschränkt ist. 1 Ein Problem entspricht immer der Aufgabe, eine Funktion / Prozedur zu berechnen 47 48 KAPITEL 3. KOMPLEXITÄTSTHEORIE Beispiel: Das Problem, zu entscheiden, ob ein Wort in der Sprache L = {ak cbk : k ∈ N} ist, ist (n + 1) zeitbeschränkt. Dafür ist nur zu zeigen, dass ein Algorithmus (eine Turing – Maschine) existiert, der in n + 1 Schritten für ein Wort w der Länge n entscheidet, ob w ∈ L ist. Wir sagen, die Turing – Maschine ist linear beschränkt. Addition: Seien die Zahlen a, b ∈ N dargestellt durch Binärzahlen; eine Turing – Maschine für die Addition benötigt log2 (b + a) + 2 + 2 log2 (b) + 1 + 1 Schritte. Sprechweise: Das Problem ist log – zeitbeschränkt. Anmerkung: Wir betrachten nur Probleme mit T (n) ≥ n+1, d.h. wir betrachten nur Problemstellungen, wo die gesamte Eingabe für den Algorithmus relevant ist. 3.1.2 Bandkomplexität Bandkomplexität bewertet den Speicheraufwand eines Algorithmus. Wir messen die Bandkomplexität wieder durch Turing – Maschinen über die Anzahl der im Laufe einer Berechnung beschriebenen Speicherzellen. Wir betrachten Turing – Maschinen mit folgender Charakteristik: • ein Eingabeband, das nicht beschrieben wird und eine Endmarkierung enthält • k einseitig unendliche Bänder Für ein Eingabewort w ∈ Z ∗ verwendet die Turing – Maschine bi (w) ∈ N Speicherzellen auf ihrem i – ten Band (1 ≤ i ≤ k). Gilt für eine Abbildung S:N→N für alle Wörter w mit n = |w|, alle i ,1 ≤ i ≤ k bi (w) ≤ S(n) so heißt die Turing – Maschine S(n) – bandbeschränkt. Wir sagen: Die Turing – Maschine hat Bandkomplexität S(n). Beispiel: Sprache L = {an cbn : n ∈ N} Wenn wir die Zahlen in Binärschreibweise notieren, dann benötigen wir 1 + log2 (n) Speicherzellen, um die an Zeichen zu zählen. Wir erhalten einen logarithmisch bandbeschränkten Algorithmus. Definition: Ein Problem heißt S(n) – bandbeschränkt, wenn es einen Algorithmus (eine Turing – Maschine) gibt, der das Problem löst und S(n) – bandbeschränkt ist. Wenn wir von Bandkomplexität S(n) sprechen, meinen wir stets max{1, S(n)}. Wir sind bei Abschätzungen von Band- / Zeitkomplexität in der Regel nicht an exakten Zahlen interessiert, sondern an Größenordnungen. Dazu verwenden wir Abbildungen f, g : N → N um das assymptotische Verhalten abzuschätzen. Wir sagen: Die Funktion g wächst mit der Ordnung Θ(f (n)), wenn gilt: ∃c, d ∈ N \ {0}, n0 ∈ N : ∀n ∈ N : n ≥ n0 ⇒ f (n) ≤ dg(n) ≤ cf (n) 3.1. KOMPLEXITÄTSMASSE Beispiel: Zeit zur Lösung eines Problems der Größenordnung führung des Lösungsalgorithmus O(n) Mikrosekunden benötigt. n log2 n n n log2 n n2 10 0, 000003 sec 0, 00001 sec 0, 00003 sec 0, 0001 sec 102 0, 000007 sec 0, 0001 sec 0, 0007 sec 0, 01 sec 103 0, 000010 sec 0, 001 sec 0, 01 sec 1 sec 104 0, 000013 sec 0, 01 sec 0, 13 sec 1, 7 min 105 0, 000017 sec 0, 1 sec 1, 7 sec 2, 8 Std Diese Tabelle zeigt den Unterschied zwischen 49 n, wenn die Aus2n 0, 001 sec 1016 Jahre astronomisch astronomisch astronomisch • prinzipieller (theoretischer) Berechenbarkeit und • praktischer Berechenbarkeit. Auch die Bandkomplexität lässt sich auf nichtdeterministische Turing – Maschinen anwenden. Eine nichtdeterministische Turing – Maschine hat Bandkomplexität S(n), falls für jedes Wort w ∈ Z ∗ mit |w| = n alle Berechnungen der Turing – Maschine höchstens S(n) Speicherzellen pro Band verwenden. 3.1.3 Zeit- und Bandkomplexitätsklassen Eine Problemstellung (Aufgabe: berechne Funktionswert / Prädikat) heißt deterministisch T (n) – zeitbeschränkt (bzw. deterministsich T (n) – bandbeschränkt), wenn es eine deterministische Turing – Maschine gibt, die das Problem löst und T (n) – zeitbeschränkt (bzw. T (n) – bandbeschränkt) ist. Analog kann nichtdeterministische T (n) – Zeit- / Bandbeschränktheit definiert werden. Abkürzungen: • DTIME (T (n)): Klasse der Probleme, die deterministisch T (n) – zeitbeschränkt sind • NTIME (T (n)): Klasse der Probleme, die nichtdeterministisch T (n) – zeitbeschränkt sind • DSPACE (T (n)): Klasse der Probleme, die deterministisch T (n) – bandbeschränkt sind • NSPACE (T (n)): Klasse der Probleme, die nichtdeterministisch T (n) – bandbeschränkt sind Wichtig: Hier brauchen uns nur die Größenordnungen zu interessieren (assymptotisches Verhalten), wie folgende Sätze zeigen: Satz (Bandkompression): Ist ein Problem S(n) – bandbeschränkt, so ist für jede Konstante c ∈ R mit c > 0 das Problem auch cS(n) – bandbeschränkt. Beweis: Konstruiere eine Turing – Maschine, die r Zeichen in ein Zeichen zusammenfasst. Korollar: Für alle c ∈ R mit c > 0 gilt NSPACE (S(n)) = NSPACE (cS(n)) DSPACE (S(n)) = DSPACE (cS(n)) Satz (Reduktion der Bänderzahl): Sei M eine Turing – Maschine, S(n) – bandbeschränkt mit k Bändern. Wir können eine Turing – Maschine konstruieren, die die k Bänder von M auf einem Band simuliert. Satz (linearer Speed – Up): Wird ein Problem von einer T (n) – zeitbeschränkten k – Band – Turing – Maschine gelöst, so wird es für jede reelle Zahl c ∈ R, c > 0 50 KAPITEL 3. KOMPLEXITÄTSTHEORIE auch durch eine cT (n) – zeitbeschränkte k – Band – Turing – Maschine gelöst, falls k > 1 und limn→∞ T (n) n = ∞. Korollar: Unter diesen Voraussetzungen gilt DTIME (T (n)) = DTIME (cT (n)) NTIME (T (n)) = NTIME (cT (n)) Satz (nach Savitch): Für jedes Akzeptanzproblem A (Erkennung, ob ein Wort in einer Sprache liegt), gilt: Ist A ∈ NSPACE (S(n)), dann gilt auch A ∈ DSPACE S 2 (n) , falls gilt (die Funktion S ist vollbandkonstruierbar): Es gibt eine Turing – Maschine, so dass gilt: Für alle n ∈ N existiert ein Eingabewort w mit |w| = n, so dass die Turing – Maschine tatsächlich S(n) Speicherzellen benötigt. 3.1.4 Polynomiale und nichtdeterministisch polynomiale Zeitkomplexität Ein Problem heißt (bezüglich Zeitkomplexität) polynomial deterministisch lösbar, wenn es in DTIME nk liegt mit k ∈ N. Analog definieren wir exponentielle Zeitkomplexität: DTIME (2n ). Besonders interessiert [ DTIME (2n ) \ DTIME nk k∈N Dies sind die Probleme, die für größere n theoretisch, aber nicht praktisch berechenbar sind. Wir diskutieren nun zwei Klassen: [ P := DTIME nk k∈N N P := [ NTIME nk k∈N Es gilt N P ⊆ DTIME (2n ). Frage: Gilt P = N P? Klarer ist diese Frage für die Band- / Speicherkomplexität beantwortet: [ PSPACE = DSPACE ni k≥1 N P SPACE = [ NSPACE ni k≥1 Nach dem Satz von Savitch: NSPACE ni ⊆ DSPACE n2i Dies ergibt sofort PSPACE = N P SPACE Es gilt NSPACE (log n) ⊆ P ⊆ N P ⊆ PSPACE Dies ergibt eine Klassifizierung von Problemen in solche, die praktisch lösbar sind und solche, die aufgrund des benötigten Aufwands (exponentielle Zunahme des Aufwandes abhängig von der Größe der Eingabe) praktisch nicht mehr lösbar sind. 3.1. KOMPLEXITÄTSMASSE 3.1.5 51 Nichtdeterminismus in Algorithmen — Backtracking Bisher hatten wir Nichtdeterminismus im Wesentlichen bei Turing – Maschinen studiert. Nun behandeln wir Nichtdeterminismus auf der Ebene von Programmiersprachen. Nichtdeterminismus liegt in einem System oder Algorithmus vor, wenn bei bestimmten Schritten Wahlmöglichkeiten gegeben sind. Achtung: Wir betrachten hier keine Annahmen über Wahrscheinlichkeiten für die Wahl der Schritte. Für funktionale Sprachen können wir Nichtdeterminismus durch folgende Konstruktion einführen: Wir betrachten für Ausdrücke E1 und E2 den nichtdeterministischen Ausdruck E1 E2 Dieser Ausdruck ergibt bei Auswertung den (einen) Wert von E1 oder den (einen) Wert von E2. Bemerkungen: • Diese Idee lässt sich auch auf Anweisungen übertragen. • Auch die Terminierung kann von der nichtdeterministischen Auswahl abhängen. Wir betrachten im Folgenden nichtdeterministische Algorithmen, bei denen die Terminierung gesichert ist. Beispiel: 1 2 4 6 8 10 12 Nichtdeterministische Erzeugung von Permutationen fct f = (nat n) seq nat: if n = 0 then <> else einf (f(n-1), n) fi fct einf = (seq nat s, nat n) seq nat: if s = <> then <n> else conc (<n>, s) [] conc (<first(s)>, einf(rest(s), n)) fi Behauptung: f (n) erzeugt nichtdeterministisch eine Permutation von {1, . . . , n}, wobei jede Permutation als Ergebnis auftreten kann. Beweis: Induktion über n ∈ N. 1. Fall: n = 0: f (0) liefert <>. 2. Fall: Annahme: f (n − 1) liefert eine beliebige Permutation über {1, . . . , n − 1}. n wird in f (n−1) an einer beliebigen Position eingefügt. Dies liefert eine Permutation von {1, . . . , n}; jede Permutation kann so erzeugt werden. Bisher hatten wir nur Beispiele betrachtet, wo jeder nichtdeterministische Berechnungspfad zu einem Ergebnis führt, das unseren Vorstellungen genügt. Oft studieren wir nichtdeterministische Berechnungen (z.B. Turing – Maschinen), wo gewisse Pfade nicht zu einem brauchbaren Ergebnis führen. Dazu definieren wir einen neuen Ausdruck failure, der darstellt, dass dieses Ergebnis nicht brauchbar ist. 52 KAPITEL 3. KOMPLEXITÄTSTHEORIE Beispiel: 1 2 4 6 8 fct sqrt = (nat n, nat y) nat: if y^2 > n then failure elif y^2 <= n <= (y+1)^2 then y else sqrt (n, 2y+1) [] sqrt (n, 2y) fi sqrt(n, 1) für n > 0 liefert die natürlichzahlige Wurzel, wenn wir vereinbaren, dass nichtdeterministische Berechnungen, die mit failure enden, nicht akzeptiert werden. Eine nichtdeterministische Berechnung darf nur mit failure enden, wenn alle Zweige auf failure führen. Beispiel: Erfüllbarkeit von aussagenlogischen Formeln. Wir betrachten eine logische Formel (boolescher Ausdruck), der genau x1 , . . . , xn als freie Identifikatoren der Sorte bool enthält. Der Ausdruck heißt erfüllbar, wenn es Werte b1 , . . . , bn ∈ B gibt, so dass für x1 = b1 , . . . , xn = bn der Ausdruck den Wert true liefert. Damit ist die Sequenz hb1 . . . bn i der Nachweis für die Erfüllbarkeit. Wir stellen den Ausdruck als Funktion dar: 1 fct ausdruck = (seq bool b) bool Ein nichtdeterministischer Algorithmus für die Erfüllbarkeit: 2 4 6 8 10 fct erfuellbar = (seq bool s) bool: if |s| = n then if ausdruck(s) then true else failure fi else erfuellbar (conc(s, <L>)) [] erfuellbar (conc(s, <0>)) fi erfuellbar(<>) liefert lauter failure – Resultate und damit insgesamt das Resultat failure, falls der gegebene Ausdruck nicht erfüllbar ist; sonst aber true. Für Programmiersprachen, die Konzepte wie nichtdeterministische Auswahl und failure anbieten, müssen spezielle Auswertungstechniken (Auswertung durch Suche) eingesetzt werden. Stichworte: • Logikprogrammierung • Constraint – Programmierung Dazu existieren eigene Theorien: (failure E) = E. Die Idee der nichtdeterministischen Auswertung lässt sich weiter verallgemeinern: Wir verwenden einen Auswahloperator some m x : q(x) wobei q ein Prädikat sei: fct q = (m x) bool: ... (3.1) 3.2. N P – VOLLSTÄNDIGKEIT 53 (3.1) liefert einen beliebigen Wert x, für den q(x) gilt. Falls für alle m x : ¬q(x) gilt, liefert (3.1) den Wert ⊥. Dieser Operator ist nützlich, um Algorithmen zu skizzieren. Beispiel: Hamiltonkreis Gegeben sei die Sorte {0, . . . , n − 1} node der Knoten im Graph. Kanten werden durch die Funktion fct g = (node, node) bool dargestellt (vgl. boolesche Matrix). Es ist g (i, k) = true genau dann, wenn eine Kante vom Knoten i zum Knoten k existiert. Ein Hamiltonkreis für einen Graph durch eine Teilmenge S von Knoten ist ein geschlossener Weg, in dem alle Knoten in S genau ein Mal auftreten. Rechenvorschrift für Hamiltonkreis: 1 2 4 6 8 10 12 14 fct hk = (set node s, seq node q) seq node: if s = emptyset then if g (last(q), first(q)) then q else failure fi else node x = some node z: z in s; if g (last(q), x) then hk (s\{x}, conc (q, <x>)) else failure fi fi Se x ∈ s ein beliebiger Knoten aus der nichtleeren Menge s. hk (s\{x}, <x>) liefert einen Hamiltonkreis, falls ein solcher existiert. 3.2 N P – Vollständigkeit Probleme in N P \ P sind nach heutigem Wissensstand nur exponentiell zu lösen. Es existieren Probleme Q in N P mit folgender Eigenschaft: Jedes andere Problem in N P kann in polynomialer Zeit auf Q zurückgeführt werden. Dann gilt: Q ∈ P ⇒ P = NP Solche Probleme Q heißen N P – vollständig (N P – hart). 3.2.1 Das Erfüllungsproblem Ein boolescher Ausdruck ist gegeben durch eine Menge von Identifikatoren x1 , . . . , xn der Sorte bool und die Verknüpfungen ¬, ∧, ∨, . . . . Natürlich können wir eine Darstellung von booleschen Ausdrücken auf Turing – Maschinen finden. Identifikatoren lassen sich durch Zahlen repräsentieren. Satz: Das Erfüllbarkeitsproblem LSAT für boolesche Ausdrücke ist N P – vollständig. Beweis: 1. LSAT ∈ N P: Wir können eine Turing – Maschine konstruieren, die nacheinander alle Werte für die Identifikatoren nichtdeterministisch festlegt und dann überprüft, ob damit der Ausdruck den Wert true hat. 54 KAPITEL 3. KOMPLEXITÄTSTHEORIE 2. Für alle R ∈ N P existiert eine Turing – Maschine, die R in polynomialer Zeit in das Problem LSAT überführt. Wir beschränken uns auf Probleme der formalen Sprachen, genauer auf das Wortproblem. Ausgangspunkt: Gegeben ist eine nichtdeterministische Turing – Maschine, die für ein gegebenes Wort in polynomialer Zeit entscheidet, ob das Wort in der Sprache ist. Beweisidee: Wir konstruieren eine Turing – Maschine U , die aus der gegebenen Turing – Maschine eine neue konstruiert, die dem Erfüllbarkeitsproblem entspricht, so dass der zugrundeliegende Ausdruck genau dann erfüllbar ist, wenn das Wort akzeptiert wird. Dazu konstruieren wir einen booleschen Ausdruck, der die Konfigurationen der Ausgangs – Turing – Maschine durch Wahrheitswerte charakterisiert sowie die Frage, ob es sich bei der Folge von Konfigurationen um eine Berechnung handelt, beantwortet. 3.2.2 N P – vollständige Probleme Mittlerweile ist für eine reiche Zahl von Problemen nachgewiesen, dass sie N P – vollständig sind. Gelingt es für eines dieser Probleme, einen deterministischen, polynomial beschränkten Algorithmus anzugeben, so ist P = N P bewiesen. Prominente Beispiele i. Clique Gegeben: • ungerichteter Graph: – V Menge der Knoten – R ⊆ V × V Menge der Kanten • k∈N Problem: Existiert eine Teilmenge C ⊆ V mit |C| = k und C × C ⊆ R? Dies entspricht der Frage, ob der Graph einen Teilgraph mit k Knoten enthält, in dem alle Knoten paarweise durch Kanten verbunden sind. ii. Ganzzahlige Programmierung / Optimierung Gegeben: • Matrix A ∈ Zn×n • Vektor v ∈ Zn Gesucht ist ein Vektor c ∈ {0, 1}n , so dass gilt: Ac ≥ v (elementweise). iii. Überdeckende Knotenmengen Gegeben: • ungerichteter Graph: – Knotenmenge V – Kantenmenge R ⊆ V × V mit R = RT • k∈N Gesucht ist eine Knotenmenge U ⊆ V mit |U | = k und U ist Überdeckung, d.h. ∀v, w ∈ V : (v, w) ∈ R ⇒ ¬(v ∈ U ⇔ w ∈ U ) Anschaulich: Gibt es eine Färbung der Knoten (U ), so dass jede Kante einen gefärbten und einen ungefärbten Knoten verbindet? 3.3. EFFIZIENTE ALGORITHMEN . . . 55 iv. Gerichteter Hamiltonkreis (siehe oben) v. Problem des Handlungsreisenden Gegeben: • Menge von Knoten V = {1, . . . , n} • dist : V × V → N • k∈N Es gelte die Dreiecksungleichung: ∀i, j, l ∈ V : dist(i, l) + dist(l, j) ≥ dist(i, j) Gesucht: Permutation x1 , . . . , xn der Knoten mit dist(xn , x1 ) + n−1 X dist(xi , xi+1 ) ≤ k i=1 Folgende verwandte Problemstellungen sind oft von annähernd gleicher Komplexität: • Stelle fest, ob für ein Problem Q eine Lösung existiert. • Berechne eine Lösung zu Q. • Berechne 3.3 optimale Lösung zu Q. Effiziente Algorithmen für N P – vollständige Probleme Auch wenn ein Problem nach heutigem Kenntnisstand nur mit exponentiellem Aufwand zu behandeln ist, ist für praktische Aufgaben die Reduzierung des Aufwands (und sei es nur um Faktoren) von großer Bedeutung. Im Folgenden studieren wir eine Reihe von Techniken zur Reduzierung des Aufwands. Für N P – vollständige Probleme müssen wir mit exponentiellem Aufwand rechnen. Dabei sind Reduktionen im Aufwand die einzige Möglichkeit, gewisse Probleme noch halbwegs vernünftig algorithmisch behandeln zu können. Dies führt auf die Frage, wie ein vorgegebener Algorithmus effizienter gemacht werden kann. Techniken zur Aufwandsreduzierung beim Rechnen mit großen Aufrufbäumen: 1. Branch and Bound (geschicktes Backtracking): Der Lösungsraum (als Baum angeordnet) wird geschickt organisiert: • Einfach auszuwertende Zweige werden zuerst durchlaufen. • Frühzeitig (ohne sie ganz zu durchlaufen) Zweige (Teilbäume) als uninteressant erkennen und nicht vollständig durchlaufen (pruning). 2. Approximative Verfahren: Oft sind optimale Lösungen für ein Problem gesucht (Rundreise von Handlungsreisenden mit minimaler Länge, Scheduling mit optimaler Verteilung etc.). In solchen Fällen sind oft Näherungslösungen auch akzeptabel. Statt des absoluten Optimums wird eine Näherung berechnet, die hinreichend gut ist. 56 KAPITEL 3. KOMPLEXITÄTSTHEORIE 3. Dynamisches Programmieren: Treten in Aufrufbäumen Aufrufe mit gleichen Parametern öfter auf, so können wir durch Tabellieren der Ergebnisse das mehrfache (aufwändige) Aufrufen vermeiden. Im Extremfall rechnen wir statt Top-Down (es wird eine Kaskade von rekursiven Aufrufen erzeugt) BottomUp: Wir erzeugen eine Tabelle mit Ergebnissen, in die wir die einfachen Fälle zuerst eintragen und dann schrittweise die Tabelle vervollständigen (Nachteile: hoher Speicheraufwand, komplexe Datenstrukturen). 4. Probabilistische Algorithmen: Diese Algorithmen verwenden Zufallszahlen (Zufallsgeneratoren). Varianten: • Algorithmen, die mit einer bestimmten Wahrscheinlichkeit ein korrektes oder optimales Ergebnis liefern. • Algorithmen, bei denen die Terminierung innerhalb einer Zeitschranke mit einer bestimmten Wahrscheinlichkeit gesichert ist. 5. Einschränkung der Problemklasse: Häufig können eigentlich N P – vollständige Problemstellungen durch Einschränkungen auf Teilprobleme zurückgeführt werden, die polynomial lösbar sind. 6. Heuristische Methoden: Dies sind Methoden, die ad-hoc eingesetzt werden, ohne genaue Angabe, warum oder wie gut sie funktionieren. Für eine angemessene Behandlung N P – vollständiger Probelme ist besonderes Geschick des Programmierers erforderlich. 3.3.1 Geschicktes Durchlaufen von Baumstrukturen Wir betrachten eine spezielle Problemstellung: Wir minimieren eine Funktion f :M →O wobei O eine linear geordnete Menge sei, über M . Dabei stellen wir uns vor, dass wir die Menge als Baum anordnen können: 1 2 fct baum = (m x) set m: if vollstaendig(x) then {x} 4 else union (i, baum(g(i, x)), 1, k(x)) 6 fi S wobei union (i, f, min, max) = min≤i≤max f sei. baum(x) erzeugt eine Menge, die baumartig angeordnet ist. 8 10 12 14 16 18 fct suchemin = (set m s) m: if |s| = 1 then some m x: x in s else m x = some m z: z in s m y = suchemin (s\{x}) if f(x) <= f(y) then x else y fi fi 3.3. EFFIZIENTE ALGORITHMEN . . . 57 Aufruf: suchemin(baum(x)) Dieses Verfahren ist sehr schematisch und in der Regel ineffizient. Durch geschicktes Verbinden von Suchen und erzeugen können wir den Aufwand reduzieren. Beispiel: Erfüllbarkeitsproblem Gegeben sei a : seq bool → bool bool, definiert auf Sequenzen der Länge n. Wir nehmen an, dass wir eine Approximationsfunktion fail(a, s) besitzen, die für alle booleschen Sequenzen s mit |s| ≤ n folgende Eigenschaft hat: fail(a, s) ∧ |s ◦ z| = n ⇒ a(s ◦ z) = f alse Folgender Algorithmus nutzt dies aus: 1 2 4 6 8 10 12 14 16 fct suche = (seq bool x) seq bool: if length(x) = n then x else seq bool s = conc (x, <L>) if fail (a, s) then suche (conc (x, <0>)) else seq bool y = suche (s) if a(y) then y else suche (conc (x, <0>)) fi fi fi Dieser Algorithmus setzt voraus, dass das gesuchte Element im Baum ist. Sonst kann über das Prädikat fail die Suche im letzten else – Zweig noch verkürzt werden. 3.3.2 Alpha / Beta – Suche Wir betrachten 2 – Personen – Spiele. Zwei Spieler α und β machen abwechselnd Züge, bis eine Endstellung erreicht ist. Wir nehmen an, dass das Spiel endlich ist, d.h. dass keine unendlichen Zugfolgen existieren. In jeder Endstellung ist ein Gewinn / Verlust für jeden Spieler festgelegt. Wir sind an einem optimalen Spiel interessiert. Wir bilden diese Situation in Informatikstrukturen wie folgt ab: 1. Sorten: • player: Sorte der Spieler (= {α, β}) • position: Sorte der Stellungen 2. Spielzüge: • fct Z = (player, position) set position Z (s, p) beschreibt die Menge der zulässigen Züge für Spieler s in Stellung p. 3. Endstellungen: 58 KAPITEL 3. KOMPLEXITÄTSTHEORIE • fct t = (player, position) bool t (s, p) legt fest, ob für Spieler s die Position p eine Endstellung ist. 4. Gewinn: • fct g = (player, position) bool g (s, p) legt fest, ob die Stellung p als Endstellung einen Gewinn für Spieler s darstellt. 5. Hilfsfunktion: • fct gegner = (player) player Es gilt gegner(α) = β und gegner(β) = α. Wir bezeichnen eine Stellung p als sicher für Spieler s, falls wir bei optimalem Spiel immer gewinnen. Rechenvorschrift: 1 2 4 6 fct sicher = (player s, position p) bool: if t (s, p) then g (s, p) else exists q in Z (s, p): not sicher (gegner (s), q) fi Es gilt (falls ¬t (gegner (s) , q)): ¬ sicher (gegner (s) , q) = ¬∃qi ∈ Z (gegner (s) , q) : ¬ sicher (gegner (gegner (s)) , q 0 ) = ∀q 0 ∈ Z (gegner (s) , q) : sicher (s, q 0 ) 0 Nun betrachten wir ein leicht verändertes Problem, indem wir annehmen, dass in jeder Endstellung p für einen Spieler s durch fct g = (player, position) int festgelegt wird, wie viel der Spieler gewinnt (bzw. verliert). 1 2 4 6 fct opt = (player s, position p) int: if t (s, p) then g (s, p) else max {-opt (gegner (s), q): q in Z (s, p)} fi Jetzt gilt für ¬t (gegner (s) , q): − opt (gegner (s) , q) = − max {− opt (gegner (gegner (s)) , q 0 ) : q 0 ∈ Z (gegner (s) , q)} = min {− opt (s, q 0 ) : q 0 ∈ Z (gegner (s) , q)} Wir betrachten nun Optimierungsmöglichkeiten. Wir unterstellen, dass wir eine Abschätzung für unseren Gewinn opt(s, p) ≤ maxopt(s, p) besitzen (Abschätzung / obere Schranke für den Gewinn). Idee: Wir suchen nun ein Spielergebnis relativ zu vorgegebenen Schranken. Dazu definieren wir eine Sorte eint mit Werten aus Z ∪ {−∞, +∞}. Festlegung: max ∅ = −∞ min ∅ = +∞ 3.3. EFFIZIENTE ALGORITHMEN . . . 59 Sei M eine Menge von Stellungen. Sei a = max{opt(s, p) : p ∈ M }. Wir definieren nun eine neue Funktion fct setopt = (player s, set position p, eint min, max) eint Dabei gelte (es sei min ≤ max) max falls max ≤ a setopt(s, M, min, max) = a falls min ≤ a ≤ max min falls a ≤ min Wir geben einen Algorithmus für setopt an, der effizienter ist als opt. Wichtig: opt(s, p) = setopt(s, {p}, −∞, +∞) 1 2 4 6 8 10 12 14 16 18 20 22 fct setopt = (player s, set position m, eint min, max) eint: if m = emptyset then min else position p = some position q: q in m if maxopt (s, p) <= min then setopt (s, m\{p}, min, max) else eint a = if t (s, p) then g (s, p) else -setopt (gegner(s), Z(s, p), -max, -min) fi if a >= max then max elif a > min then setopt (s, m\{p}, a, max) else setopt (s, m\{p}, min, max) fi fi fi 3.3.3 Dynamisches Programmieren Idee: Optimierung von Algorithmen mit Rekursions – Aufrufbäumen, in denen viele gleiche Aufrufe auftreten: Jeder Aufruf wird nur ein Mal ausgewertet, tabelliert und dann wird jeweils der ermittelte Wert verwendet. Nachteile: • hoher Speicherplatzbedarf • Algorithmen werden kompliziert Beispiel: 1 2 4 6 Handlungsreisenden – Problem fct perm = (seq node s, set node m) set seq node: if m = emptyset then {s} else node x = some node z: z in m; union (i, perm (insert (s, x, i), m\{x}), 0, length (s)) fi 60 KAPITEL 3. KOMPLEXITÄTSTHEORIE mintour (m, i) mintour (m\{i}, k1) ... mintour (m\{i}, kn) ... ... Abbildung 3.1: Rekursionsstruktur von mintour insert (s, x, i) fügt den Knoten x in die Sequenz s nach Position i ein. perm (ε, M) erzeugt die Menge aller Permutationen der Menge M. Es sei eine Abstandsfunktion gegeben: 1 2 fct d = (node i, j) nat: "Abstand zwischen i und j" Aufgabe des Handlungsreisenden: Minimiere sd(s) + d(first(s), last(s)) über s ∈ perm(ε, v) für die Knotenmenge v 6= ∅. 1 2 fct mintour = (set node m, node i: i in m) nat: min {sd (conc (<x0>, s)): s in perm (<>, m) and last (s) = i} mintour (m, i) berechnet von einem gegebenen Anfangsknoten x0 aus alle Wege von x0 nach i über alle Knoten in m, wobei i ∈ m sei. Problem des Handlungsreisenden für Knotenmenge m und Ausgangspunkt x0 ∈ / m: min {mintour(m, x) + d(x, x0 )} x∈m (Länge des kürzesten Wegs von x0 nach x über alle Knoten in m und Rückkehr zu x0 ). Rechenvorschrift für mintour: 1 2 4 6 fct mintour = (set node m, node i: i in m) nat: if |m| = 1 then d (x0, i) else min {mintour (m\{i}, k) + d (k, i): k in m\{i}} fi Dieser Algorithmus hat die Rekursionsstruktur (sei m = {k1 , . . . , kn }) aus Abbildung (3.1). Q Eine Analyse zeigt, dass in jeder Schicht t des Aufrufbaums nur 1≤i≤t n − i + 1 verschiedene Aufrufe auftreten. Dies liefert insgesamt n2 2n verschiedene Aufrufe, d.h. einen Berechnungsaufwand von O(n2 2n ) gegenüber O(n!) beim ursprünglichen Algorithmus. 3.3.4 Greedy – Algorithmen Greedy – Algorithmen arbeiten nach dem Prinzip der lokalen Optimierung. Idee: Wähle den nächsten Schritt (lokal) optimal. Dies führt nicht immer zu einem globalen Optimum. Für gewisse Problemklassen führt lokale Optimierung jedoch zu einer globalen Optimierung. 3.3. EFFIZIENTE ALGORITHMEN . . . 61 Beispiel: Dijkstras Algorithmus der kürzesten Wege Gegeben sind eine Knotenmenge V = {1, . . . , n} und eine Abstandsfunktion d : V × V → N ∪ {∞} Wir suchen die Länge des kürzesten Wegs zwischen zwei Punkten. Wir betten das Problem ein und betrachten folgende Aufgabe: Gegeben: • Knoten x, y • Knotenmenge m ⊆ V Gesucht: kürzester Weg von x nach y x → x1 → x2 → x3 → · · · → xn → y wobei x1 , . . . , xn ∈ m seien. 1 2 4 6 fct dijkstra = (set node m, node x, y) enat: if m = emptyset then d (x, y) else node c = some node z: z in m and p (z) min {dijkstra (m\{c}, x, y), d (x, c) + dijkstra (m\{c}, c, y)} fi Dabei sei p (z) = ∀ node b : b ∈ m ⇒ d(x, z) ≤ d(x, b). Eine effiziente Implementierung dieses Algorithmus arbeitet mit Techniken der dynamischen Programmierung auf einer Matrix, die jeweils die Wege zwischen Knoten speichert, soweit nur Knoten in einer bestimmten vorgegebenen Menge als innere Knoten verwendet werden. 62 KAPITEL 3. KOMPLEXITÄTSTHEORIE Kapitel 4 Effiziente Algorithmen und Datenstrukturen (s. gleichnamiges Buch von Niklas Wirth) Für gegebene Aufgabenstellungen ist es wichtig, geschickt Datenstrukturen zu wählen und Algorithmen, die darauf effizient arbeiten. Wichtig bei der Wahl der Datenstrukturen ist dabei die Frage, welche Zugriffsoperationen möglichst effizient ausgeführt werden sollen. 4.1 Diskussion ausgewählter Algorithmen Sortieren ist eine der wichtigsten Aufgaben in der betriebswirtschaftlichen Informatik. 4.1.1 Komplexität von Sortierverfahren Gängige Sortierverfahren: Insertsort Selectsort Mergesort Quicksort Heapsort Bubblesort Sortieren Sortieren Sortieren Sortieren Sortieren Sortieren durch durch durch durch durch durch Einsortieren Auswählen Mischen Zerteilen Auswahlbäume Vertauschen Aufwand n2 n2 n log n n log n n log n n2 Beispiel: Bubblesort Gegeben sei ein Feld var [1:n] array int a. Folgendes Programm sortiert die Elemente im Feld a aufsteigend. 1 2 4 6 8 var nat i := 1; while i < n do if a[i] > a[i+1] then a[i], a[i+1] := a[i+1], a[i]; if i > 1 then i := i - 1 else i := i + 1 fi 63 64 KAPITEL 4. EFFIZIENTE ALGORITHMEN UND DATENSTRUKTUREN 10 12 else i := i + 1 fi od Wir stellen uns nun die Frage, mit welchem Aufwand wir eine Sequenz sortieren können. Bubblesort hat im Allgemeinen einen Aufwand von n2 , ist also von O(n2 ). Wichtige Unterschiede in der Aufwandsschätzung: • Aufwand im ungünstigsten Fall (worst case) • durchschnittlicher Aufwand (bei gegebener Wahrscheinlichkeitsverteilung auf den Eingaben) Weiterer Aspekt: Speicheraufwand Aufwand von Quicksort: • worst case: n2 • Durchschnitt: n log n Welches Verfahren gewählt wird, hängt stark von Rahmenbedingungen ab: • Größe der Sequenz: Sind die Sequenzen sehr klein, so empfehlen sich einfache, speziell darauf zugeschnittene Verfahren. • Kenntnisse über die Anordnung der Elemente (Wahrscheinlichkeitsverteilung der Eingabe): Sind die Elemente in der zu sortierenden Sequenz nicht zufällig angeordnet, sondern beispielsweise schon weitgehend vorsortiert, so arbeiten bestimmte Verfahren gut, andere extrem schlecht. • Nebenbedingungen technischer Art: Wie sind die Sequenzen gespeichert? Wird im Hauptspeicher (internes Sortieren) oder im Hintergrundspeicher (externes Sortieren) sortiert? 4.1.2 Wege in Graphen Viele praktische Fragestellungen führen auf Probleme, die sich mit Wegen in Graphen beschreiben lassen. Wir behandeln stellvertretend einen Algorithmus zum Finden von Wegen in einem gerichteten Graphen: Warshalls Algorithmus. Gegeben: • Knotenmenge V = {1, . . . , n} • Kanten: c : V × V → B mit c(i, j) ∼ es gibt eine Kante von i nach j. Wir rechnen die transitive Hülle aus: 1 2 fct th = (nat i, j) bool: wth (i, j, n) Idee: wth (i, j, h) bestimmt die Antwort auf die Frage von i nach j über innere Knoten ≤ k? 4 6 8 existiert ein Weg in c fct wth = (nat i, j, k) bool: if k = 0 then c (i, j) else wth (i, j, k-1) or (wth (i, k, k-1) and wth (k, j, k-1)) fi 4.2. BÄUME 65 Diese Idee können wir in einen Algorithmus umwandeln, der auf einem zweidimensionalen Feld a (Adjazenzmatrix) arbeitet: {∀i, j : 1 ≤ i, j ≤ n ⇒ a[i, j] = c(i, j)} 1 2 4 6 for k := 1 to n do for i := 1 to n do for j := 1 to n do a[i,j] := a[i,j] or (a[i,k] and a[k,j]) od od od {∀i, j : 1 ≤ i, j ≤ n : a[i, j] = wth(i, j, n)} Komplexität: n3 4.2 Bäume Baumstrukturen sind ein Kernthema der Informatik. 4.2.1 Geordnete, orientierte, sortierte Bäume Es existieren zwei Blickweisen auf Bäume: 1. Bäume als Datenstrukturen mit rekursivem Aufbau und Zugriffsfunktionen 2. Bäume als Graphen Zu Bäumen als Graphen: Ein ungerichteter Graph G heißt nichtorientierter Baum, falls G • zyklenfrei und • zusammenhängend ist. Ein gerichteter Graph heißt orientierter Baum, falls • eine Wurzel k existiert (ein Knoten k) und • von k aus alle anderen Knoten auf genau einem Weg erreichbar sind. Ein geordneter Baum ist ein orientierter Baum, bei dem für jeden Knoten eine Reihenfolge für seine Teilbäume gegeben ist. sort ordtree = ordtree (m root, seq ordtree subtrees) Der Verzweigungsgrad ist die (maximale) Anzahl der Teilbäume pro Knoten. In einem vollständigen Baum haben alle Wege (zu Blättern von der Wurzel) die selbe Länge. 4.2.2 Darstellung von Bäumen durch Felder Üblicherweise werden Bäume durch verkettete Zeigerstrukturen in Rechnern dargestellt. Wir können vollständige Bäume auch statisch in Feldern ablegen. a Darstellung im Feld: [a, b, d, e, c, f, g] b c d ef g 66 KAPITEL 4. EFFIZIENTE ALGORITHMEN UND DATENSTRUKTUREN m1 m2 . . . . . . . . . mk . . . . . . Abbildung 4.1: B – Baum 4.2.3 AVL – Bäume Beim Arbeiten mit Bäumen sind wir am kurzen (effizienten) Zugriff auf die Elemente interessiert. Dies ist nur gewährleistet, wenn die Bäume nicht entarten, d.h. wenn die Höhe des Baumes logarithmisch mit der Anzahl der Knoten im Baum wächst. Dies könnte auf die Forderung hinauslaufen, dass alle Wege von der Wurzel zu den Blättern sich höchstens um 1 in der Länge unterscheiden. Allerdings wird es sehr aufwändig, beim Einfügen oder Löschen von Knoten die Ausgewogenheit zu erhalten. Ausweg: Statt völliger Ausgewogenheit fordern wir, dass sich die Höhe des linken und rechten Teilbaumes um maximal 1 unterscheiden. AVL – Baum: Sortierter Binärbaum, bei dem sich in allen Knoten die Höhen der Teilbäume nur um maximal 1 unterscheiden. Sortiert bedeutet dabei: Für alle Teilbäume gilt: Die Einträge im linken Teilbaum sind kleiner oder gleich der Wurzel und die Wurzel ist kleiner oder gleich allen Einträgen im rechten Teilbaum. Vorteil: Gesteuerte Suche ist möglich. Wichtig: Bei AVL – Bäumen können Knoten mit geringem Aufwand eingefügt oder gelöscht werden. 4.2.4 B – Bäume B – Bäume sind die Grundlage für die Speicherung von Informationen in Datenbanken. Sie wurden 1970 von R. Bayer vorgeschlagen. Sei n ∈ N eine gegebene Zahl. Ein B – Baum über einer linear geordneten Menge M hat die Gestalt aus Abb. 4.1. Ein B – Baum ist leer oder enthält zwischen n und 2n Teilbäume: n − 1 ≤ k ≤ 2n − 1. Ein B – Baum ist sortiert. Vorteile: 1. Es kann gezielt gesucht werden. 2. Die Höhe (Anzahl der Suchschritte) ist extrem klein. (Beim Speichern der B – Bäume auf Hintergrundmedien sind nur wenige Seitenzugriffe erforderlich.) 3. Einfügen und Löschen von Elementen ist extrem billig. Bemerkung: Die Wurzel des B – Baums kann weniger als n Einträge enthalten. In B – Bäumen haben alle Wege von der Wurzel zu Blättern die gleiche Länge. 4.3 Effiziente Speicherung großer Datenmengen Die Datenstruktur der (endlichen) Mengen wird in den meisten Programmiersprachen nicht direkt unterstützt. Im Folgenden betrachten wir Datenstrukturen für die effiziente Behandlung großer Mengen. 4.3. EFFIZIENTE SPEICHERUNG GROSSER DATENMENGEN 4.3.1 67 Rechenstruktur der Mengen (mit Zugriff über Schlüssel) Wir nehmen an, dass wir Datensätze der Sorte data speichern wollen. Für jeden Datensatz setzen wir einen Schlüssel voraus: fct key = (data) key Wir wollen eine endliche Menge von Daten speichern und über Schlüssel darauf zugreifen. Die Sorte store bezeichne die Menge der endlichen Mengen von Daten. Zunächst interessiert uns die Struktur der Elemente der Sorte store nicht, sondern die Operationen, die wir zur Verfügung haben: 1 2 4 fct fct fct fct emptystore = store get = (store, key) data insert = (store, data) store delete = (store, key) store Logische Festlegung der Wirkungsweise dieser Funktionen (Schnittstellenverhalten): k = key(d) ⇒ get (insert (s, d) , k) = d k 6= key(d) ⇒ get (insert (s, d) , k) = get(s, k) delete (emptystore, k) = emptystore k = key(d) ⇒ delete (insert (s, d) , k) = delete(s, k) k 6= key(d) ⇒ delete (insert (s, d) , k) = insert (delete(s, k), d) Zusätzliche Operationen: 6 fct isempty = (store) bool fct isentry = (store, key) bool isentry(emptystore, k) = false isentry(insert(s, d), k) = (k = key(d) ∨ isentry(s, k)) 4.3.2 Mengendarstellung durch AVL – Bäume Wir stellen die Elemente der Sorte store durch AVL – Bäume dar: sort store = tree data Damit sind bestimmte Funktionen wie root, left, right auf store vorgegeben. Die weiteren (eigentlichen) Funktionen auf store programmieren wir nun, abgestützt auf diese Grundfunktionen. 1 2 4 6 8 10 12 14 fct get = (store s, key k) data: if key (root (s)) = k then root (s) elif k < key (root (s)) then get (left (s), k) else get (right (s), k) fi fct insert = (store s, data d) store: if s = emptytree then cons (emptytree, d, emptytree) elif key (d) = key (root (s)) then cons (left (s), d, right (s)) elif key (d) < key (root (s)) then 68 KAPITEL 4. EFFIZIENTE ALGORITHMEN UND DATENSTRUKTUREN 16 18 cons (insert (left (s), d), root (s), right (s)) else cons (left (s), root (s), insert (right (s), d)) fi Achtung: insert liefert einen sortierten, aber nicht notwendigerweise balancierten Baum — selbst, wenn s balanciert ist. 1 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38 40 42 44 46 fct balinsert = (store a, data d) store: if a = emptytree then cons (emptytree, d, emptytree) elif key (d) = key (root (a)) then cons (left (a), d, right (a)) elif key (d) < key (root (a)) then store b = balinsert (left (a), d); if hi (b) > 1 + hi (right (a)) then leftbalance (b, root (a), right (a)) else cons (b, root (a), right (a)) fi else store b = balinsert (right (a), d); if hi (b) > 1 + hi (left (a)) then rightbalance (left (a), root (a), b) else cons (left (a), root (a), b) fi fi fct leftbalance = (store b, data aw, store ar) store: if hi (left (b)) >= hi (right (b)) then cons (left (b), root (b), cons (right (b, aw, ar))) else cons (cons (left (b), root (b), left (right (b))), root (right (b)), cons (right (right (b)), aw, ar)) fi fct rightbalance = (store al, data aw, store b) store: if hi (right (b)) >= hi (left (b)) then cons (cons (al, aw, left (b)), root (b), right (b)) else cons (cons (al, aw, left (left (b))), root (left (b)), cons (right (left (b)), root (b), right (b))) fi fct baldelete = (store a, key k) store: if a = emptytree then emptytree elif key (root (a)) = k then delete_root (a) elif k < key (root (a)) then store b = baldelete (left (a), k); if 1 + hi (b) < hi (right (a)) then rightbalance (b, root (a), right (a)) else 4.3. EFFIZIENTE SPEICHERUNG GROSSER DATENMENGEN 48 50 52 54 56 69 cons (b, root (a), right (a)) fi else store b = baldelete (right (a), k); if 1 + hi (b) < hi (l (a)) then leftbalance (left (a), root (a), b) else cons (left (a), root (a), b) fi fi 58 60 62 64 66 68 70 72 74 76 78 fct delete_root = (store a) store: if left (a) = emptytree then right (a) elif right (a) = emptytree then left (a) else data e = greatest (left (a)); store b = baldelete (left (a), key (e)) if 1 + hi (b) < hi (right (a)) then rightbalance (b, e, right (a)) else cons (b, e, right (a)) fi fi fct greatest = (store a) data: if right (a) = emptytree then root (a) else greatest (right (a)) fi Das Beispiel der AVL – Bäume ist typisch für Datenstrukturen, die einer größeren Grundsorte entstammen (im Beispiel Binärbäume), aber noch zusätzliche Eigenschaften (im Beispiel Balanciertheit und Sortiertheit) aufweisen. Diese Eigenschaften heißen auch Datenstrukturinvarianten. Prinzip einer Datenstrukturinvarianten: Alle zur Verfügung gestellten Operationen haben die Eigenschaft, dass — falls die Invariante für die Eingangsparameter gilt — diese auch für das Ergebnis gilt. Diese Version von AVL – Bäumen hat linearen Aufwand (O(n), wobei n die Anzahl der Elemente im Baum ist). Idee: Wir speichern im Baum zusätzlich zur jeweiligen Wurzel die Höhe. Ergebnis: O(log(n)) – Algorithmen für das Einfügen und Löschen. Sortendeklaration: sort btree = cons (btree left, data root, btree right, nat hi) Weitere Optimierung: Statt der Höhe speichern wir eine Zahl aus {−1, 0, 1} für linker Teilbaum höher / ausgewogen / rechter Teilbaum höher. Beim Einfügen gilt: Wenn ein Mal balanciert wird, hat der entstehende Baum die gleiche Höhe wie der ursprüngliche Baum vor dem Einfügen. Dies zeigt, dass höchstens ein Balancierschritt pro Einfügung erforderlich ist. Dieses Argument gilt nicht für das Löschen. Hier kann im ungünstigsten Fall in jedem rekursiven Aufruf ein Balancierschritt auftreten. 70 KAPITEL 4. EFFIZIENTE ALGORITHMEN UND DATENSTRUKTUREN 4.3.3 Streuspeicherverfahren Streuspeicherverfahren (hashing) erlauben die Speicherung von Daten unter ihren Schlüsseln in einem linearen Feld der Länge z (wobei z deutlich kleiner ist als die Anzahl der verschiedenen Schlüssel). Wir benötigen eine Streufunktion h : key → [0, z − 1] Die benötigten Operationen emptystore, get, insert, delete werden nun für ein Feld realisiert: sort store = [0:z-1] array data Dabei nehmen wir an, dass data ein Element empty umfasst, das als Platzhalter dient für Feldelemente, die nicht belegt sind. emptystore entspricht dem Feld, in dem alle Einträge den Wert empty haben. Um ein neues Element d in die Streuspeichertabelle (hashtable) einzutragen, berechnen wir zum Schlüssel key(d) den Index h (key (d)). Falls das entsprechende Feldelement leer ist, erfolgt der Eintrag; falls nicht, sprechen wir von einer Kollision und nutzen ein Verfahren zur Kollisionsauflösung. Aufgaben bei Streuspeichertabellen: • Größe des Feldes festlegen • Wahl der Streufunktion • Bestimmung eines Verfahrens zur Kollisionsauflösung Erfahrungswerte: • Tabellengröße so wählen, dass die Tabelle höchstens zu 90% gefüllt ist. • Streufunktion so wählen, dass sie gut streut (starke Abhängigkeit von Daten und Häufigkeit von Daten). Einfache Wahl (seien die Schlüssel Zahlen): h(i) = i mod z. Dabei sollte z als Primzahl gewählt werden. • Folgende Verfahren zur Kollisionsauflösung: – offene Adressierung: Bei Kollisionen wird das kollidierende Element ebenfalls im Feld gespeichert. – geschlossene Adressierung: Die Elemente werden in einem gesonderten Bereich gespeichert. Das Suchen eines freien Platzes bei offener Adressierung nennen wir Sondieren. • Lineares Sondieren besteht darin, dass wir in gleich langen Schritten nach einem freien Platz suchen: h(k), h(k) + j, h(k) + 2j, . . . • quadratisches Sondieren: h(k), h(k) + 1, h(k) + 4, h(k) + 9, . . . (natürlich jeweils mod z). Erfahrungswert: Bei 90% Füllung sind im Mittel 2, 56 Sondierungsschritte erforderlich.