4.3 Primitive Rekursion kann nicht alles: die Ackermann-Funktion
In den vorhergehenden Teilkapiteln haben wir gesehen, dass Sie allerhand
mit primitiv rekursiven Funktionen implementieren können. Darunter Dinge,
die komplexere Rekursion oder Schleifen mit mehreren lokalen Variablen zu brauchen
scheinen (wie die Fibonacci-Zahlen), ja sogar Dinge, die über den
Bereich der natürlichen Zahlen hinausgehen, wie zum Beispiel das Sortieren
eines beliebig langen Arrays. Kernpunkt war die Erkenntnis, dass wir
komplexere "Datenstrukturen" wie Paare von natürlichen Zahlen
als eine natürliche Zahl codieren können und somit
der primitiven Rekursion zugänglich machen können.
Es liegt also Nahe, zu vermuten, dass primitive Rekursion bereits
den Berechenbarkeitsbegriff zufriedenstellend formalisiert: das also
alles "Berechenbare" auch primitiv rekursiv sei.
Der Wikipedia-Artikel
über die Ackermann-Funktion schreibt, dass der deutsche Mathematiker
David Hilbert dies auch vermutete. Im Jahre 1926
jedoch definierte Wilhelm Ackermann
eine Funktion, die "offensichtlich berechenbar", jedoch nicht primitiv rekursiv ist.
Die Funktion, die wir heute die Ackermann-Funktion nennen, ist allerdings
eine vereinfachte Version, die 1935 von der ungarischen Mathematikerin
Rózsa Péter gefunden wurde (obwohl
der letztere Artikel das Jahr 1955 nennt).
Definition 4.3.1(Péter-Ackermann-Funktion).
Manchmal sehen Sie in der Literatur auch die äquivalente, vielleicht lesbarere Definition:
Für festes schreiben wir auch für die ein-parametrige
Funktion
Es lohnt sich, diese Funktion für kleine Werte von zu untersuchen.
Aus der Definition geht unmittelbar hervor, dass
ist.
Für rechnen wir
Soweit schaut die Funktion nicht besonders beindruckend aus. bekommen iwr
nach dem gleichen Schema:
wir fangen also mit an und zählen mal eine 2 drauf. Wir erhalten
, also
wird etwas unangenehmer, weil wir keine gute Intuition haben, was
" mal verdoppeln und jedes Mal 3 draufzählen" ergibt. Mit 1 beginnend erhalten wir also
Das ist schon genug, um eine Vermutung zu formulieren:
und diese dann per Indution zu beweisen.
Die Péter-Ackermann-Funktion ist berechenbar
Theorem 4.3.2
Sei gegeben. Die Funktion ist primitiv rekursiv.
Beweis.
Wir verwenden Induktion über . Für gilt und somit
. Sei nun also . Wir sehen, dass
Ich schreibe nun anstatt als Parameter, um mich der Notation
der primitiven Rekursion anzunähern. Wir wissen bereits, dass
primitiv rekursiv ist. Wir können also schreiben als
Wir können also wählen. Was ist ?
Der Startwert soll sein, wir haben aber kein , bzw. dies
ist der "leere Vektor". Wir brauchen eine Funktion
, die null Argumente nimmt und zurückgibt. Also:
. Wir könnten sogar noch frecher sein
und schreiben,
einfach mal hintereinander; diesen Wert also hard-coden.
Allerdings ist die erste Variante einfacher, und somit
Wir sehen also: jedes , betrachtet als Funktion mit einem
Eingabeparameter, ist primitiv rekursiv.
Wir haben also gezeigt, dass jedes primitiv rekursiv ist.
Heißt das auch, dass die zwei-parametrige Funktion berechenbar ist?
In der primitiven Rekursion haben wir keine Möglichkeit, den Index als
Eingabewert zu lesen und dann aus dem unendlichen Array primitiv rekursiver Funktionen
den Eintrag auszulesen.
Aber berechenbar in einem ganz allgemeinen Sinn? Diese Frage können wir
zu diesem Zeitpunkt nicht formal beantworten, weil wir den Begriff
allgemeiner Berechenbarkeit noch gar nicht definiert haben. Es ist allerdings
an der Definition von nichts magisches, und intuitiv würden wir
sagen, dass es klar berechenbar ist; so wie wir ja auch leicht eine Funktion
implementieren könnten, die berechnet.
Die Ackermann-Péter-Funktion ist nicht primitiv rekursiv
Theorem 4.3.3
Die Ackermann-Péter-Funktion ist nicht primitiv rekursiv.
Mein Beweis paraphrasiert im Wesentlichen den auf
planetmath.org
Die Aussage verstehen. Beachten Sie, dass Theorem 3.3.3 nicht mit
Theorem 3.3.2 im Widerspruch steht. Theorem 3.3.3 besagt, dass primitiv rekursiv ist,
für jedes . Die Zahl ist hier also Teil der Aussage, nicht
Eingabeparameter der Funktion; die Funktion hat nur einen Eingabeparameter.
Theorem 3.3.3 hingegen macht eine Aussage über eine Funktion mit
zwei Eingabeparametern, und ist nun einer dieser beiden.
Anders ausgedrückt: sie können zwar jedes einzelne in der "Programmiersprache" der
primitiven Rekursion implementieren, allerdings brauchen Sie für jedes neuen Code.
Sie können keinen Code schreiben, der, gegeben , den Code für irgendwie
implizit produziert und dann mit aufruft. Jedenfalls nicht in der Programmiersprache
der primitiven Rekursion.
Beweisidee.
Für jedes feste ist die ein-parametrige Funktion
primitiv rekursiv und
kann als Funktion mit verschachtelten for-Schleifen betrachtet werden.
Es wird sich herausstellen, dass in gewisser Weise die am schnellsten wachsende
aller solcher Funktionen ist. Wäre also primitiv-rekursiv, dann könnten
wir es mit verschachtelten for-Schleifen berechnen; was ein
Widerspruch ist, weil bereits schneller wächst als jede primitiv-rekursive
Funktion mit verschachtelten Schleifen.
Beweis.
Wir brauchen eine robuste Definition, was es heißt, dass eine Funktion
schneller wächst als
.
Definition. 4.3.4
Sei und . Die Funktion
majorisiert, wenn
für alle gilt.
Wir schreiben auch kurzerhand .
Sei zum Beispiel und .
Dann majorisiert die Funktion nicht;
sei beispielsweise , dann ist
aber . Allerdings
majorisiert die Funktion , ganz allgemein weil
für alle gilt.
Wir werden nun zeigen, dass jede primitiv rekursive Funktion
von einem majorisiert wird; hierbei ist wichtig, dass der Index
als Konstante betrachtet wird, also von der "Bauweise" von abhängen darf, nicht
aber von den Eingabewerten, die bekommt.
Lemma 4.3.5
Für jede primitiv rekursive Funktion gibt
es ein , so dass von majorisiert wird.
Aus diesem Lemma folgt dann, dass selbst nicht
primitiv rekursiv sein kann. Wäre es dies, dann gäbe es ja ein
, so dass das ein-parametrige die zwei-parametrige Funktion
majorisieren würde:
für alle Werte und somit insbesondere
was offensichtlich ein Widerspruch ist, da beide Seiten gleich sind.
Wir werden nun das Lemma beweisen. Wir verwenden Induktion über die
Weise, in der die Funktion konstruiert worden ist, also über die
Anzahl der Comp- und PrimRec-Kombinatoren, die wir zum Bau von gebraucht haben.
Im folgenden schreiben wir, wenn wir einen Vektor aus
natürlichen Zahlen haben, oft .
Induktionsbasis. Wir betrachten wir die Basisfunktionen
.
Wir wissen bereits, dass ist, also .
Leider majorisiert also nicht. Wie steht es mit
? Es gilt , und somit ist
wobei wir die Schreibweise verwenden.
Wir schlussfolgern: majorisiert jede Basisfunktion.
Induktionsschritt.
Wenn keine Basisfunktion ist, dann wurde mittels Komposition oder
primitiver Rekursion konstruiert. Für jeden Fall führen wir eine getrennte Rechnung
durch.
Komposition:,
für primitiv rekursive Funktionen . Jede dieser Funktionen
wurde mit weniger Kombinatoren konstruiert; somit wird jede dieser Funktionen
von einem majorisiert:
. Für einen Eingabevektor
schreiben wir und rechnen:
Wir setzen nun . Dann ist der obige Wert höchstens:
Schlussendlich behaupte ich, dass gilt:
Also: majorisiert .
Primitive Rekursion:, also
wobei bereits mit weniger Kombinatoren konstruierte primitiv rekursive Funktionen
sind. Daher gibt es ein mit und .
Behauptung 4.3.6, wobei
.
Beweis.
Wir verwenden Induktion über . Wenn ist,
dann gilt
Wenn ist, dann gilt
üü
Somit ist die Behauptung bewiesen.
Die Behauptung reicht aber noch nicht, da
die rechte Seite der Ungleichung
eine Funktion in zwei
Parametern ist: und , wir aber für das zu beweisende Lemma aber
eine Funktion in einem Parameter brauchen, nämlich .
Sei also . Dann gilt
Es gilt also und das Lemma ist bewiesen.
In der Vorlesung am 7. Mai 2024 hatte ich den Grad einer primitiv-rekursiven Funktion
definiert. Das ist in etwa die "Verschachtelungstiefe" von . Betrachten wir beispielsweise
die Funktion und dröseln auf, wie wir diese
als primitiv-rekursive Funktion konstruiert haben: