Ackermannfunktion
Die Ackermannfunktion, 1928 von dem Mathematiker Wilhelm Ackermann definiert, spielt auf Grund ihres starken Wachstums in der Berechenbarkeitstheorie eine wichtige Rolle. Sie ist berechenbar, aber nicht primitiv-rekursiv, denn sie wächst schneller als jede primitiv-rekursive Funktion.
Definition
Heute ist die folgende, von Hans Hermes vereinfachte Version gebräuchlich:
Beispiele:
Es gibt noch eine weiter vereinfachte Variante :
- (y mal)
Implementation
In der Programmiersprache C lässt sich die Ackermannfunktion wie folgt implementieren:
int ack(int x, int y) { if (y == 0) return x + 1; else if (x == 0) return ack(x - 1, 1); else return ack(x - 1, ack(x, y - 1)); }
Rekursiv, aber nicht primitiv-rekursiv
Die Ackermannfunktion wächst extrem schnell; schon ist ungefähr . Dieses starke Wachstum wird ausgenutzt, um zu beweisen, dass die (z.B. WHILE-) berechenbare Funktion schneller wächst als jede primitiv-rekursive Funktion und deshalb selbst nicht primitiv-rekursiv ist, damit auch nicht LOOP-berechenbar. Das heißt, dass man sie nicht in der einfachen Programmiersprache LOOP aufschreiben kann, in der nur Addition, Wertzuweisungen und endlich oft durchlaufene Schleife (Programmierung)n möglich sind.
Da die Ackermannfunktion WHILE-berechenbar ist, ist sie auch µ-rekursiv. Es ist jedoch nicht einfach, eine explizite µ-rekursive Definition anzugeben.
Wertetabelle
x\y | 0 | 1 | 2 | 3 | 4 | y |
---|---|---|---|---|---|---|
0 | 1 | 2 | 3 | 4 | 5 | x + 1 |
1 | 2 | 3 | 4 | 5 | 6 | y + 2 |
2 | 3 | 5 | 7 | 9 | 11 | 2y + 3 |
3 | 5 | 13 | 29 | 61 | 125 | 8×2y − 3 |
4 | 13 | 65533 | ( Terme) | |||
5 | 65533 | Fehler beim Parsen (SVG (MathML kann über ein Browser-Plugin aktiviert werden): Ungültige Antwort („Math extension cannot connect to Restbase.“) von Server „http://localhost:6011/de.wikipedia.org/v1/“:): {\displaystyle ack(4,ack(5,2))} | ||||
6 |
Intuitive Erklärung
Intuitiv ist die erste Zeile der Wertetabelle (genauer: die Werte von Fehler beim Parsen (SVG (MathML kann über ein Browser-Plugin aktiviert werden): Ungültige Antwort („Math extension cannot connect to Restbase.“) von Server „http://localhost:6011/de.wikipedia.org/v1/“:): {\displaystyle ack(0,y)} ) einfach eine Liste aller natürlichen Zahlen, und die angewandte mathematische Operation ist die Addition einer 1 zu . Alle folgenden Zeilen enthalten einfach Anweisungen, in dieser Zeile einen Wert zu suchen. Im Falle der Zeile vereinfacht sich diese Anweisung dahingehend, den Wert in der Zeile zu suchen, aber diese Vereinfachung ist schon etwas schwieriger herzuleiten – zum Beispiel:
ack(1, 2) = ack(0, ack(1,1)) = ack(0, ack(0, ack(1,0))) = ack(0, ack(0, 2)) = ack(0, 3) = 4
Wir betrachten nun einen komplexeren Fall, nämlich , den ersten Funktionswert, der so groß ist, dass er praktisch nicht dezimal aufgeschrieben werden kann.
ack(4, 3) = ack(3, ack(4, 2)) = ack(3, ack(3, ack(4, 1))) = ack(3, ack(3, ack(3, ack(4, 0)))) = ack(3, ack(3, ack(3, ack(3, 1)))) = ack(3, ack(3, ack(3, ack(2, ack(3, 0))))) = ack(3, ack(3, ack(3, ack(2, ack(2, 1))))) = ack(3, ack(3, ack(3, ack(2, ack(1, ack(2, 0)))))) = ack(3, ack(3, ack(3, ack(2, ack(1, ack(1, 1)))))) = ack(3, ack(3, ack(3, ack(2, ack(1, ack(0, ack(1, 0))))))) = ack(3, ack(3, ack(3, ack(2, ack(1, ack(0, ack(0, 1))))))) = ack(3, ack(3, ack(3, ack(2, ack(1, ack(0, 2)))))) = ack(3, ack(3, ack(3, ack(2, ack(1, 3))))) = ack(3, ack(3, ack(3, ack(2, ack(0, ack(1, 2)))))) = ack(3, ack(3, ack(3, ack(2, ack(0, ack(0, ack(1, 1))))))) = ack(3, ack(3, ack(3, ack(2, ack(0, ack(0, ack(0, ack(1, 0)))))))) = ack(3, ack(3, ack(3, ack(2, ack(0, ack(0, ack(0, ack(0, 1)))))))) = ack(3, ack(3, ack(3, ack(2, ack(0, ack(0, ack(0, 2)))))) = ack(3, ack(3, ack(3, ack(2, ack(0, ack(0, 3))))) = ack(3, ack(3, ack(3, ack(2, ack(0, 4))))) = ack(3, ack(3, ack(3, ack(2, 5))))
Das ist für einige Zeit der einfachste Fall einer solchen Expansion, und es ist nun offensichtlich, warum Funktionswerte wie dieser in der obigen Tabelle selten direkt berechnet werden. Es ist auch interessant festzustellen, wie viele Schritte nötig sind, um schon sehr einfach aussehende Ackermann-Ausdrücke zu vereinfachen. Jede Zeile im vorigen Beispiel ist eine einzige Anwendung eines der drei Teile der Definition der Ackermannfunktion. Wenn wir an dieser Stelle mehrere logische Schritte überspringen, könnten wir zu 13 auswerten und dann versuchen, Fehler beim Parsen (SVG (MathML kann über ein Browser-Plugin aktiviert werden): Ungültige Antwort („Math extension cannot connect to Restbase.“) von Server „http://localhost:6011/de.wikipedia.org/v1/“:): {\displaystyle ack(3,13)} auszuwerten – das ist 8179. Der nächste Funktionsaufruf, , liefert eine Zahl, die der Zahl der Atome im Universum entspricht, einige Dutzend mal mit sich selbst multipliziert. Diese Zahl wird schließlich in die Berechnung eingesetzt, der irgendwann zu einem Ausdruck der Form ausgeschrieben würde, der aber mit unseren Mitteln nicht mehr aufgeschrieben werden kann.
Der wirklich faszinierende Aspekt der Ackermann-Funktion ist, dass die einzige Berechnung, die neben den rekursiven Aufrufen tatsächlich auftaucht, die Berechnung von ist, die einfach um 1 erhöht.
Explizite Beschreibung
Intuitiv definiert die Ackermannfunktion eine verallgemeinerte Multiplikation mit 2 durch iterierte Additionen und eine Exponentialfunktion zur Basis 2 durch iterierte Multiplikationen, weiter Iterationen dieser Operation usw. Am knappsten kann sie nicht-rekursiv mithilfe von Conway-Kettenpfeilen ...
... oder mit Hyper-Operatoren ausgedrückt werden:
Geschichte
1928 betrachtete Wilhelm Ackermann eine Funktion von drei Variablen, die -fache iterierte Potenzierung von mit oder in Conways Notation. Er bewies, dass dies eine rekursive Funktion ist, die nicht primitiv-rekursiv ist. Diese Definition wurde später von Hans Hermes bzw. Rozsa Peter and Raphael Robinson zu der obigen Definition mit zwei Argumenten vereinfacht.
Inverse
Da die oben betrachtete Funktion sehr schnell wächst, wächst ihre Inverse sehr langsam. Diese Inverse taucht in der Laufzeitanalyse bestimmter Algorithmen auf. In diesem und anderen Zusammenhängen wird die Ackermannfunktion oft leicht umdefiniert zu einer Funktion mit ähnlichem asymptotischem Verhalten, aber ohne die -Terme, oder aber mit den Potenzen von 2 in Zeile 0, wobei die Zeilen 0-2 der ursprünglichen Definition ausgelassen werden. Diese modifizierten Funktionen sind nicht äquivalent zur Ackermannfunktion, aber nach den Maßstäben der Laufzeitanalyse können sie als äquivalent betrachtet werden. Die Inverse der Funktion ist für jede vorstellbare Eingabegröße kleiner als 4, kann also in der praktischen Analyse von Algorithmen als konstant betrachtet werden.
Einsatz als Benchmark
Auf Grund ihrer extremen Rekursionstiefe kann die Ackermannfunktion als Benchmark für die Fähigkeit eines Compilers, Rekursion zu optimieren, verwendet werden. Zum Beispiel kann ein Compiler, der "erkennt", dass etwas unterhalb einer Potenz von 2 liegt oder der Zwischenergebnisse wie und speichern kann, statt sie neu zu berechnen, die Berechnung von um das Hundert- bis Tausendfache beschleunigen. Außerdem erhält man einen bedeutenden Zeitvorteil, wenn man direkt berechnet, statt es rekursiv zu zu expandieren. Die direkte Berechnung von erfordert lineare Zeit in . Die Berechnung von erfordert quadratische Zeit, denn sie führt zu verschachtelten Aufrufen von für verschiedene . Die Berechnung von erfordert schließlich eine zu proportionale Zeit.
Es sei noch festgehalten, dass der Wert , der in Dezimalschreibweise auf verschiedenen Internetseiten auftaucht, wahrscheinlich nicht mit der rekursiven Definition der Ackermannfunktion praktisch berechnet werden kann.
Weblinks
- Einige Werte der Ackermannfunktion (Englisch)
- Beispielanwendung der Ackermannfunktion als Benchmark. Beachten Sie die große Zahl der Funktionsaufrufe schon bei der Berechnung kleiner Werte (Englisch)
- Dezimaler Wert von ack(4,2)