Proseminar Theoretische Informatik - WS07/08

Werbung
Proseminar Theoretische Informatik - WS07/08 - Prof. Helmut Alt
I. Einleitung
Berechnungsmodelle – LOOP , WHILE , GOTO , rekursive Funktionen

Berechenbarkeitsmodelle
Welchen Sinn haben diese?
Wie kann man effizient etwas nachweisen?
- ndet Turingmaschine, ...
Modell / Reduktion mit berechenbaren Problemen
Hilfsmittel
- natürliche Zahlen, einfache Konzepte darauf (Wiederholung, Schachtelung,
Variablen, Wertemanipulation) -> “einfache” Beweise für Machbarkeit
Problem -> Lösung findbar? Existiert überhaupt? -> “Einfacher Nachweis”, wenn
Lösung gefunden oder bewiesen dass es eine gibt.
Kann man wissen, ob es eine Lösung gibt?
Usw.
Proseminar Theoretische Informatik - WS07/08 - Prof. Helmut Alt
II. LOOP , WHILE , GOTO

Definition der LOOP – Sprache
Variablen: x0 , x1 , x2 , ...
Konstanten: 0 , 1 , 2 , ...
Trennsymbole: ( ; ) , ( := )
Operationszeichen: + Schlüsselwörter: LOOP , DO , END
induktive Definition der Syntax:
1. Eine Wertzuweisung der Form xi := xj ± c , wobei c eine Konstante ist,
ist ein LOOP-Programm.
2. Falls P1 und P2 LOOP-Programme sind, so ist auch P1 ; P2 ein LOOP-Programm.
3. LOOP xi DO P END , wobei P ein LOOP-Programm ist und xi eine Variable, ist
ein LOOP-Programm.
Semantik:
Das Programm startet mit Eingabevariablen n1 , ... , nk aus N in den Variablen x1 , ...
xk , während alle anderen Variablen des Programms mit Null starten.
Das Ergebnis der Berechnungen steht am Ende in x0.
Die Wirkweise der Semantik erschließt sich weitgehend intuitiv,
die Loop-Schleife allerdings benutzt den Wert von „xi“ vor dem Ausführen von „P“
und führt das Programm so oft aus, d.h. Es gibt keine Kontrollmöglichkeit
dynamischer Natur, die Zahl der Wiederholungen kann nicht aus der Schleife selbst
heraus beeinflußt werden.
Definition (LOOP-berechenbar):
Eine Funktion f : Nk -> N heißt LOOP-berechenbar, wenn es ein LOOP-Programm P
gibt, dass für alle x aus Nk als Eingabewert(e) mit f(x) in x0 stoppt.
•
•
–
–
–
Feststellungen:
Alle LOOP-berechenbaren Funktionen sind total.
Es gibt totale intuitiv berechenbare Funktionen, die nicht LOOP-berechenbar sind.
( vgl. Ackermannfunktion, letzter Vortrag )

Die WHILE – Sprache
Die WHILE-Sprache erweitert die LOOP-Sprache um das Konzept der „WHILESchleife“:
– WHILE xi ≠ 0 DO P END
( Das WHILE-Programm P wird sooft ausgeführt, wie der Variablen-Wert xi nicht
Null ist. )
Ansonsten sind alle Konzepte aus LOOP in WHILE übertragen.
( Die Folgerungen allerdings ändern sich, LOOP ... END wird eigentlich hinfällig. )
•
Definition ( WHILE-berechenbar ):
Eine Funktion f : Nk -> N heißt WHILE-berechenbar, falls es ein WHILEProgramm P gibt, dass für alle x aus Nk als Eingabewert(e), für die f definiert ist,
mit f(x) in x0 hält, und nicht hält für alle x, für die f nicht definiert ist.
Endlosschleifen sind mit dem WHILE-Konstrukt offensichtlich möglich.
•

Satz:
Turingmaschinen können WHILE-Programme simulieren,
also ist jede WHILE-berechenbare Funktion auch Turing-berechenbar.
[ Beweis / eher nicht, keine Zeit dafür ]
Die GOTO – Sprache
Ein GOTO-Programm besteht aus einer Folge von Anweisungen, die jeweils einer
eigenen Marke zugeordnet sind:
M1 : A1 ; M2 : A2 ; ... ; Mk : Ak
Für A1..k sind folgende Anweisungen möglich:
– Wertzuweisungen: xi := xj ± c ( c ist Konstante )
– unbedingter Sprung: GOTO Mi
– bedingter Sprung: IF xi = c THEN GOTO Mj
– Stopanweisung: HALT
Die Semantik erschließt sich intuitiv, das Programm startet mit A1 , bei einem Sprung
wird an der entsprechenden Marke fortgefahren, ohne Sprung an der nächsten.
HALT beendet das Programm.
Endlosschleifen sind offensichtlich möglich.
•
•
•
•
•
Definition ( GOTO-berechenbar ):
[ analog zu WHILE-berechenbar ]
Eine Funktion f : Nk -> N heißt GOTO-berechenbar, falls es ein GOTO-Programm
P gibt, dass für alle x aus Nk als Eingabewert(e), für die f definiert ist, mit f(x) in
x0 hält, und nicht hält für alle x, für die f nicht definiert ist.
Satz:
Jedes WHILE-Programm kann durch ein GOTO-Programm simuliert werden.
D.h., Jede WHILE-berechenbare Funktion ist auch GOTO-berechenbar.
[ Beweis , eher Skizze, einfacher]
Satz:
Jedes GOTO-Programm kann durch ein WHILE-Programm simuliert werden.
D.h., Jede GOTO-berechenbare Funktion ist auch WHILE-berechenbar.
( Anmerkung: Das geht mit nur einer WHILE-Schleife )
[ Beweis , geht mit einer WHILE-Schleife ]
Satz ( Kleenesche Normalform für WHILE-Programme ):
Jede WHILE-berechenbare Funktion kann durch ein WHILE-Programm mit nur
einer WHILE-Schleife berechnet werden.
[ Beweis , nach vorherigem Beweis WHILE->GOTO->WHILE]
Satz:
GOTO-Programme können Turingmaschinen simulieren.
Damit ist jede Turing-berechenbare Funktion auch GOTO-berechenbar.
Beweis (mit 2-Zähler Maschine, siehe Vortrag “2 Zähler sind universell”):
Problem: Wir brauchen Funktion c( x , y ) zur bijektiven Codierung zweier
natürlicher Zahlen in eine und ihre Umkehrfunktionen e( n ) und f( n ) aus dem
Teil “rekursive Funktionen”, sowie die Darstellung < x1 , x2 , ... , xk > = c( x1 ,
c( x2 , ... , c( xk , 0 ) ... ) ) , und dessen Umkehrfunktionen di, welche dort
ausführlicher beschrieben sind.
Die Funktionen c , e , f sind LOOP-berechenbar, wie später gezeigt wird.
Wir drücken die Funktionalität der Maschine durch GOTO-Ausdrücke aus.
Das Eingabeband wird über eine Eingabevariable x = < x1 , x2 , ... , xk > simuliert,
wobei xi die Zeichen des Eingabealphabets sind.
Die beiden Zähler sind durch zwei Variablen p1 , p2 repräsentiert.
Es gibt eine Variable q um den Zustand anzugeben (Zahl, 0 = Start).
Es gibt eine Variable c , die das zuletzt gelesene (aktuelles) Zeichen enthält.
Eine Variable a , die angibt, ob der Zustand akzeptierend ist.
Dazu eine gewisse Menge an Hilfsvariablen.
Die d Zustände qi des Automaten und ihre Übergänge werden als codiertes
verschachteltes Tupel in der Eingabevariable z mitgeliefert:
z = < cq1 , cq2 , ... , cqd > , wobei cqi jetzt wieder ein Tupel ist, dass die nötigen
Informationen enthält: ( Akzeptierend 0|1 , ( u1 , u2 , ... , um ) )
ui geben die Übergänge an , also wieder ein Tupel mit:
( ( Eingabezeichen , p1 = 0 , p2 = 0 ) , Ziel-Zustand , Δ p1 , Δ p2 ).
Das Programm simuliert jetzt die 2-Zählermaschine, die als codiertes Tupel
eingegeben wurde. Der Startzustand muss am Anfang stehen, daher ist q auf 0
gesetzt ( aktueller Zustand ). a wird gesetzt über e( d0( z ) ) .
Skizze:
Das Programm wird initialisiert mit q = 0 und geht dann in eine Hauptschleife:
Hilfsvariable(Übergangsfunktionen) wird gesetzt: f( dq( z ) )
a wird gesetzt mit e( dq( z ) )
Gibt es ein Eingabezeichen so wird c = e( x ) und x = f( x ) gesetzt.
Gibt es keins ( x = 0 ):
Die Ausgabe wird gesetzt ( nach Wunsch, auf a oder auf < p1 , p2 > ),
wenn das Programm bei nicht-akzeptieren nicht halten soll, dann gehe in
Endlosschleife.
( c , p1 = 0 , p2 = 0 ) wird codiert und der Reihe nach mit den Übergangsfunktionen
d.h. e( Übergangsfunktion[ Schleifenvariable ] ) verglichen,
bei Übereinstimmung werden Δ p1 , Δ p2 ausgelesen d2,3( f( Ü...) ) und
angewendet.
Q wird auf den Zielzustand gesetzt d1( f( Ü...) ).

–
–
Fazit:
Die Klassen der GOTO- / WHILE- / Turing-berechenbaren Funktionen sind
gleich.
Die Klasse der (totalen) WHILE-berechenbaren Funktionen enthält die Klasse der
LOOP-berechenbaren Funktionen als echte Teilmenge.
Proseminar Theoretische Informatik - WS07/08 - Prof. Helmut Alt
III.Rekursive Funktionen

Induktive Definition der Klasse der primitiv rekursiven Funktionen:
(1)
Alle Konstanten Funktionen sind primitiv Rekursiv.
(2)
Alle identischen Abbildungen (Projektionen) sind primitiv rekursiv.
(3)
Die Nachfolgerfunktion s(n) = n + 1 auf den natürlichen Zahlen ist primitiv rekursiv.
(4)
Funktionen, die durch Einsetzung (Komposition) von primitiv rekursiven Funktionen entstehen,
sind primitiv rekursiv.
(5)
Funktionen die durch “primitive Rekursion” aus primitiv rekursiven Funktionen entstehen, sind
primitiv rekursiv.
D.h. So eine Funktion f erfüllt das Gleichungssystem:
f( 0 , ... ) = g ( ... )
f( n + 1 , ... ) = h( f( n , ... ) , ... )
Mit primitiv rekursiven Funktionen g und h.
Hier geht also generell um Funktionen von Nk nach N.
Die Funktionen unter 1. bis 3. werden Basisfunktionen genannt.
•
–
–
Feststellungen:
Addition , Multiplikation und modifizierte Subtraktion sind primitiv rekursiv (siehe Vorlesung).
Primitiv rekursive Funktionen sind total.
•
Satz:
Die Klasse der primitiv rekursiven Funktion stimmt genau mit der Klasse der LOOPberechenbaren Funktion überein.
[ Beweis weiter unten ]
•
Natürliche Zahlen (Hilfsmittel für Beweis):
Wir benutzen eine Funktion, die zwei natürliche Zahlen bijektiv auf eine abbildet.
Diese geht auf Cantor zurück („Abzählen von N2 über die Diagonalen“) und wird auch
Cantorsche Paarungsfunktion genannt (Wikipedia).
c( x , y ) = ( x + y + 1 ) * ( x + y ) / 2 + x
[ Folie / Tafel zur Veranschaulichung]
[Beweis der Bijektivität hier nicht ]
c ist primitiv rekursiv ( Addition , Multiplikation ).
Dieses ermöglicht uns sofort beliebig viele natürliche Zahlen in einer zu kodieren,
und zwar durch Mehrfachanwendung von C, für ein k+1-Tupel:
< n0 , n1 , ... , nk > := c( n0 , c( n1 , ... , c( nk , 0 ) ... ) )
Also immer c( Argument , Kodierung des Rests durch c ).
Da c bijektiv ist, können wir unter der Bedingung, dass c(x,y) = n gelte,
die Umkehrfunktionen e und f zu c benutzen:
e( n ) = x und f( n ) = y, also c( e(n) , f(n) ) = n.
Und damit auch die Umkehrfunktionen d0, ..., dk für ein n = < n0 , n1 , ... , nk > angeben, die uns
die Komponenten liefern:
d0(n) = e( n ) = n0
d1(n) = e( f(n) ) = n1
...
dk(n) = e( fk(n) ) = nk [ f wird k-mal angewendet auf n ]
Vorsicht mit dk+1(n), das liefert wieder Null, es ist also eine Prüfung [f(n)=0] nötig, wenn man
nicht weiß, wie lang das Tupel ist, welches in der Zahl n kodiert ist.
(!) Diese Kodierung hat die Folge, dass man k-stellige Funktionen als einstellige Funktion
darstellen kann, so dass es überhaupt erst sinnvoll ist Berechenbarkeit allgemein mit Funktionen
auf den Natürlichen Zahlen beschreiben zu wollen.
Wichtige Feststellung:
Die Umkehrfunktionen von c ist auch primitiv rekursiv!
Daraus folgt auch schnell , dass di primitiv rekursiv sind, und wir haben ein gutes Hilfsmittel für
folgende Beweise.
–
Begriff der primitiven Rekursivität auf Prädikate erweitern (zunächst einstellig, mehrstellig ist
aber plausibel), eigentlich über die charakteristische Funktion ( schreiben Prädikat stattdessen).
Für ein einstelliges Prädikat P( x ) definieren wir den beschränkten max-Operator,
q( n ) := max{ x ≤ n | P( x ) }
Falls das Maximum nicht existiert soll q(n) einfach Null liefern, damit q total ist.
q( 0 ) = 0
q( n + 1 ) = q( n ) + P( n + 1 ) * ( n + 1 – q( n ) )
[Dieses entspricht: falls P(n + 1 ) : q( n + 1 ) = n+1 , sonst: q( n + 1 ) = q( n ) ]
Analog: beschränkter Existenzquantor...
Für ein gegebenes Prädikat P( x ) soll Q( n ) wahr sein ( Q(n)=1 ) ,
genau dann wenn es ein x ≤ n gibt, mit P( x ) :
Q( 0 ) = P( 0 )
Q( n + 1 ) = P( n + 1 ) + Q( n ) - P( n + 1 ) * Q( n )
Q und q sind primitiv rekursiv , wenn die verwendeten Prädikate P auch primitiv rekursiv sind.
Erweiterung auf mehrstellige Prädikate ist denkbar, wenn die weiteren Argumente auch in q/Q
dann vorkommen.
–
Umkehrfunktionen zu e und f angeben:
e´( n , m , k ) = max( x ≤ n | ∃ y ≤ k : c( x , y ) = m }
e( n ) = e´( n , n , n )
f´( n , m , k ) = max( y ≤ n | ∃ x ≤ k : c( x , y ) = m }
f( n ) = f´( n , n , n )
Damit sind e und f primitiv rekursiv.
•
–
–
–
Beweis zum Satz ( primitiv rekursiv <-> LOOP-berechenbar ):
„<-“:
Sei f : Nr -> N LOOP-berechenbar. P sei ein LOOP-Programm, das f berechnet.
Die Programmvariablen von P seien x0 , x1 , ... , xk (k≥r).
Wir zeigen, dass es eine primitiv rekursive Funktion gP : N -> N gibt, die die Wirkung von P auf
die Werte der Programmvariablen beschreibt, wie folgt:
Wenn a0 , a1 , ... , ak die Werte der Programmvariablen vor Ausführung sind, und b0 , b1 , ... , bk
deren Werte nach Ausführung des Programms, so gilt:
gP( < a0 , a1 , ... , ak > ) = < b0 , b1 , ... , bk >
Das ganze induktiv über den Aufbau von P:
Falls P die Form xi := xj ± c, so ist
gP( n ) = < d0( n ) , ... , di-1( n ) , dj( n ) ± c , di+1( n ) , ... , dk( n ) >
Falls P die Form hat Q ; R, so ist gP( n ) = gR( gQ( n ) ).
gQ und gR gibt es nach Induktionsvoraussetzung, daher ist das eine Komposition von primitiv
rekursiven Funktionen.
Falls P die Form LOOP xi DO Q END hat:
Nach I.V. Gibt es eine Funktion gQ, die Q berechnet.
Wir definieren eine Hilfsfunktion h:
h( 0 , x ) = x
h( n + 1 , x ) = gQ( h( n , x ) )
h gibt den Zustand der Programmvariablen x = < x0 , x1 , ... , xk > nach n Anwendungen von Q
wieder, damit ist gP wie folgt gefunden:
gP( n ) = h( di( n ) , n )
Damit können wir f ausdrücken:
f( n1 , ... , nr ) = d0( gP( < 0 , n1 , ... , nr , 0 , ... , 0 > ) ).
(Am Ende stehen k-r Nullen.)
–
–
–
„->“:
Induktiv über den Aufbau der primitiv rekursiven Funktionen:
Basisfunktionen: einfach, fast nach Definition.
Einsetzung: durch Hintereinanderausführung von LOOP Programmen für die anderen primitiv
rekursiven Funktionen, die ja nach Induktionsvoraussetzung existieren. Dabei werden die
Zwischenergebnisse jeweils in separaten Variablen gespeichert ( „Adapter“-Programm ).
f entstand durch primitive Rekursion und hat die Form:
f( 0 , x1 , ... , xr ) = g( x1 , ... , xr )
f( n + 1 , x1 , ... , xr ) = h( f( n , x1 , ... , xr ) , n , x1 , ... , xr )
f kann durch ein LOOP-Programm mit einer Schleife berechnet werden:
y := g( x1 , ... , xr ) ; k := n ; LOOP n DO k := k – 1 ; y := h( y , k , x1 , ... , xr ) END
( Hier verkürzt: für g und h existieren bereits LOOP-Programme, nach I.V. )

Der μ-Operator und die Klasse der μ-rekursiven Funktionen
Sei f eine k+1-stellige Funktion.
Der μ-Operator bildet f auf eine k-stellige Funktion g ab:
g( x1 , ... , xk ) = min{ n | f( n , x1 , ... , xk ) = 0 und f.a. m<n ist f( m , x1 , ... , xk ) definiert }
Zusätzlich soll min Ø als undefiniert gesetzt sein.
Damit können echte partielle Funktionen entstehen!
(Beispiel: Konstante Funktionen die nicht Null sind)
•
Definition ( Klasse der μ-rekursiven Funktionen ):
Die Klasse der μ-rekursiven Funktionen ist die kleinste Klasse von Funktionen,
die die Basisfunktionen ( konstante Funktionen , Projektionen , Nachfolgerfunktion ) enthält
und abgeschlossen ist unter Einsetzung, primitiver Rekursion und Anwendung des μ-Operators.
•
Satz:
Die Klasse der μ-rekursiven Funktionen stimmt genau mit der Klasse der WHILEberechenbaren Funktionen überein ( damit auch mit GOTO- , Turing- ).
Beweis:
Dieser Beweis funktioniert analog zum Beweis für LOOP-Berechenbarkeit gegenüber
primitiver Rekursivität, und führt daher nur das Konzept für die WHILE-Schleife
und den μ-Operator vor:
„<-“:
Falls P ein WHILE-Programm der Form WHILE xi ≠ 0 DO Q END ist,
benutzen wir wieder eine Hilfsfunktion h( n , x ), die den Zustand der Programmvariablen
x = < x1 , ... , xk > nach n Ausführungen von Q wiedergibt:
gP = h( μ( dih )( x ) , x )
[ μ( dih )( x ) gibt gerade die minimale Wiederholungszahl des Programms Q wieder, für die xi
den Wert 0 liefert. ]
„->“:
g sei die durch Anwendung des μ-Operators auf eine Funktion f entstandene Funktion.
Also g = μf, so berechnet das folgende Programm g:
x0 := 0 ; y := f( 0 , x1 , ... , xn ) ;
WHILE y ≠ 0 DO
x0 := x0 + 1 ; y := f( x0 , x1 , ... , xn )
END
( Für f existiert nach I.V. Bereits ein WHILE-Programm )
•
Satz (Kleene):
Für jede n-stellige μ-rekursive Funktion f gibt es zwei (n+1)-stellige, primitiv rekursive
Funktionen p und q, so dass sich f darstellen lässt als:
f( x1 , ... , xn ) = p( x1 , ... , xn , μq( x1 , ... , xn ) )
[ μq ist die n-stellige, durch Anwenden des Operators auf q entstandene Funktion. ]
~ ohne Beweis.
Proseminar Theoretische Informatik - WS07/08 - Prof. Helmut Alt
IV.Die Ackermannfunktion
Ackermann gab 1928 eine Funktion an, die intuitiv berechenbar ist und sogar total,
aber nicht LOOP-berechenbar.
[ siehe letzter Vortrag ]
•
Vereinfachte Form der Definition:
a( 0 , y ) = y + 1
a( x , 0 ) = a( x - 1 , 1 )
a( x , y ) = a( x – 1 , a( x , y -1 ) )
Die Idee war, eine Folge zu kreieren, die wächst, wie etwa: a+b , a*b , ab , ... (...?).
Die Tabelle zeigt für x,y den Wert und die Zahl der benötigten rekursiven Aufrufe.
Am Beispiel a( 4 ,1 ) sieht man gut die Definition und das Wachstum.
a( 4 , 1 ) = a( 3 , a( 4 , 0 ) ) = a( 3 , 13 ) = 65533
a( 4 , 4 ) ist größer als die Zahl der Atome im ganzen Universum (wie war sie noch?).
Zusammenfassung aus dem letzten Vortrag:
•
•
•
•
•
•
•
Die Ackermannfunktion ist total.
Lemma A:
y < a( x , y )
Lemma B:
a( x , y ) < a( x , y + 1 )
Lemma C:
a( x , y + 1 ) ≤ a( x + 1 , y )
Lemma D:
a( x , y ) < a( x + 1 , y )
Allgemeine Monotonie-Eigenschaft:
Für x, x´, y, y´ mit x≤x´ und y≤y´ gilt:
a( x , y ) ≤ a( x´, y´)
Definition:
Wir ordnen jedem LOOP-Programm P eine Funktion fP zu,
so dass gilt:
fP : N -> N , f(n) = max{ ∑i( n´i ) | ∑i( ni ) }
i durchläuft die Indizes der in P vorkommenden Variablen.
ni seien die Werte der im Programm benutzten Variablen xi mit ni=xi und zwar vor
Ausführung des Programms, das ist also die Summe der Eingaben, da alle anderen
Variablen dann Null sind.
n´i seien analog die Variablenwerte nach Ausführung des Programms.
fP(n) berechnet also die maximal mögliche Summe der im Programm
vorkommenden Variablen nach Ausführung, für alle Eingabevarianten, für die die
Summe der Eingabevariablen kleiner oder gleich n ist.
•
•
Lemma E:
Für jedes LOOP-Programm P gibt es eine Konstante k ,so dass für alle n gilt:
fP(n) < a( k , n )
Satz:
Die Ackermannfunktion ist nicht LOOP-berechenbar.
[ Beweiskonzept: annehmen a sei LOOP-berechenbar, P berechne g(n) := a(n,n)...
LEMMA E: g(n) ≤ fP(n) < a(k,n) => g(k) ≤ fP(k) < a(k,k) = g(k) Widerspruch.]
•
•
Satz:
Es gibt totale, WHILE-berechenbare Funktionen, die nicht LOOP-berechenbar
sind.
(Beweis zum obigen Satz:) Die Ackermannfunktion ist WHILE-berechenbar.
Wir benutzen eine Mischung aus WHILE-Sprache und dem Formalismus der μrekursiven Funktionen, also eine Art von Pseudocode, von dem wir aber sicher
sein können, dass wir ihn zu einem streng formal gesehen korrekten WHILEProgramm umformen können, da wir wissen, dass “ μ-rekursiv = WHILE =
GOTO = TM” gilt.
Damit wird auch deutlich, dass die verschiedenen Konzepte als Hilfsmittel taugen,
um z.B. Diesen Beweis übersichtlich gestalten zu können.
Als erstes benutzen wir einen Stack, den wir über die primitiv rekursiven
Funktionen c( x , y ) und ihre Umkehrfunktionen e( n ) und f( n ) realisieren (aus
dem letzten Abschnitt “rekursive Funktionen”)
Ein Stack s mit sei realisiert durch eine natürliche Zahl als Datum:
n = < n1 , n2 , ... , nk > = c( n1 , c( n2 , ... , C( nk , 0 ) ... ) )
Folgende Operationen werden auf dem Stack implementiert:
INIT( s ) durch n := 0, wobei n die Variable ist, die den Stackinhalt trägt
PUSH( a , s ) durch n := c( a , n )
POP( s ) durch r := e( n ) ; n := f( n ) , wobei r die Ausgabe ist.
Diese Operationen sind mit primitiv rekursiven Funktionen umgesetzt also in
WHILE-Code umsetzbar.
Die Variablennamen werden lesefreundlich angegeben,
daher das “Schlüsselwort” INPUT, das angibt, welches die Eingabevariablen sind,
OUTPUT , das die Ausgabevariable bestimmt und das Programm beendet.
INPUT( x , y );
INIT( stack );
PUSH( x , stack );
PUSH( y , stack );
WHILE size( stack ) ≠ 1 DO
y := POP( stack )
x := PUSH( stack )
IF x = 0 THEN PUSH( y + 1 , stack )
ELSEIF y = 0 THEN PUSH( x – 1 , stack ); PUSH( 1 , stack )
ELSE PUSH( x – 1 , stack ); PUSH( x , stack ) ; PUSH( y – 1 , stack );
END { IF }
END { WHILE }
result := POP( stack );
OUTPUT( result )
Die Abfrage size( stack ) ≠ 1 wird mit f( n ) ≠ 0 umgesetzt (WHILE-gerecht).
Dieses Programm ist in WHILE-Syntax umwandelbar, entweder durch elementare
Schritte ( x := y + c -> WHILE y ≠ 0 DO x := x + 1 END ; x := x + c ), oder durch
Umwandlung der primitiv rekursiven Funktionen c , e , f in WHILE-Programme.
Streng genommen bleibt noch zu zeigen, dass dieses Programm wirklich die
Ackermannfunktion berechnet, aber es ist schnell klar, dass es die vereinfachte
Definition direkt umsetzt.
Proseminar Theoretische Informatik - WS07/08 - Prof. Helmut Alt
V. Ausklang
Es gibt Funktionen, die man nicht angeben kann, obwohl sie wohldefiniert sind.
Fleißiger Biber -> Entscheidbarkeit
Quellen:
Uwe Schöning
Theoretische Informatik-kurzgefaßt
Spektrum Akad. Verlag 1995, 2. Auflage und 4.Auflage 2003 ( korrigierter Nachdruck )
http://www.wikipedia.org ( aktuell , 10.01.2008 )
( Vorträge aus der Veranstaltung )
Herunterladen