Einführung in die Informatik 4

Werbung
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.
Herunterladen