TIA: Berechenbare Zahlenfunktionen (Lernziele KE2)

In dieser Kurseinheit gibt es zwar nicht viele Lernziele, aber lasst euch nicht täuschen: sie sind wichtig. Die Beiträge zu diesem Thema, die ich auch im passenden Kontext in diesem Beitrag verlinkt habe sind:

Starten wir aber einfach wieder anhand der Lernziele und kämpfen uns da durch.

Lernziel 1

Angabe und Erläuterung der Definition und der Eigenschaften der Cantorschen Tupelfunktion

Zum Glück habe ich bereits hier einen langen Beitrag zum Thema erstellt und wiederhole mich deswegen nicht, sondern springe sofort zur Antwort.

Antwort zum Lernziel: mit der Cantorschen Tupelfunktion ist es uns möglich einem \(n\)-Tupel mit Elementen aus \(\mathbb{N}\) eine einzige, eindeutige Zahl aus \(\mathbb{N}\) zuzuordnen (Reduktion). Durch diese bijektive Abbildung von \(\mathbb{N}^n\rightarrow\mathbb{N}\) können wir uns auf die Berechnung von einstelligen Funktionen beschränken.

Einen weiteren prominenten Platz hat die Cantorsche Tupelfunktion bei dem Vergleich von Mengen: zwei Mengen sind gleichmächtig wenn man zwischen ihnen eine Bijektion herstellen kann.

Lernziel 2

Erläuterung der Abschlusseigenschaften berechenbarer Zahlenfunktionen

Ich bin mir hier gerade etwas unschlüssig: im Skript ist nicht mehr viel zu dem Thema drin, als der Satz zur Substitution, primitive Rekursion, \(\tilde{\mu}\) und der Umkehrfunktion. Vielleicht dann am besten an ein paar Beispielen?

Substitution

Zunächst haben wir zwei ggf. partielle Funktionen \(g:\subseteq\mathbb{N}^m\rightarrow\mathbb{N}\) und ggf. partielle Funktionen \(h_1\) bis \(h_m\) (d.h. so viele Funktionen wie der Definitionsbereich von \(g\) Parameter hat), mit \(h_1,…,h_m\subseteq\mathbb{N}^k\rightarrow\mathbb{N}\). Alle Funktionen sind berechenbar. Dann ist auch die Substitution \(Sub(g,h_1,…,h_m)\) berechenbar.

\(Sub(g,h_1,…,h_m)\subseteq\mathbb{N}^k\rightarrow\mathbb{N}\) ist definiert durch \(Sub(g,h_1,…,h_m)(x):=g(h_1(x),…,h_m(x))\)

Viel Zeug, oder? Keine Sorge, ist ganz easy.

Beispiel

\(g(x,y)=x+y\)

\(h_1(x,y,z)=x+y\)

\(h_2(x,y,z)=x+z\)

Die Substitution sieht nun so aus:

\(Sub(g,h_1,h_2)(x,y,z)=g(h_1(x,y,z),h_2(x,y,z))\).

Belegen wir unsere Funktion mit Werten: \(x=2,y=3,z=4\)

\(\begin{array} Sub(g,h_1,h_2)(2,3,4)&=g(h_1(2,3,4),h_2(2,3,4))\\&=g(5,h_2(2,3,4))\\&=g(5,6)\\&=11\end{array}\)

War doch trivial, oder (das wollte ich immer schon mal sagen!)?

Primitive Rekursion

Das ist ein klein wenig ekeliger als die Substitution, aber wir bekommen das gleich noch klein.

Auch hier haben wir zwei Funktionen  \(g:\mathbb{N}^k\rightarrow\mathbb{N}\) und  \(h:\mathbb{N}^{k+2}\rightarrow\mathbb{N}\), die berechenbar sind (Achtung: sie müssen auch total sein, denn sonst können wir nicht über diese „rekursivieren“). Wenn das der Fall ist, dann ist auch \(Prk(g,h)\) berechenbar.

\(Prk(g,h)\) ist definiert als:

\(f(x,0) = g(x)\)

\(f(x,y+1)=h(x,y,f(x,y))\)

Für alle \(x\in\mathbb{N}^k\) und \(y\in\mathbb{N}\). Man sieht bereits jetzt, dass wir rekursiv arbeiten. Noch deutlicher wird es an einem Beispiel.

Beispiel

\(g(x,z)=x+z\)

\(h(a,b,x,z)=a+b+x+z\)

Nehmen wir einfach mal \(y=3\) (\(3\)-malige Rekursion) und \(x=2,z=3,a=4,b=5\).

\(\begin{array} Prk(g,h)(2,3)&=f(2,3,3)\\&=h(2,3,2,f(2,3,2))\\&=h(2,3,2,(h(2,3,1,f(2,3,1))))\\&=h(2,3,2,(h(2,3,1,(h(2,3,0,f(2,3,0))))))\\&=h(2,3,2,(h(2,3,1,(h(2,3,0,g(2,3))))))\\&=h(2,3,2,(h(2,3,1,(h(2,3,0,5)))))\\&=h(2,3,2,(h(2,3,1,10)))\\&=h(2,3,2,16)\\&=23\end{array}\)

Versuchen wir das mit \(y=2\):

\(\begin{array} Prk(g,h)(2,2)&=f(2,3,2)\\&=h(2,3,1,f(2,3,1))\\&=h(2,3,1,(h(2,3,0,f(2,3,0))))\\&=h(2,3,1,(h(2,3,0,5)))\\&=h(2,3,1,10)\\&=16\end{array}\)

Viel Text, aber sicherlich gut nachzuvollziehen. Wir loopen und also durch die Funktionen mit einer festen Anzahl an Schleifen! In diesem Beitrag habe ich die primitive Rekursion etwas detaillierter betrachtet.

Und was ist wenn wir keine feste Anzahl an Schleifen haben können? Dann hilft uns unser \(\mu\)-Operator.

\(\tilde{\mu}\)

Ist \(h\) mit \(h:\subseteq\mathbb{N}^{k+1}\rightarrow\mathbb{N}\) berechenbar, so ist es auch \(\tilde{\mu}(h)\). Wie ihr seht, auch hier ist \(h\) total. Was ist \(\tilde{\mu}\)?! \(\tilde{\mu}:\subseteq\mathbb{N}^{k}\rightarrow\mathbb{N}\). Es ist definiert durch:

\(\tilde{\mu}(h)(x)=\mu y[h(x,y)=0]\)

What the… Entspannung, Entspannung! Das ist nichts weiter als unsere \(\mu\)-Rekursion. Ich suche gerade im Skript ob wir das bereits irgendwo auf den vorherigen Seiten hatten… witzig. Hatten wir nicht. Das ist wirklich die erste Erwähnung. Und dann gleich ins kalte Wasser. Nicht schlimm, denn unser \(\tilde{\mu}\) ist nichts weiter, als der Operator, der es uns erlaubt auch Dinge zu berechnen, wo wir die genaue Anzahl an Schleifendurchläufen noch nicht wissen.

Und zwar indem wir unserer zu berechnende Funktion so weit umformen, dass wir beim Ende der Berechnung auf eine Nullstelle stoßen. Ich werde das nicht groß ausführen, sondern verweise ganz frech auf meinen alten Beitrag zum Thema.

Umkehrfunktion \(f^{-1}\)

Das schwerste zum Schluss. Hier gehen wir davon aus, dass \(f\) injektiv ist (wichtig!). Wir beschränken uns auch auf einstellige Funktionen. Warum? Na? Na? Genau: aufgrund der Cantorschen Tupelfunktion können wir das. Wenn diese Funktion also berechenbar ist, so ist es auch ihre Umkehrfunktion. Klingt logisch. Ist es auch.

Während für \(f\) Totalität und Injektivität gefordert ist, können wir bei \(f^{-1}\) bei einer ggf. partiellen Funktion rauskommen. Das liegt daran, dass wir in der Bildmenge von \(f\) evtl. Elemente haben, die wir mit unserer Funktion nicht „treffen“. Diese landen aber für \(f^{-1}\) dennoch in der Definitionmenge, sind aber nicht definiert. Damit ist \(f^{-1}\) eben partiell.

Beispiel

\(f(x) = x+5\) mit der Umkehrfunktion \(f^{-1}(\tilde{x})=\tilde{x}-5\). Setzen wir \(x=10\) und \(\tilde{x}=f(x)\), so haben wir:

\(f(10)=15\)

\(f^{-1}(15)=10\)

Passt.

Damit haben wir die Abschlusseigenschaften für berechenbare Zahlenfunktionen durch.

Antwort zum Lernziel: Funktionen, die sich aus den Funktionen Substitution, primitive Rekursion, \(\mu\)-Rekursion und der inversen Funktion zusammensetzen sind ebenfalls berechenbar.

Für die Substitution können die Funktionen auch partiell sein, für de primitive und \(\mu\)-Rekursion sind wir jedoch auf totale Funktionen angewiesen, da wir sonst nicht durch die Schleifendurchläufe kommen. Während wir bei der primitiven Rekursion zwingend die Anzahl der Schleifendurchläufe kennen müssen, ist es uns bei der \(\mu\)-Rekursion durch das „Nullsetzen“ der Funktion nicht wichtig. Es bricht eben ab wenn die Nullstelle gefunden ist. Wie lange es dauert wissen wir nicht.

Ersteres ist das Äquivalent zur \(LOOP\)-Schleife („tue etwas genau \(x\) Mal“), während die \(\mu\)-rekursion einer \(WHILE\)-Schleife („tue etwas bis…“) entspricht.

Für die inverse Funktion benötigen wir neben einer totalen Funktion \(f\) jedoch auch die Eigenschaft der Injektivität für \(f\), da wir nur auf maximal ein Element aus der Bildmenge abbilden dürfen, da sonst die Umkehrabbildung auf zwei unterschiedliche Elemente abbilden würde.

Lernziel 3

Definition der WHILE-Programme, Erläuterung ihrer Semantik und Angabe des Zusammenhangs zu berechenbaren Funktionen

Letztes Lernziel in dieser Kurseinheit. In diesem Beitrag sind die Details bereits herausgearbeitet, ich kümmere mich daher nur um die Definition und Semantik aus dem Skript. Das Fazit kann man wortwörtlich eig. auch für die Antwort zum Lernziel übernehmen. Also kümmern wir uns um die Syntax und die Semantik.

\(R_i+\) und \(R_i-\) sind \(WHILE\)-Programme

Dabei addiert \(R_i+\) im \(i\)-ten Registereine \(1\), \(R_i-\) subtrahiert eine \(1\) im \(i\)-ten Register.

IF \(R_i=0\) THEN \(P\) ELSE \(Q\)

Wir gehen davon aus, dass \(P\) und \(Q\) selbst \(WHILE\)-Programme sind.

WHILE \(R_i\neq 0\) DO \(P\)

Kennen wir auch schon. Solange das Register \(R_i\) nicht \(0\) ist, wird \(P\) ausgeführt.

Zusätzlich dazu haben wir noch unser kleines Tau \(\tau\). Dadurch weisen wir unseren oben beschriebenen Kommandos tatsächliche Funktionen auf der Datenmenge \(D=\mathbb{N}^{\mathbb{N}}\) zu, die wir bereits verbal beschrieben haben (sie ist die uns bekannte Datenmenge der Registermaschinen). Als Beispiel nehmen wir \(R_i+\) und \(R_i-\):

\(\tau(R_i+)(a_0,a_1,…):=(a_0,a_1,…,a_i+1,…,)\)

\(\tau(R_i-)(a_0,a_1,…):=(a_0,a_1,…,a_i-1,…,)\)

Wir können einem kompletten \(WHILE\)-Programm mittels \(\tau\) ebenfalls eine Ausgabe auf der Datenmenge \(D\) beschaffen: \(\tau(P)\). Und damit haben wir dann die komplette Registerausgabe unserer Maschine, die das Programm \(P\) berechnet. Damit schaffen wir es, dass unser Programm auf der Eingabecodierung einer Registermaschine operieren kann und uns auch eine Ausgabecodierung der Registermaschine liefert (siehe vorheriger Beitrag).

Noch eine kleine Definition für die \(WHILE\)-Berechenbarkeit

Wir nennen eine Funktion \(f:\mathbb{N}^k\rightarrow\mathbb{N}\) \(WHILE\)-berechenbar genau dann wenn gilt: \(f=AC(\tau(P)(EC))\)

Ähnliches Spiel wie im Beitrag zu KE1. Wir haben unterschiedliche Datenmengen für \(\tau\) und für die Flussdiagramme der Registermaschinen. Mit \(EC\) und \(AC\) wandelt wir diese nur ineinander um.

Das führt uns direkt zum Zusammenhang zu den berechenbaren Funktionen.

Beispiel

Funktion \(f(x,y)=x+y\) (operiert auf \(\mathbb{N}^2\rightarrow\mathbb{N}\))

Dazu haben wir ein \(WHILE\)-Programm \(P\), welches uns die Funktion \(f\) berechnet. Es operiert auf \(\mathbb{N}^3\rightarrow\mathbb{N}^3\)

R0=R1;
WHILE R2 NOT 0 DO
  R0+;
  R2-;
END;

Wir können auch ein Flussdiagramm \(F\) einer Registermaschine \(M\) angeben, dass uns die gleiche Funktion raushaut.

Während das Flussdiagramm ebenfalls auf \(\mathbb{N}^3\rightarrow\mathbb{N}^3\) operiert, haben wir bei der Registermaschine jedoch \(\mathbb{N}^3\rightarrow\mathbb{N}\).

while-machine

Damit gilt \(f=\tau(P)=f_M=f_F\). Oder doch nicht? Spielen wir das doch einfach mal mit \(x=2,y=3\) durch:

\(f(2,3)=5\)

\(\tau(P)(0,2,3)=(5,2,0)\)

\(f_M(0,2,3)=5\)

\(f_F(0,2,3)=(5,2,0)\)

Okay, die Berechnung ist anscheinend in Ordnung. Aber die Ausgabe von z.B. \(\tau(P)(0,2,3)=(5,2,0)\) ist sicher nicht gleich \(f_M(0,2,3)=5\) (siehe die Definition der Eingabe- und Ausgabecodierung von Registermaschinen im Lernziel 4 von Beitrag zu KE1).

Hier kommen unsere Funktionen \(EC\) und \(AC\) bei allen drei Berechnungsarten ins Spiel, die unsere Eingabe und unsere Ausgabe entsprechend umformen, so dass sie passen.

Definieren wir also z.B. \(AC\) für \(\tau(P)\) neu: \(AC(x,y,z)=x\), so haben wir

\(\begin{array}{}AC(\tau(P)(0,2,3))&=AC(5,2,0)\\&=5\end{array}\)

Damit würde \(f_M=AC(\tau(P))\) wieder passen.

Das spannende also daran ist, dass es durchaus vorkommen kann, dass unser Flussdiagramm, unsere Registermaschine oder unser \(WHILE\)-Programm nicht alle auf einer Datenmenge operieren, sondern alle auf unterschiedlichen Mengen (warum auch immer). Um aber zu sagen, dass z.B. \(\tau(P)=f_M\) gilt, müssen wir sie evtl. umgestalten. Für \(\tau(P)=f_F\) brauchen wir das hingegen – wie wir oben gesehen haben – nicht.

Lange Rede, kurzer Sinn:

Zu jedem \(WHILE\)-Programm gibt es ein Flussdiagramm einer Registermaschine mit \(\tau(P)=f_F\)

Zu jedem Flussdiagramm einer Registermaschine gibt es ein \(WHILE\)-Programm mit \(f_F=\tau(P)\) mit einer \(WHILE\)-Schleife.

Damit gilt: die Funktion \(f\) ist \(WHILE\)-berechenbar genau dann wenn sie Register-berechenbar ist.

Antwort zum Lernziel: die \(WHILE\)-Programme bestehen aus bedingten Anweisungen (\(IF\)) und Schleifen (\(WHILE\)), sowie Anweisungen zum Register hoch- oder runterzählen und Hintereinanderausführungen von Anweisungen.

Der Zusammenhang zu berechenbaren Funktionen kommt daher, dass man zu jedem \(WHILE\)-Programm eine Registermaschine angeben kann, die die selbe Funktion berechnet und zu jeder Registermaschine ein \(WHILE\)-Programm existiert.

\(WHILE\)-Programme sind mächtiger als \(LOOP\)-Programme: man kann \(LOOP\)-Schleifen mit \(WHILE\)-Schleifen nachbilden, aber nicht anders herum, da man die Anzahl der Schleifendurchläufe im Vorfeld nicht bei jeder \(WHILE\)-Schleife kennt.

Die \(LOOP\)-Anwendungen sind das Äquivalent zur primitiven Rekursion, während die \(WHILE\)-Programme erst mit dem \(\mu\)-Operator (durch „Nullsetzen“ der Funktion) nachgebildet werden können.

TIA: Flussdiagramme, Maschinen und berechenbare Zahlenfunktionen (Lernziele KE1, Update 2)

Update: Einwand von Felix aufgenommen.

Update: Marion hat einen Fehler bei der Berechnung der \(ES\) gefunden. Danke!

Update: Eine Registermaschine kann nicht die Subtraktion, sondern nur die arithmetische Differenz.

Update: Hinweis darauf, dass das erste Register bei der Eingabecodierung \(EC\) für eine Maschine belegt werden kann (wir haben keine Einschränkungen), aber nicht durch die Eingabecodierung für eine Registermaschine!

Als ich die ersten Beiträge zur Kurseinheit 1 von Teil A der theoretischen Informatik geschrieben habe, orientierte ich mich an den Themen. Entstanden sind damit die Unterbeiträge:

Irgendwann, gegen Ende der Kurseinheit 5 habe ich gemerkt, dass es vielleicht sinniger ist anhand der Lernziele zu gehen. Das bringt etwas mehr Struktur in die Beiträge und wirft einen roten Faden durch das Skript.

Als ich dann bei Kurseinheit 7 von Teil B angelangt war, hat sich mein Entschluss gefestigt die alten Beiträge ebenfalls zu überarbeiten. Was ich hiermit also tun möchte. 

Lernziel 1

Angabe und Erläuterung der Definition des Flussdiagramms

Ich würde vorschlagen, wir definieren wieder und erklären die Definition Zeile für Zeile: das Flussdiagramm ist ein 4-Tupel \(F=(Q,D,\sigma,q_o)\)

\(Q\) als Markenmenge
\(D\) als Datenmenge
\(q_0\) als Startmarke
\(\sigma\)

zu jeder Marke gehört entweder eine Zuweisung

\((f,p)\)

oder eine Verzweigung/Test

\((S,p,p^{‚})\)

Beispiel gefällig?

Beispiel: \(F_1=(Q,D,\sigma,q_0)\) mit

\(Q=\{1,2,3,4\}\): wir haben nur vier Marken im Flussdiagramm

\(D=\mathbb{N}^2\): wir operieren auf max. zweistelligen, natürlichen Zahlen

\(q_0=1\): die Anfangsmarke ist \(1\)

Und nun kommt unsere Funktion, die jeder Marke eine Operation zuweist. Das macht das Flussdiagramm nämlich erst aus.

\(\sigma(1)=(y=1,2)\) (Zuweisung)

\(\sigma(2)=(y>x,4,3)\) (Verzweigung/Test)

\(\sigma(3)=(y=y+1,2)\) (Zuweisung)

\(\sigma(4)=HALT\) (Haltemarke)

Und wenn wir \(F_1\) auf Papier bringen:

flussdiagramm_KE1

Nicht so schwer, oder? Was tut das Flussdiagramm \(F_1\)? Nichts besonderes: es zählt nur \(y\) hoch bis es größer ist als \(x\). Nicht besonders sinnvoll, aber kurz (ich bin faul, sorry) und anschaulich.

Antwort zum Lernziel: Ein Flussdiagramm besteht aus einer Marken- und Datenmenge, einer Startmarke und einer Funktion, die jeder Marke aus der Markenmenge eine Zuweisung oder eine Verzweigung zuordnet.

Lernziel 2

Angabe und Erläuterung der Semantik des Flussdiagramms

Semantik? Bedeutung! Das Flussdiagramm \(F\) definiert nämlich nichts anderes als eine Funktion \(f_F\), die aus den Parametern (oben waren es \(x\) und \(y\)) in einer Endkonfiguration (so denn es eine gibt) ein Ergebnis generiert. Die Semantik eines Flussdiagramms gliedert sich in folgende Begriffe:

  • Konfiguration: z.B. Anfangs- und Endkonfiguration sowie Zwischenkonfigurationen
  • Einzelschrittfunktion: Übergang in einem Berechnungsschritt von einer Konfiguration in eine andere
  • Schrittzahlfunktion: \(div\) wenn es zu einer Eingabe keine Endkonfiguration gibt oder die Anzahl der Schritte, die zu einer Eingabe bis zur Endkonfiguration geführt haben
  • Gesamtschrittfunktion: Endkonfiguration zu einer Eingabe. Wenn sie existiert. Ansonsten \(div\).
  • Semantik: Das Ergebnis

Beispiel: \(F_1=(Q,D,\sigma,q_0)\) (von oben)

Sagen wir, wir starten \(F_1\) mit \(x=2\) und \(y=0\).

  • Anfangskonfiguration: \((1,(2,0))\) und Endkonfiguration: \((4,(2,3))\) (Zwischenkonfigurationen lasse ich mal weg)
  • Einzelschrittfunktion: z.B. \(ES(1,(2,0)=(2,(2,1))\). Nach einem Schritt befinden wir uns in Marke \(2\) und haben nur \(y=1\) gesetzt. Würden wir aber z.B. \(ES^6(1,(2,0))\) nehmen, so müssten wir fünf Berechnungsschritte im Flussdiagramm ausführen und wären bei
\(\begin{align}ES^6(1,(2,0))&=ES^5(2,(2,1))\\&=ES^4(3,(2,1))\\&=ES^3(2,(2,2))\\&=ES^2(3,(2,2))\\&=ES(2,(2,3))\\&=(4,(2,3))\end{align}\)

(Update: Nach Einwand von Felix korrigiert. Danke!)

  • Schrittzahlfunktion: \(SZ(1,(2,0))=7\). Nach \(7\) Schritten kommen wir, ausgehend von der Anfangskonfiguration \((1,(2,0))\) in die Endkonfiguration \((4,(2,3))\).
  • Gesamtschrittfunktion: \(GS(1,(2,0))=(4,(2,3))\). Die Endkonfiguration zur Anfangskonfiguration \((1,(2,0))\) ist eben \((4,(2,3))\) und damit unsere Gesamtschrittfunktion.
  • Semantik: \(f_{F_1}(2,0)=(2,3)\). Das Ergebnis der harten Arbeit unseres Flussdiagrsmms.

Auch nicht viel schwerer als Lernziel 1.

In einem alten Beitrag habe ich das bereits für Registermaschinen aufgegriffen.

Antwort zum Lernziel: das Flussdiagramm definiert eine Funktion \(f_F\) (die Semantik des Flussdiagramms) auf der Datenmenge des Flussdiagramms, welche von einer Anfangskonfiguration (Startmarke, Eingabe) nach endlich vielen Schritten zu einer Endkonfiguration (Endmarke, Ausgabe) führt. Oder auch nicht.

Die Belegung der Variablen im Flussdiagramm ist das Ergebnis der Berechnung, es gilt dann \(f_F(Eingabe)=Ausgabe\). Gibt es zu der Eingabe kein Ergebnis, so ist \(f_F(Eingabe)=div\).

Lernziel 3

Angabe und Erläuterung der Definition und Semantik einer Maschine

Kommen wir nun zu den Maschinen. Warum reicht uns das Flussdiagramm von oben nicht? Weil die Datenmenge \(D\) des Flussdiagramms meistens nicht mit der Definitions- oder Bildmenge unserer durch das Flussdiagramm darzustellenden Funktion übereinstimmen wird. Wir brauchen hier also zusätzlich noch ein paar andere Dinge: eine Maschine ist ein 5-Tupel mit \(M=(F,X,Y,EC,AC)\).

\(F\) ist ein Flussdiagramm (siehe oben)

\(X\) ist eine Eingabe- und \(Y\) eine Ausgabemenge.

\(EC:X\rightarrow D\): Eingabecodierung. Es ist nichts weiter als eine Funktion, die aus einem Element aus \(X\) eine Eingabecodierung aus \(D\) für unser Flussdagramm ausgibt. Under Flussdiagramm nimmt nämlich keine Elemente aus \(X\), sondern nur aus \(D\) entgegen.

\(AC:D\rightarrow Y\): Ausgabecodierung. Ebenfalls nur eine Funktion, die hier jedoch zu einem Element aus \(D\), der Datenmenge des Flussdiagramms ein Element aus der Ausgabemenge \(Y\) zuweist.

Damit berechnet \(M\), unsere Maschine also eine Funktion \(f_M=AC(f_F(EC))\)

Das letztere erschließt sich wahrscheinlich nicht sofort. Aber wie können das an einem Beispiel etwas anschaulicher gestalten.

Achtung (Update): während wir bei der Eingabecodierung bei einer Maschine es zulassen, dass wir das erste Register belegen, tun wir das keinesfalls bei einer Registermaschine! Das ist bei der Initialbelegung durch die Funktion \(EC\) nicht zulässig, denn es fungiert als Ergebnisregister und ist initial immer mit \(0\) belegt!

Beispiel: Maschine \(M\) mit dem Flussdiagramm von oben, \(x=2\) und den zusätzlichen Mengen und Funktionen

\(X,Y=\mathbb{N}\): die Mengen unserer Eingabe- und Ausgabecodierungen für unsere Maschine sind nicht mehr Tupel (\(\mathbb{N}^2\)), wie sie das Flussdiagramm brauchst, sondern bestehen nur noch aus einem einzigen Element.

\(EC:X\rightarrow D\) mit \(EC(x)=(x,0)\).

\(AC:D\rightarrow Y\) mit \(AC(x,y)=y\)

Wir können unsere Maschine \(M\) also einfach nur mit \(x\) füttern und erhalten durch die Eingabefunktion \(EC\) auch unser \(y\) für die Eingabe in unser Flussdiagramm. Ähnliches gilt für die Ausgabecodierung: unser Flussdiagramm gibt uns \((x,y)\) als Ausgabe heraus, was wir mit der Funktion \(AC\) in \(y\) umwandeln.

Setzen wir also \(x=2\) mal ein:

\(EC(x)=(2,0)\). Damit füttern wir unser Flussdiagramm und bekommen als Ausgabe des Flussdiagramms \(f_F=(2,3)\). Das werfen wir in unsere Ausgabecodierung \(AC(2,3)=3\) und haben die Ausgabe unserer Maschine \(f_M\).

Der letzte Punkt unserer Maschine mit ausgefülltem \(x=2\) bedeutet also:

\(\begin{align}f_M&=AC(f_F(EC(2)))\\&=AC(f_F(2,0))\\&=AC(2,3)\\&=3\end{align}\)

That’s it.

Antwort zum Lernziel: eine Maschine tut für uns also nichts anderes, als es uns zu ermöglichen nicht mehr auf der Datenmenge \(D\) des Flussdiagramms operieren zu müssen. Durch die „Eingabecodierungs-Funktion“ \(EC\) können wir aus einer beliebigen Eingabemenge \(X\) in die Datenmenge \(D\) des Flussdiagramms abbilden und so unser Flussdiagramm mit einer passenden Eingabe füttern.

Mittels der „Ausgabecodierungs-Funktion“ \(AC\) gelingt uns auch der Rückweg aus der Ausgabe des Flussdiagramms (die ja ein Element aus \(D\) ist) in die Ausgabemenge \(Y\).

Damit ist die Semantik einer Maschine die Funktion \(f_M\subseteq X\rightarrow{Y}\), die mit Hilfe von \(EC\) und \(AC\) die Mengen \(X\) und \(Y\) von, bzw. in die Datenmenge \(D\) für das Flussdiagramm überführt. Es gilt somit \(f_M=AC(f_F(EC))\) wenn wir von einer Anfangskonfiguration zu einer Endkonfiguration kommen. Ansonsten ist \(f_M=div\).

Lernziel 4

Angabe und Erläuterung der Definition einer Registermaschine

Kommen wir nun zu den Registermaschinen. Den eig. Stars dieser Kurseinheit. Ich hatte bereits in diesem Beitrag etwas zum Thema geschrieben. Es macht also keinen Sinn das hier noch einmal zu Wiederholen. Daher: bitte dort nachlesen und wiederkommen 😉

Update: nicht vergessen, die Eingabecodierung für eine Registermaschine belegt nicht das erste Register \(R_0\)!

\(EC^{(k)}(x1,…,x_k):=(0,x_1,…,x_k)\)

Ebenso ergeht es uns mit der Ausgabecodierung: uns ist egal, was in den anderen Registern steht, wir finden nur das erste Register spannend:

\(AC(a_0,a_1,…):=a_0\)

Antwort zum Lernziel: die Registermaschinen bestehen aus Registern mit durch die Eingabecodierung induzierten Registerbelegungen und der „Ausgabecodierungs-Funktion“, welche uns das 1. Register (Register \(R_0\)) der Registermaschine als Ausgaberegister festlegt. Dadurch ist es uns nur möglich die Register \(R_1-R_m\) mittels Eingabecodierung zu belegen.

Die Registermaschine verfügt nur über drei Grundoperationen: Addition von \(1\), Subtraktion arithmetische Differenz und Test auf \(0\).

Lernziel 5

Angabe und Erläuterung der Definition einer verallgemeinerten Registermaschine

Auch die Erläuterungen zur verallgemeinerte Registermaschine sind hier zu finden. Daher kümmern wir uns nur um das Lernziel.

Antwort zum Lernziel: Durch die verallgemeinerte Registermaschine ist es uns möglich weitaus mehr Operationen als die Grundoperationen Addition und Subtraktion von \(1\), sowie Test auf \(0\) durchzuführen.

Diese Operationen werden jedoch aus den Grundoperationen zusammengesetzt und erlauben es uns Registerinhalte miteinander zu vergleichen oder Registern Werte zuzuweisen, die durch Funktionen berechnet wurden (wobei Registerinhalte der Registermaschine selbst als Parameter dieser Funktion fungieren können).

Lernziel 6

Angabe der Definition einer berechenbaren Zahlenfunktion

Ich gebe mal ganz frech die Definition aus dem Skript wieder:

Eine Funktion \(f:\subseteq\mathbb{N}^k\rightarrow\mathbb{N}\) heißt berechenbar wenn es eine \(k\)-stellige Registermaschine \(M\) gibt, die sie berechnet, so dass gilt: \(f=f_M\).

Das ist eine der zentralen Definitionen im Teil A der theoretischen Informatik und findet sich auch häufig als Frage in den Protokollen der mündlichen Prüfung wieder: diese Definition sagt uns, dass wir eine Funktion dann berechenbar nennen können, wenn wir eine Registermaschine angeben, die sie berechnet und im Endeffekt auf die Grundoperationen Addition und Subtraktion von \(1\) sowie Test auf \(0\) zurückführt.

Beispiel: \(f(x,y)=x+y\)

Wollen wir also nachweisen, dass die Funktion \(f\) berechenbar ist, so müssen wir eine Registermaschine inkl. Flussdiagramm angeben, dass uns die gleiche Ausgabe in Register \(R_0\) liefert, wie die Funktion \(f\).

Für Details und den Beweis, dass die Addition zweier Register (\(f(x,y)=x+y\)) berechenbar ist, empfehle ich euch hier den alten Beitrag zum Thema.

Antwort zum Lernziel: Wir können eine Funktion mit \(k\)Parametern also berechenbar nennen wenn wir sie mit einer \(k\)-stelligen Registermaschine und der Grundoperationen innerhalb eines Flussdiagramms berechnen können., so dass gilt: \(f = f_M\).

Lernziel 7

Zeigen, dass eine Funktion berechenbar ist

Das Thema wurde ebenfalls hier detaillierter besprochen und gezeigt wie man eine Funktion (das angesprochene Beispiel mit der Addition zweier Register) als berechenbar nachweist. Ich begnüge mich also hier mit dem letzten Lernziel dieser Kurseinheit.

Antwort zum Lernziel: Um zu beweisen, dass eine Funktion berechenbar ist, muss man diese am Ende nur mit den Grundoperationen Subtraktion und Addition von \(1\) und Test auf \(0\) abbilden können.

Hat man z.B. die Addition mittels dieser Grundoperationen nachgebildet und mittels vollständiger Induktion bewiesen, dass sie für alle Werte das gewünschte Ergebnis liefert, so kann man diese komplexe Operation nun in seiner verallgemeinerten Registermaschine für weitere Beweise anderer Funktionen nutzen.

Z.B. kann man ausgehend von den Grundoperationen die Addition zweier Register \(x+y\) beweisen und anschließend mit der Addition den Beweis der Multiplikation \(x\cdot y\) angehen. Mit der Multiplikation gelingt uns dann das Beweisverfahren für die Exponentialfunktion \(x^y\), usw.

Die Lernziele von Kurseinheit 1 haben wir damit durch. Noch 6 und der Kurs ist komplett 😉 Bei Fehler wie immer: ASAP in die Kommentare damit falsches Zeug nicht so lange online bleibt!