Zum Inhalt springen

Kurs:Einführung in die mathematische Logik (Osnabrück 2021)/Vorlesung 18/latex

Aus Wikiversity

\setcounter{section}{18}

Wir kehren nun zur Ausgangsfrage dieses Kurses zurück, ob es eine Maschine geben kann, die mathematische Probleme \zusatzklammer {etwa aus der Zahlentheorie} {} {} löst. In den vorhergehenden Vorlesungen haben wir eine formale Sprache entwickelt, in der man solche nichttrivialen Probleme präzise formulieren kann. Ferner haben wir gesehen, wie ein formaler Beweis \zusatzklammer {eine Ableitung im Prädikatenkalkül} {} {} in dieser Sprache aussieht, und dass es nach dem Vollständigkeitssatz für jeden mathematisch beweisbaren Ausdruck der Sprache auch einen formalen Beweis gibt.

In dem vorgestellten Ableitungskalkül der Prädikatenlogik sind die Start\-tautologien und die Ableitungsregeln übersichtlich strukturiert. Zwar nehmen die Starttautologien häufig Bezug auf beliebige Ausdrücke \zusatzklammer {und Variablen} {} {} der Sprache, doch da die Ausdrücke prinzipiell auflistbar sind, gilt dies auch für die Starttautologien. Daher kann man sich auch gut einen Algorithmus vorstellen, der nach und nach alle formalen Beweise und somit auch alle formal-beweisbaren Ausdrücke ausgibt. Ein andersgelagertes Problem ist die Fragestellung, ob es ein Entscheidungsverfahren für die Prädikatenlogik gibt, ob es also ein algorithmisches Verfahren gibt, dass zu einem gegebenen Ausdruck überprüfen kann, ob es dafür einen formalen Beweis gibt oder nicht.

Wenn wir bisher von Algorithmen gesprochen haben, so haben wir dabei immer an intuitiv durchführbare Algorithmen gedacht, ohne ein konkretes Durchführungsmodell vor Augen zu haben. In dieser Vorlesung stellen wir die Arbeitsweise einer konkreten algorithmischen Maschine vor, der Registermaschine, die wir von nun an als mechanische Realisierung unserer intuitiven Vorstellung von Algorithmen auffassen wollen.






\zwischenueberschrift{Registermaschinen}






\bild{ \begin{center}
\includegraphics[width=5.5cm]{\bildeinlesung {Alan_Turing_cropped.jpg} }
\end{center}
\bildtext {Statue von Alan Turing (1912-1954).} }

\bildlizenz { Alan Turing cropped.jpg } {Jon Callas} {Compro} {Commons} {CC by sa 2.0} {}

Es gibt verschiedene Möglichkeiten, eine deterministisch arbeitende Maschine zu modellieren. Wir arbeiten hier mit Registermaschinen, da diese einem wirklichen Computer ziemlich nahe kommen und daher etwas vertrauter sind als Turingmaschinen oder rekursive Funktionen \zusatzklammer {wobei letztere vom mathematischen Standpunkt her eleganter sind} {} {.}




\inputdefinition
{}
{

Unter einer \definitionswort {Registermaschine}{} versteht man eine endliche Folge von Registern
\mathl{R_1, R_2 , \ldots , R_m}{} \zusatzklammer {oder Speichern} {} {,} deren Inhalt jeweils eine natürliche Zahl ist, die durch eine endliche \zusatzklammer {eventuell leere} {} {} Folge von Strichen repräsentiert wird.

Ein \definitionswort {Programm für eine Registermaschine}{} ist eine endliche durchnummerierte Folge von Befehlen
\mathl{B_1, B_2 , \ldots , B_h}{,} wobei es für die einzelnen Befehle
\mathl{B_j}{} die folgenden Möglichkeiten gibt. \aufzaehlungfuenf{
\mathl{i +}{} \zusatzklammer {erhöhe den Inhalt des Registers $R_i$ um $1$, d.h. um einen Strich} {} {.} }{
\mathl{i -}{} \zusatzklammer {reduziere den Inhalt des Registers $R_i$ um $1$, d.h. ziehe einen Strich ab; wenn der Inhalt leer ist, so lasse ihn leer} {} {.} }{$C (i j)$ \zusatzklammer {wenn das $i$-te Register leer ist, so gehe zum Befehl $B_j$, andernfalls zum nächsten Befehl} {} {.} }{Drucke \zusatzklammer {drucke den Inhalt des ersten Registers} {} {.} }{Halte an. } Dabei muss
\mavergleichskette
{\vergleichskette
{ i }
{ \leq }{ m }
{ }{ }
{ }{ }
{ }{ }
} {}{}{} für alle in einer Programmzeile adressierten Register und
\mavergleichskette
{\vergleichskette
{ j }
{ \leq }{ h }
{ }{ }
{ }{ }
{ }{ }
} {}{}{} für alle adressierten Befehlszeilen gelten. Die letzte Befehlszeile $B_h$ ist ein Haltebefehl und sonst gibt es keinen Haltebefehl.

}

Die beiden ersten Befehle nennt man \stichwort {Inkrementierung} {} bzw. \stichwort {Dekrementierung} {.} Der dritte Befehl ist der \stichwort {Abfragebefehl} {} oder die \zusatzklammer {bedingte} {} {} \stichwort {Sprunganweisung} {.} Es folgen \stichwort {Druckbefehl} {} \zusatzklammer {\stichwort {Ausgabebefehl} {}} {} {} und \stichwort {Haltebefehl} {.}

Ein Durchlauf eines Programmes für eine Registermaschine arbeitet die Befehle des Programmes der Reihe nach ab und zwar unter den jeweiligen zum Bearbeitungszeitpunkt vorgefundenen Registerbelegungen. Wenn die aktuelle Programmzeile ein bedingter Sprungbefehl
\mathl{C(ij)}{} ist, so wird, falls die Bedingung zu diesem Zeitpunkt erfüllt ist \zusatzklammer {also falls das Register $R_i$ leer ist} {} {,} zur Programmzeile $B_j$ gewechselt. Wenn die Endzeile $B_h$, also der Haltebefehl erreicht wird, so ist die Bearbeitung beendet.

Die Belegung \zusatzklammer {oder der Inhalt} {} {} des Registers $R_i$, die sich im Laufe des Programmdurchlaufs mehrfach ändern kann, werden wir häufig mit $r_i$ bezeichnen. Dies ist stets eine natürliche Zahl. Wenn das Register $R_i$ leer ist, so ist sein Inhalt
\mavergleichskette
{\vergleichskette
{r_i }
{ = }{0 }
{ }{ }
{ }{ }
{ }{ }
} {}{}{.}

Die Möglichkeiten einer Registermaschine scheinen auf den ersten Blick recht bescheiden zu sein. Man sieht aber recht schnell, dass man aus diesen wenigen Befehlen Programmabschnitte zusammensetzen kann, die zunehmend komplexere Befehle ausführen. Komplexe Befehle, von denen schon gezeigt wurde, dass sie sich mit Hilfe der Grundbefehle realisieren lassen, werden ohne weiteren Kommentar weiterverwendet.

Man sagt, dass ein Programm \stichwort {korrekt} {} ist, wenn es das tut, was es tun soll. Wenn beispielsweise gesagt wird, dass ein Programm zwei Zahlen addiert, so wird die Korrektheit dadurch bewiesen, dass man eben durch Analyse des Programmcodes nachweist, dass bei beliebiger Belegung der beiden Register, deren Inhalte addiert werden sollen, das Programm schließlich anhält und in ein weiteres Register wirklich die Summe der beiden Zahlen gespeichert ist. Ein Korrektheitsnachweis ist häufig eine mühevolle Kleinarbeit mit aufwändigen Fallunterscheidungen, in den natürlich auch mathematische Überlegungen eingehen, wie z.B. bei der Addition die Eigenschaft, dass
\mavergleichskette
{\vergleichskette
{ s+t }
{ = }{ s+(t-1)+1 }
{ }{ }
{ }{ }
{ }{ }
} {}{}{} ist, was einen induktiven Korrektheitsbeweis ermöglicht. Wir werden diese Korrektheitsüberlegungen häufig kurz halten.






\zwischenueberschrift{Programmbeispiele}

Wir beschreiben einige Programme bzw. Programmabschnitte für Registermaschinen. Wenn man Programme aus schon entwickelten Programmabschnitten zusammensetzt, so ändern sich natürlich die absoluten Befehlsnummern im Programm, was wir aber ignorieren werden.


\inputbeispiel{}
{

Einen unbedingten Sprung \zusatzklammer {ein \anfuehrung{Go to-Befehl}{}} {} {} zu einer bestimmten Programmzeile, der also nicht von einer Abfrage abhängt, kann man dadurch realisieren, dass man ein neues Register $R_{k}$ hinzunimmt, das von keiner anderen Programmzeile adressiert wird und dessen Inhalt auf $0$ gesetzt wird. Dann bewirkt der Befehl
\mathl{C(k j)}{,} dass zur $j$-ten Programmzeile gewechselt wird, da der Inhalt des Registers $R_{k}$ im gesamten Programmverlauf gleich $0$ bleibt.


}




\inputbeispiel{}
{

Ein Programm soll sämtliche natürlichen Zahlen der Reihe nach ausdrucken. Dazu brauchen wir eine Registermaschine mit zwei Registern \mathkor {} {R_1} {und} {R_2} {,} die zum Start beide leer sind. Das zweite Register bleibt unverändert und wird nur für den unbedingten Sprungbefehl verwendet. Die Haltezeile wird nie erreicht. \aufzaehlungvier{Drucke }{$1+$ }{Gehe zu $1$ }{Halte an }


}




\inputbeispiel{}
{

Das Register $R_i$ soll geleert werden. Dies geschieht durch das folgende Programm. \aufzaehlungvier{
\mathl{C(i,4)}{} }{$i-$ }{Gehe zu $1$ }{Halte an }


}






\inputbemerkung
{}
{

Wir erlauben, dass bei einer Registermaschine die Anfangsbelegung der Register von außen festgelegt wird. Man könnte aber auch festlegen, dass die Anfangsbelegung stets die Nullbelegung ist, ohne die Berechnungsmöglichkeiten der Registermaschine einzuschränken. Dann kann man die eigentlich gewünschte Anfangsbelegung dadurch erreichen, dass man dem Programm ein \anfuehrung{Belegungsprogramm}{} voranstellt, das den einzelnen Registern $R_i$ durch die $s_i$ Befehle
\mathl{i+ , \ldots , i+}{} die gewünschte Belegung $s_i$ zuweist.

Man könnte auch erstmal ein \anfuehrung{Entleerungsprogramm}{} vorschalten, das alle Register leert und daran anschließend die Belegung durchführt, doch muss man für den Entleerungsvorgang, der nach Beispiel 18.4 einen unbedingten Sprungbefehl verwendet, zumindest ein leeres Register zur Verfügung haben.

}

Wenn der Registerinhalt $r_i$ um eine natürliche Zahl $k$ erhöht werden soll, also $k$-fach direkt hintereinander inkrementiert werden soll, so schreiben wir dafür auch
\mathl{i + \cdots +}{} mit $k$ Pluszeichen.




\inputbeispiel{}
{

Es soll mit einer \definitionsverweis {Registermaschine}{}{} festgestellt werden, ob der Inhalt $r_i$ des Registers $R_i$ größer oder gleich dem Inhalt $r_j$ des Registers $R_j$ ist. Dazu reserviert man das leere Register $R_k$ \zusatzklammer {$i,j,k$ seien paarweise verschieden} {} {} und baut einen Programmabschnitt der folgenden Art. \aufzaehlungsieben{
\mathl{C(j,6)}{} }{
\mathl{j-}{} }{
\mathl{C(i,7)}{} }{
\mathl{i-}{} }{Gehe zu $1$ }{
\mathl{k+}{} }{Halte an } Wenn dieser Programmabschnitt abgelaufen ist, so steht im Register $R_k$ der Wert \mathkor {} {r_k=1} {oder} {r_k=0} {,} je nachdem, ob
\mavergleichskette
{\vergleichskette
{r_i }
{ \geq }{r_j }
{ }{ }
{ }{ }
{ }{ }
} {}{}{} ist oder nicht, und zwar unabhängig davon, ob man damit die Eingangsdaten oder Zwischendaten, wenn das Programm den ersten Befehl abarbeitet, meint. Die Korrektheit dieses Programms beruht darauf, dass
\mavergleichskette
{\vergleichskette
{r }
{ \geq }{s }
{ }{ }
{ }{ }
{ }{ }
} {}{}{} genau dann gilt, wenn
\mavergleichskette
{\vergleichskette
{r-1 }
{ \geq }{s-1 }
{ }{ }
{ }{ }
{ }{ }
} {}{}{} ist. Dies ermöglicht einen Induktionsbeweis.


}




\inputbeispiel{}
{

Wir wollen überprüfen, ob die Inhalte von zwei Registern \mathkor {} {R_i} {und} {R_j} {} übereinstimmen. Dazu kann man das Programm aus Beispiel 18.6 einfach abändern zu \aufzaehlungneun{
\mathl{C(j,6)}{} }{
\mathl{j-}{} }{
\mathl{C(i,9)}{} }{
\mathl{i-}{} }{Gehe zu $1$ }{
\mathl{C(i,8)}{} }{Gehe zu $9$ }{
\mathl{k+}{} }{Halte an } Bei Gleichheit erhält man
\mavergleichskette
{\vergleichskette
{ r_k }
{ = }{ 1 }
{ }{ }
{ }{ }
{ }{ }
} {}{}{,} bei Ungleichheit
\mavergleichskette
{\vergleichskette
{ r_k }
{ = }{ 0 }
{ }{ }
{ }{ }
{ }{ }
} {}{}{.}


}

In den obigen beiden Beispielen wurde die Antwort im Register $R_k$ \zusatzklammer {in der Form \mathkor {} {0} {oder} {1} {} abgespeichert} {} {.} Der Druckbefehl nimmt aber immer Bezug auf $R_1$. Daher ist es nötig, Registerinhalte in andere Register zu verschieben.


\inputbeispiel{}
{

Wir wollen den Registerinhalt $r_i$ des Re\-gis\-ters $R_i$ in das Register $R_j$ übertragen \zusatzklammer {unabhängig von dessen Inhalt} {} {.} Dies leistet das folgende Programm. \aufzaehlungsechs{Leere $R_j$ }{
\mathl{C(i,6)}{} }{
\mathl{i-}{} }{
\mathl{j+}{} }{Gehe zu $2$ }{Halte an }


}

Bei diesem Programm wird im Laufe der Durchführung das Ausgangsregister der Übertragung leer gemacht. Dies ist nicht immer erwünscht, häufig möchte man den Inhalt eines Registers kopieren und sich den Inhalt zugleich merken.


\inputbeispiel{}
{

Wir wollen den Registerinhalt $r_i$ des Registers $R_i$ in das Register $R_j$ übertragen \zusatzklammer {unabhängig von dessen Inhalt} {} {,} ohne $R_i$ zu leeren. Dazu brauchen wir ein drittes Register $R_k$ und das folgende Programm. \aufzaehlungneun{Leere $R_j$ }{Leere $R_k$ }{
\mathl{C(i,8)}{} }{
\mathl{i-}{} }{
\mathl{j+}{} }{
\mathl{k+}{} }{Gehe zu $3$ }{Übertrage den Inhalt von $R_k$ nach $R_i$ }{Halte an } Hier wird zwar im Laufe des Programms der Inhalt von $R_i$ verändert, zum Schluss wird der ursprüngliche Inhalt aber wieder hergestellt.


}




\inputbeispiel{}
{

Die beiden Registerinhalte $r_i$ \zusatzklammer {von $R_i$} {} {} und $r_j$ \zusatzklammer {von $R_j$} {} {} sollen addiert werden, wobei die Summe zum Schluss in $R_k$ stehen soll \zusatzklammer {es seien \mathlk{i,j,k}{} paarweise verschieden} {} {.} Dies leistet das folgende Programm. \aufzaehlungsieben{Leere $R_k$ }{Übertrage $r_i$ nach $R_k$ }{
\mathl{C(j,7)}{} }{$j-$ }{$k+$ }{Gehe zu $3$ }{Halte an }


}

Mit der Addition und der Kopie von Inhalten kann man auch den Inhalt eines Registers zu einem anderen Register dazuaddieren. Dies kann man natürlich auch einfach direkt realisieren.


\inputbeispiel{}
{

Die beiden Registerinhalte $r_i$ (von $R_i$) und $r_j$ (von $R_j$) sollen multipliziert werden, wobei das Produkt zum Schluss in $R_k$ stehen soll \zusatzklammer {es seien \mathlk{i,j,k}{} paarweise verschieden} {} {.} Dies leistet das folgende Programm mit dem Hilfsregister $R_\ell$. \aufzaehlungsieben{Leere $R_k$ }{Übertrage den Inhalt von $R_i$ nach $R_\ell$ ohne $R_i$ zu leeren }{
\mathl{C(j,7)}{} }{Addiere den Inhalt von $R_\ell$ zu $R_k$ hinzu }{$j-$ }{Gehe zu $2$ }{Halte an } Die Korrektheit dieses Programms beruht auf
\mavergleichskette
{\vergleichskette
{ r \cdot s }
{ = }{ (r-1) s + s }
{ }{ }
{ }{ }
{ }{ }
} {}{}{;} für das Produkt $rs$ muss man $r$-mal $s$ mit sich selbst addieren.


}




\inputbeispiel{}
{

Es soll überprüft werden, ob der Registerinhalt $r_t$ \zusatzklammer {von $R_t$} {} {} den Registerinhalt $r_j$ \zusatzklammer {von $R_j$} {} {} teilt. Falls ja soll das Programm $1$ ausgeben, andernfalls $0$. Dies leistet das folgende Programm mit den Hilfsregistern \mathkor {} {R_k} {und} {R_\ell} {} \zusatzklammer {für Teilprogramme braucht man noch weitere Hilfsregister} {} {.} Das Ausgaberegister $R_1$ soll zu Beginn leer sein. \aufzaehlungneun{Leere $R_\ell$ }{Berechne
\mathl{r_t \cdot r_\ell}{} und schreibe das Ergebnis in $R_k$ \zusatzklammer {ohne
\mathl{r_t,r_\ell}{} zu verändern} {} {} }{Bei
\mavergleichskette
{\vergleichskette
{ r_k }
{ > }{ r_j }
{ }{ }
{ }{ }
{ }{ }
} {}{}{} gehe zu 8 }{Bei
\mavergleichskette
{\vergleichskette
{ r_k }
{ = }{ r_j }
{ }{ }
{ }{ }
{ }{ }
} {}{}{} gehe zu 7 }{$\ell +$ }{Gehe zu 2 }{$1+$ }{Drucke }{Halte an }


}




\inputbeispiel{}
{

Es soll überprüft werden, ob der Registerinhalt $r_j$ \zusatzklammer {von $R_j$} {} {} eine \definitionsverweis {Primzahl}{}{} ist. Falls ja soll das Programm $1$ ausgeben, andernfalls $0$. Dies leistet das folgende Programm mit dem Hilfsregister $R_t$ \zusatzklammer {für Teilprogramme braucht man noch weitere Hilfsregister} {} {.} Das Ausgaberegister $R_1$ soll zu Beginn leer sein. \aufzaehlungzweireihe {\itemfuenf {Leere $R_t$ }{
\mathl{t +}{} }{
\mathl{t +}{} }{Wenn
\mathl{r_t = r_j}{,} so gehe zu 8 }{Wenn
\mathl{r_t \geq r_j}{,} so gehe zu 9\zusatzfussnote {Die Programmzeile (5) ist nur für
\mathl{r_j =0,1}{} von Bedeutung} {.} {} } } {\itemfuenf {Wenn $r_j$ von $r_t$ geteilt wird, so gehe zu 9 }{Gehe zu 3 }{$1+$ }{Drucke }{Halte an } }


}




\inputbeispiel{}
{

Es sollen die geraden Zahlen $\geq 4$ daraufhin überprüft werden, ob sie die Eigenschaft in der Goldbachvermutung erfüllen, also ob sie die Summe von zwei Primzahlen sind. Das Programm soll die Ausgabe $0$ machen, falls ein Gegenbeispiel gefunden wurde. Dies leistet das folgende Programm mit den Registern $R_n$, $R_k$ und $R_i$, die alle zu Beginn auf $0$ gesetzt seien. Auch das Ausgaberegister $R_1$ soll zu Beginn leer sein. Wir testen ab $6$, um uns auf ungerade Primzahlen als Summanden beschränken zu können. \aufzaehlungzweireihe {\itemsechs {
\mathl{n + +++}{} }{
\mathl{n + +}{} }{Leere $R_k$ }{
\mathl{k +}{} }{
\mathl{k + +}{} }{Wenn
\mavergleichskette
{\vergleichskette
{ r_k }
{ \geq }{ r_n }
{ }{ }
{ }{ }
{ }{ }
} {}{}{,} so gehe zu 12 } } {\itemsieben {Wenn $r_k$ eine Primzahl ist, so gehe zu 9 } {Gehe zu 5 } {Berechne
\mathl{r_n - r_k}{,} schreibe das Ergebnis in $R_i$ } {Wenn $r_i$ eine Primzahl ist, so gehe zu 2 } {Gehe zu 5 } {Drucke } {Halte an } }


}