Recent from talks
Nothing was collected or created yet.
Ackermann function
View on WikipediaIn computability theory, the Ackermann function, named after Wilhelm Ackermann, is one of the simplest[1] and earliest-discovered examples of a total computable function that is not primitive recursive. All primitive recursive functions are total and computable, but the Ackermann function illustrates that not all total computable functions are primitive recursive.
After Ackermann's publication[2] of his function (which had three non-negative integer arguments), many authors modified it to suit various purposes, so that today "the Ackermann function" may refer to any of numerous variants of the original function. One common version is the two-argument Ackermann–Péter function developed by Rózsa Péter and Raphael Robinson. This function is defined from the recurrence relation with appropriate base cases. Its value grows very rapidly; for example, results in , an integer with 19,729 decimal digits.[3]
History
[edit]In the late 1920s, the mathematicians Gabriel Sudan and Wilhelm Ackermann, students of David Hilbert, were studying the foundations of computation. Both Sudan and Ackermann are credited[4] with discovering total computable functions (termed simply "recursive" in some references) that are not primitive recursive. Sudan published the lesser-known Sudan function, then shortly afterwards and independently, in 1928, Ackermann published his function (from Greek, the letter phi). Ackermann's three-argument function, , is defined such that for , it reproduces the basic operations of addition, multiplication, and exponentiation as
and for it extends these basic operations in a way that can be compared to the hyperoperations:
(Aside from its historic role as a total-computable-but-not-primitive-recursive function, Ackermann's original function is seen to extend the basic arithmetic operations beyond exponentiation, although not as seamlessly as do variants of Ackermann's function that are specifically designed for that purpose—such as Goodstein's hyperoperation sequence.)
In On the Infinite,[5] David Hilbert hypothesized that the Ackermann function was not primitive recursive, but it was Ackermann, Hilbert's personal secretary and former student, who actually proved the hypothesis in his paper On Hilbert's Construction of the Real Numbers.[2][6]
Rózsa Péter[7] and Raphael Robinson[8] later developed a two-variable version of the Ackermann function that became preferred by almost all authors.
The generalized hyperoperation sequence, e.g. , is a version of the Ackermann function as well.[9]
In 1963 R. Creighton Buck based an intuitive two-variable [n 1] variant on the hyperoperation sequence:[10][11]
Compared to most other versions, Buck's function has no unessential offsets:
Many other versions of Ackermann function have been investigated.[12][13]
Definition
[edit]Definition: as m-ary function
[edit]Ackermann's original three-argument function is defined recursively as follows for nonnegative integers and :
Of the various two-argument versions, the one developed by Péter and Robinson (called "the" Ackermann function by most authors) is defined for nonnegative integers and as follows:
The Ackermann function has also been expressed in relation to the hyperoperation sequence:[14][15]
or, written in Knuth's up-arrow notation (extended to integer indices ):
or, equivalently, in terms of Buck's function F:[10]
By induction on , one can show that for all .
Definition: as iterated 1-ary function
[edit]Define as the n-th iterate of :
Iteration is the process of composing a function with itself a certain number of times. Function composition is an associative operation, so .
Conceiving the Ackermann function as a sequence of unary functions, one can set .
The function then becomes a sequence of unary[n 2] functions, defined from iteration:
Computation
[edit]Computation by LOOP program
[edit]The functions fit into the (finite-level) fast-growing hierarchy (FGH) of functions[16]
The following inequality holds:[17]
For fixed , the function can be computed by a LOOP program of nesting depth :[18]
# INPUT (n) LOOP n: # nesting depth: 1 LOOP n: # nesting depth: 2 ... # ... LOOP n: # nesting depth: k n += 1 # # OUTPUT (n)
The function can also be computed by a LOOP-k program.[19] (The program (schema) is not listed here.)
It is obvious that , not being a primitive recursive function —see below—, cannot be computed by a LOOP program.
Computation by term rewriting system, based on 2-ary function
[edit]The recursive definition of the Ackermann function can naturally be transposed to a term rewriting system (TRS).
The definition of the 2-ary Ackermann function leads to the obvious reduction rules[20][21]
Example
Compute
The reduction sequence is [n 3]
| Leftmost-outermost (one-step) strategy: | Leftmost-innermost (one-step) strategy: |
To compute one can use a stack, which initially contains the elements .
Then repeatedly the two top elements are replaced according to the rules[n 4]
Schematically, starting from :
WHILE stackLength <> 1
{
POP 2 elements;
PUSH 1 or 2 or 3 elements, applying the rules r1, r2, r3
}
The pseudocode is published in Grossman & Zeitman (1988).
For example, on input ,
| the stack configurations | reflect the reduction[n 5] |
Remarks
- The leftmost-innermost strategy is implemented in 225 computer languages on Rosetta Code.
- For all the computation of takes no more than steps.[22]
- Grossman & Zeitman (1988) pointed out that in the computation of the maximum length of the stack is , as long as .
Their own algorithm, inherently iterative, computes within time and within space.
Computation by TRS, based on iterated 1-ary function
[edit]The definition of the iterated 1-ary Ackermann functions leads to different reduction rules
As function composition is associative, instead of rule r6 one can define
Like in the previous section the computation of can be implemented with a stack.
Initially the stack contains the three elements .
Then repeatedly the three top elements are replaced according to the rules[n 4]
Schematically, starting from :
WHILE stackLength <> 1
{
POP 3 elements;
PUSH 1 or 3 or 5 elements, applying the rules r4, r5, r6;
}
Example
On input the successive stack configurations are
The corresponding equalities are
When reduction rule r7 is used instead of rule r6, the replacements in the stack will follow
The successive stack configurations will then be
The corresponding equalities are
Remarks
- On any given input the TRSs presented so far converge in the same number of steps. They also use the same reduction rules (in this comparison the rules r1, r2, r3 are considered "the same as" the rules r4, r5, r6/r7 respectively). For example, the reduction of converges in 14 steps: 6 × r1, 3 × r2, 5 × r3. The reduction of converges in the same 14 steps: 6 × r4, 3 × r5, 5 × r6/r7. The TRSs differ in the order in which the reduction rules are applied.
- When is computed following the rules {r4, r5, r6}, the maximum length of the stack stays below . When reduction rule r7 is used instead of rule r6, the maximum length of the stack is only . The length of the stack reflects the recursion depth. As the reduction according to the rules {r4, r5, r7} involves a smaller maximum depth of recursion,[n 6] this computation is more efficient in that respect.
Computation by TRS, based on hyperoperators
[edit]As Sundblad (1971) — or Porto & Matos (1980) — showed explicitly, the Ackermann function can be expressed in terms of the hyperoperation sequence:
or, after removal of the constant 2 from the parameter list, in terms of Buck's function
Buck's function ,[10] a variant of Ackermann function by itself, can be computed with the following reduction rules:
Instead of rule b6 one can define the rule
To compute the Ackermann function it suffices to add three reduction rules
These rules take care of the base case A(0,n), the alignment (n+3) and the fudge (-3).
Example
Compute
| using reduction rule :[n 5] | using reduction rule :[n 5] |
The matching equalities are
- when the TRS with the reduction rule is applied:
- when the TRS with the reduction rule is applied:
Remarks
- The computation of according to the rules {b1 - b5, b6, r8 - r10} is deeply recursive. The maximum depth of nested s is . The culprit is the order in which iteration is executed: . The first disappears only after the whole sequence is unfolded.
- The computation according to the rules {b1 - b5, b7, r8 - r10} is more efficient in that respect. The iteration simulates the repeated loop over a block of code.[n 7] The nesting is limited to , one recursion level per iterated function. Meyer & Ritchie (1967) showed this correspondence.
- These considerations concern the recursion depth only. Either way of iterating leads to the same number of reduction steps, involving the same rules (when the rules b6 and b7 are considered "the same"). The reduction of for instance converges in 35 steps: 12 × b1, 4 × b2, 1 × b3, 4 × b5, 12 × b6/b7, 1 × r9, 1 × r10. The modus iterandi only affects the order in which the reduction rules are applied.
- A real gain of execution time can only be achieved by not recalculating subresults over and over again. Memoization is an optimization technique where the results of function calls are cached and returned when the same inputs occur again. See for instance Ward (1993). Grossman & Zeitman (1988) published a cunning algorithm that computes within time and within space.
Huge numbers
[edit]To demonstrate how the computation of results in many steps and in a large number:[n 5]
Table of values
[edit]Computing the Ackermann function can be restated in terms of an infinite table. First, place the natural numbers along the top row. To determine a number in the table, take the number immediately to the left. Then use that number to look up the required number in the column given by that number and one row up. If there is no number to its left, simply look at the column headed "1" in the previous row. Here is a small upper-left portion of the table:
n m
|
0 | 1 | 2 | 3 | 4 | n |
|---|---|---|---|---|---|---|
| 0 | 1 | 2 | 3 | 4 | 5 | |
| 1 | 2 | 3 | 4 | 5 | 6 | |
| 2 | 3 | 5 | 7 | 9 | 11 | |
| 3 | 5 | 13 | 29 | 61 | 125 | |
| 4 | 13 | 65533 | 265536 – 3 | |||
| 5 | 65533 |
|||||
| 6 | ||||||
| m |
The numbers here that are only expressed with recursive exponentiation or Knuth arrows are very large and would take up too much space to notate in plain decimal digits.
Despite the large values occurring in this early section of the table, some even larger numbers have been defined, such as Graham's number, which cannot be written with any small number of Knuth arrows. This number is constructed with a technique similar to applying the Ackermann function to itself recursively.
This is a repeat of the above table, but with the values replaced by the relevant expression from the function definition to show the pattern clearly:
n m
|
0 | 1 | 2 | 3 | 4 | n |
|---|---|---|---|---|---|---|
| 0 | 0+1 | 1+1 | 2+1 | 3+1 | 4+1 | n + 1 |
| 1 | A(0, 1) | A(0, A(1, 0)) = A(0, 2) |
A(0, A(1, 1)) = A(0, 3) |
A(0, A(1, 2)) = A(0, 4) |
A(0, A(1, 3)) = A(0, 5) |
A(0, A(1, n−1)) |
| 2 | A(1, 1) | A(1, A(2, 0)) = A(1, 3) |
A(1, A(2, 1)) = A(1, 5) |
A(1, A(2, 2)) = A(1, 7) |
A(1, A(2, 3)) = A(1, 9) |
A(1, A(2, n−1)) |
| 3 | A(2, 1) | A(2, A(3, 0)) = A(2, 5) |
A(2, A(3, 1)) = A(2, 13) |
A(2, A(3, 2)) = A(2, 29) |
A(2, A(3, 3)) = A(2, 61) |
A(2, A(3, n−1)) |
| 4 | A(3, 1) | A(3, A(4, 0)) = A(3, 13) |
A(3, A(4, 1)) = A(3, 65533) |
A(3, A(4, 2)) | A(3, A(4, 3)) | A(3, A(4, n−1)) |
| 5 | A(4, 1) | A(4, A(5, 0)) | A(4, A(5, 1)) | A(4, A(5, 2)) | A(4, A(5, 3)) | A(4, A(5, n−1)) |
| 6 | A(5, 1) | A(5, A(6, 0)) | A(5, A(6, 1)) | A(5, A(6, 2)) | A(5, A(6, 3)) | A(5, A(6, n−1)) |
Properties
[edit]General remarks
[edit]- It may not be immediately obvious that the evaluation of always terminates. However, the recursion is bounded because in each recursive application either decreases, or remains the same and decreases. Each time that reaches zero, decreases, so eventually reaches zero as well. (Expressed more technically, in each case the pair decreases in the lexicographic order on pairs, which is a well-ordering, just like the ordering of single non-negative integers; this means one cannot go down in the ordering infinitely many times in succession.) However, when decreases there is no upper bound on how much can increase — and it will often increase greatly.
- For small values of m like 1, 2, or 3, the Ackermann function grows relatively slowly with respect to n (at most exponentially). For , however, it grows much more quickly; even is about 2.00353×1019728, and the decimal expansion of is very large by any typical measure, about 2.12004×106.03123×1019727.
- An interesting aspect is that the only arithmetic operation it ever uses is addition of 1. Its fast growing power is based solely on nested recursion. This also implies that its running time is at least proportional to its output, and so is also extremely huge. In actuality, for most cases the running time is far larger than the output; see above.
- A single-argument version that increases both and at the same time dwarfs every primitive recursive function, including very fast-growing functions such as the exponential function, the factorial function, multi- and superfactorial functions, and even functions defined using Knuth's up-arrow notation (except when the indexed up-arrow is used). It can be seen that is roughly comparable to in the fast-growing hierarchy. This extreme growth can be exploited to show that , which is obviously computable on a machine with infinite memory such as a Turing machine and so is a computable function, grows faster than any primitive recursive function and is therefore not primitive recursive.
Not primitive recursive
[edit]The Ackermann function grows faster than any primitive recursive function and therefore is not itself primitive recursive.
Proof sketch:
Primitive recursive functions are built from basic functions using composition and primitive recursion, and all grow within a certain rate. We define, constructively, a hierarchy of total functions by:
where denotes -fold iteration of on input .[23] This hierarchy grows strictly faster with increasing , and every primitive recursive function is eventually bounded above by some . This can be shown by structural induction on the definitions of primitive recursive functions.
However, the Ackermann function eventually exceeds every ; for every , there exists such that for all sufficiently large . Thus, grows faster than any primitive recursive function and is therefore not primitive recursive.
Inverse
[edit]Since the function f(n) = A(n, n) considered above grows very rapidly, its inverse function, f−1, grows very slowly. This inverse Ackermann function f−1 is usually denoted by α. In fact, α(n) is less than 5 for any practical input size n, since A(4, 4) is on the order of .
This inverse appears in the time complexity of some algorithms, such as the disjoint-set data structure and Chazelle's algorithm for minimum spanning trees. Sometimes Ackermann's original function or other variations are used in these settings, but they all grow at similarly high rates. In particular, some modified functions simplify the expression by eliminating the −3 and similar terms.
A two-parameter variation of the inverse Ackermann function can be defined as follows, where is the floor function:
This function arises in more precise analyses of the algorithms mentioned above, and gives a more refined time bound. In the disjoint-set data structure, m represents the number of operations while n represents the number of elements; in the minimum spanning tree algorithm, m represents the number of edges while n represents the number of vertices. Several slightly different definitions of α(m, n) exist; for example, log2 n is sometimes replaced by n, and the floor function is sometimes replaced by a ceiling.
Other studies might define an inverse function of one where m is set to a constant, such that the inverse applies to a particular row.[24]
The inverse of the Ackermann function is primitive recursive, since it is graph primitive recursive, and it is upper bounded by a primitive recursive function.[25]
Usage
[edit]In computational complexity
[edit]The Ackermann function appears in the time complexity of some algorithms,[26] such as vector addition systems[27] and Petri net reachability, thus showing they are computationally infeasible for large instances.[28]
The inverse of the Ackermann function appears in some time complexity results. For instance, the disjoint-set data structure takes amortized time per operation proportional to the inverse Ackermann function,[29] and cannot be made faster within the cell-probe model of computational complexity.[30]
In discrete geometry
[edit]Certain problems in discrete geometry related to Davenport–Schinzel sequences have complexity bounds in which the inverse Ackermann function appears. For instance, for line segments in the plane, the unbounded face of the arrangement of the segments has complexity , and some systems of line segments have an unbounded face of complexity .[31]
As a benchmark
[edit]The Ackermann function, due to its definition in terms of extremely deep recursion, can be used as a benchmark of a compiler's ability to optimize recursion. The first published use of Ackermann's function in this way was in 1970 by Dragoș Vaida[32] and, almost simultaneously, in 1971, by Yngve Sundblad.[14]
Sundblad's seminal paper was taken up by Brian Wichmann (co-author of the Whetstone benchmark) in a trilogy of papers written between 1975 and 1982.[33][34][35]
See also
[edit]Notes
[edit]- ^ with parameter order reversed
- ^ 'curried'
- ^ In each step the underlined redex is rewritten.
- ^ a b here: leftmost-innermost strategy!
- ^ a b c d For better readability
S(0) is notated as 1,
S(S(0)) is notated as 2,
S(S(S(0))) is notated as 3,
etc... - ^ The maximum depth of recursion refers to the number of levels of activation of a procedure that exist during the deepest call of the procedure. Cornelius & Kirby (1975)
- ^ LOOP n+1 TIMES DO F
References
[edit]- ^ Monin & Hinchey 2003, p. 61.
- ^ a b Ackermann 1928.
- ^ "Decimal expansion of A(4,2)". kosara.net. 27 August 2000. Archived from the original on 20 January 2010.
- ^ Calude, Marcus & Tevy 1979.
- ^ Hilbert 1926, p. 185.
- ^ van Heijenoort 1977.
- ^ Péter 1935.
- ^ Robinson 1948.
- ^ Ritchie 1965, p. 1028.
- ^ a b c Buck 1963.
- ^ Meeussen & Zantema 1992, p. 6.
- ^ Munafo 1999a.
- ^ Ritchie 1965.
- ^ a b Sundblad 1971.
- ^ Porto & Matos 1980.
- ^ Odifreddi 1999, p. 298.
- ^ "The Ackermann hierarchy vs. the fast growing hierarchy". StackExchange.
- ^ Indentation according to the off-side rule (INDENT ... DEDENT), like in Python:
for _ in range (n): n += 1
- ^ Meyer & Ritchie 1967.
- ^ Grossman & Zeitman 1988.
- ^ Paulson 2021.
- ^ Cohen 1987, p. 56, Proposition 3.16 (see in proof).
- ^ Another sequence of functions, , defining the Grzegorczyk hierarchy, is frequently used to partition the primitive recursive functions into "growth classes". However, (or ) and do not align in their indexing.
- ^ Pettie 2002.
- ^ Matos 2014.
- ^ Brubaker 2023.
- ^ Czerwiński & Orlikowski 2022.
- ^ Leroux 2022.
- ^ Tarjan 1975.
- ^ Fredman & Saks 1989.
- ^ Wiernik & Sharir 1988.
- ^ Vaida 1970.
- ^ Wichmann 1976.
- ^ Wichmann 1977.
- ^ Wichmann 1982.
Bibliography
[edit]- Ackermann, Wilhelm (1928). "Zum Hilbertschen Aufbau der reellen Zahlen" [On the Hilbertian construction of the real numbers]. Mathematische Annalen (in German). 99: 118–133. doi:10.1007/BF01459088. S2CID 123431274.
- Buck, R. C. (1963). "Mathematical Induction and Recursive Definitions". American Mathematical Monthly. 70 (2): 128–135. doi:10.2307/2312881. JSTOR 2312881.
- Calude, Cristian; Marcus, Solomon; Tevy, Ionel (November 1979). "The first example of a recursive function which is not primitive recursive". Historia Math. 6 (4): 380–84. doi:10.1016/0315-0860(79)90024-7.
- Cohen, Daniel E. (January 1987). Computability and logic. Halsted Press. ISBN 9780745800349.
- Cornelius, B. J.; Kirby, G. H. (1975). "Depth of recursion and the Ackermann function". BIT Numerical Mathematics. 15 (2): 144–150. doi:10.1007/BF01932687. S2CID 120532578.
- Czerwiński, Wojciech; Orlikowski, Łukasz (7 February 2022). Reachability in Vector Addition Systems is Ackermann-complete. Proceedings of the 2021 IEEE 62nd Annual Symposium on Foundations of Computer Science. arXiv:2104.13866. doi:10.1109/FOCS52979.2021.00120.
- Fredman, M.; Saks, M. (May 1989). "The cell probe complexity of dynamic data structures". Proceedings of the twenty-first annual ACM symposium on Theory of computing – STOC '89. pp. 345–354. doi:10.1145/73007.73040. ISBN 0897913078. S2CID 13470414.
- Grossman, Jerrold W.; Zeitman, R. Suzanne (May 1988). "An inherently iterative computation of ackermann's function". Theoretical Computer Science. 57 (2–3): 327–330. doi:10.1016/0304-3975(88)90046-1.
- van Heijenoort, Jean (1977) [reprinted with corrections, first published in 1967]. From Frege to Gödel: A Source Book in Mathematical Logic, 1879–1931. Harvard University Press.
- Hilbert, David (1926). "Über das Unendliche" [On the infinite]. Mathematische Annalen (in German). 95: 161–190. doi:10.1007/BF01206605. S2CID 121888793.
- Leroux, Jérôme (7 February 2022). The Reachability Problem for Petri Nets is Not Primitive Recursive. Proceedings of the 2021 IEEE 62nd Annual Symposium on Foundations of Computer Science. arXiv:2104.12695. doi:10.1109/FOCS52979.2021.00121.
- Löb, M. H.; Wainer, S. S. (1970). "Hierarchies of number-theoretic functions. I.". Archiv für mathematische Logik und Grundlagenforschung. 13 (1–2): 39–51. doi:10.1007/BF01967649.
- Matos, Armando B (7 May 2014). "The inverse of the Ackermann function is primitive recursive" (PDF). Archived (PDF) from the original on 9 October 2022.
- Meeussen, V. C. S.; Zantema, H. (1992). Derivation lengths in term rewriting from interpretations in the naturals (PDF) (Report). University of Utrecht Department of Computer Science. ISSN 0924-3275. Archived (PDF) from the original on 9 October 2022.
- Meyer, Albert R.; Ritchie, Dennis MacAlistair (1967). "The complexity of loop programs". Proceedings of the 1967 22nd national conference. ACM '67: Proceedings of the 1967 22nd national conference. pp. 465–469. doi:10.1145/800196.806014.
- Monin, Jean-Francois; Hinchey, M. G. (2003). Understanding Formal Methods. Springer. p. 61. ISBN 9781852332471.
- Munafo, Robert (1999a). "Versions of Ackermann's Function". Large Numbers at MROB. Retrieved 6 November 2021.
- Munafo, Robert (1999b). "Inventing New Operators and Functions". Large Numbers at MROB. Retrieved 6 November 2021.
- Odifreddi, Piergiorgio (1999). Classical recursion theory. Vol. II. Studies in Logic and the Foundations of Mathematics. Vol. 143. Amsterdam: North-Holland. ISBN 978-0-444-50205-6. MR 1718169.
- Paulson, Lawrence C. (2021). "Ackermann's Function in Iterative Form: A Proof Assistant Experiment". Retrieved 19 October 2021.
- Péter, Rózsa (1935). "Konstruktion nichtrekursiver Funktionen" [Construction of non-recursive functions]. Mathematische Annalen (in German). 111: 42–60. doi:10.1007/BF01472200. S2CID 121107217.
- Pettie, S. (2002). "An inverse-Ackermann style lower bound for the online minimum spanning tree verification problem". The 43rd Annual IEEE Symposium on Foundations of Computer Science, 2002. Proceedings. pp. 155–163. doi:10.1109/SFCS.2002.1181892. ISBN 0-7695-1822-2. S2CID 8636108.
- Porto, António; Matos, Armando B. (1 September 1980). "Ackermann and the superpowers" (PDF). ACM SIGACT News. 12 (3): 90–95. doi:10.1145/1008861.1008872. S2CID 29780652. Archived (PDF) from the original on 9 October 2022. Original version 1980, published in ACM SIGACT News, modified on 20 October 2012 and 23 January 2016 (working paper)
- Ritchie, Robert Wells (November 1965). "Classes of recursive functions based on Ackermann's function". Pacific Journal of Mathematics. 15 (3): 1027–1044. doi:10.2140/pjm.1965.15.1027.
- Robinson, Raphael Mitchel (1948). "Recursion and Double Recursion". Bulletin of the American Mathematical Society. 54 (10): 987–93. doi:10.1090/S0002-9904-1948-09121-2.
- Sundblad, Yngve (March 1971). "The Ackermann function. A theoretical, computational, and formula manipulative study". BIT Numerical Mathematics. 11 (1): 107–119. doi:10.1007/BF01935330. S2CID 123416408.
- Tarjan, Robert Endre (1975). "Efficiency of a Good But Not Linear Set Union Algorithm". Journal of the ACM. 22 (2): 215–225. doi:10.1145/321879.321884. hdl:1813/5942. S2CID 11105749.
- Vaida, Dragoș (1970). "Compiler Validation for an Algol-like Language". Bulletin Mathématique de la Société des Sciences Mathématiques de la République Socialiste de Roumanie. Nouvelle série. 14 (62) (4): 487–502. JSTOR 43679758.
- Wainer, S. S. (1970). "A classification of the ordinal recursive functions". Archiv für mathematische Logik und Grundlagenforschung. 13 (3–4): 136–153. doi:10.1007/bf01973619.
- Ward, Martin P. (16 July 1993). Iterative Procedures for Computing Ackerman's Function. CiteSeerX 10.1.1.35.9907.
- Wichmann, Brian A. (March 1976). "Ackermann's function: A study in the efficiency of calling procedures". BIT Numerical Mathematics. 16: 103–110. CiteSeerX 10.1.1.108.4125. doi:10.1007/BF01940783. S2CID 16993343.
- Wichmann, Brian A. (July 1977). "How to call procedures, or second thoughts on Ackermann's function". BIT Numerical Mathematics. 16 (3): 103–110. doi:10.1002/spe.4380070303. S2CID 206507320.
- Wichmann, Brian A. (July 1982). "Latest results from the procedure calling test, Ackermann's function" (PDF). Archived (PDF) from the original on 9 October 2022.
External links
[edit]- "Ackermann function". Encyclopedia of Mathematics. EMS Press. 2001 [1994].
- Weisstein, Eric W. "Ackermann function". MathWorld.
This article incorporates public domain material from Paul E. Black. "Ackermann's function". Dictionary of Algorithms and Data Structures. NIST.- An animated Ackermann function calculator
- Aaronson, Scott (1999). "Who Can Name the Bigger Number?".
- Ackermann functions. Includes a table of some values.
- Brubaker, Ben (4 December 2023). "An Easy-Sounding Problem Yields Numbers Too Big for Our Universe".
- Munafo, Robert. "Large Numbers". describes several variations on the definition of A.
- Nivasch, Gabriel (October 2021). "Inverse Ackermann without pain". Archived from the original on 21 August 2007. Retrieved 18 June 2023.
- Seidel, Raimund. "Understanding the inverse Ackermann function" (PDF).
- The Ackermann function written in different programming languages, (on Rosetta Code)
- Smith, Harry J. "Ackermann's Function". Archived from the original on 26 October 2009.) Some study and programming.
- Wiernik, Ady; Sharir, Micha (1988). "Planar realizations of nonlinear Davenport–Schinzel sequences by segments". Discrete & Computational Geometry. 3 (1): 15–47. doi:10.1007/BF02187894. MR 0918177.
Ackermann function
View on GrokipediaA(0, n) = n + 1
A(m + 1, 0) = A(m, 1)
A(m + 1, n + 1) = A(m, A(m + 1, n))
A(0, n) = n + 1
A(m + 1, 0) = A(m, 1)
A(m + 1, n + 1) = A(m, A(m + 1, n))
Introduction and History
Overview
The Ackermann function stands as a foundational mathematical construct in recursion theory, defined through a recursive process that generates extraordinarily large integers even from modest input values. This function exemplifies the power and limitations of recursive definitions in computability, where its iterative structure leads to outputs that escalate far beyond everyday numerical scales, such as A(4, 2) = 2^{65536} - 3, a number with 19,729 digits that vastly exceeds the estimated 10^{80} atoms in the observable universe.[1][7] Its significance lies in illustrating the boundaries of primitive recursive functions, a class of computable operations built from basic arithmetic using bounded recursion, which the Ackermann function surpasses by employing unbounded recursion. This distinction highlights how certain total functions, while fully computable, escape the constraints of primitive recursion, providing a concrete counterexample in theoretical computer science.[8] The function is commonly expressed in its two-argument form, denoted A(m, n), where m and n are non-negative integers that control the levels of recursion and iteration, respectively. In computability theory, it serves as a total computable function—meaning it halts and produces a defined output for every input pair—yet remains non-primitive recursive, underscoring the hierarchy of recursive functions and the expressive limits of formal systems.[9]Historical Development
In the early 1920s, David Hilbert and his collaborators, including student Wilhelm Ackermann, advanced proof theory as part of Hilbert's program to establish the consistency of mathematics through finitary methods, emphasizing the study of recursive definitions to distinguish intuitive computability from more general recursion.[10] Ackermann, working under Hilbert at the University of Göttingen, contributed to this effort by exploring functions that exceeded primitive recursive bounds while remaining total and computable.[11] In 1928, Ackermann published a seminal three-argument recursive function in his paper "Zum Hilbertschen Aufbau der reellen Zahlen" within Mathematische Annalen, using it to exemplify a total recursive function beyond the scope of primitive recursion in the context of Hilbert's foundational framework.[12] This function, defined via nested iterations, highlighted limitations in primitive recursive methods and supported Hilbert's finitist approach by demonstrating computability without impredicativity.[13] Building on these ideas, Rózsa Péter independently developed a comparable rapidly growing two-argument function in her 1935 paper "Über den Zusammenhang der verschiedenen Begriffe der rekursiven Funktionen," published in Mathematische Annalen, to delineate the boundaries of primitive recursive functions and introduce non-primitive recursive examples. Péter's formulation simplified Ackermann's while preserving its growth properties, marking a key refinement in the analysis of recursive hierarchies during the foundational crisis of mathematics.[14][13] The function evolved further in 1947 when Raphael M. Robinson presented a streamlined two-argument version in his article "Primitive Recursive Functions" in the Bulletin of the American Mathematical Society, adjusting initial conditions for elegance and proving its non-primitive recursiveness, which solidified it as the standard Ackermann function. Throughout the 20th century, subsequent interpretations linked these formulations to hyperoperations, framing the Ackermann function as a diagonalization over the hyperoperation sequence, thus connecting recursion theory to broader arithmetic hierarchies.[13]Definitions
Multi-argument Formulation
The multi-argument formulation of the Ackermann function originates from Wilhelm Ackermann's 1928 paper, where he introduced a three-argument recursive function φ(m, n, p) to exemplify a total recursive function that is not primitive recursive.[3] Although the original definition involved higher-type recursion with auxiliary functions, a commonly used simplification for non-negative integers m, n, p is: These equations establish base cases for m = 0 and handle the recursive nesting for higher m, where the computation of φ(m + 1, n + 1, p) invokes φ(m, ·, p) applied to an inner recursive call, creating deeply nested recursions that escalate in complexity with increasing m. For fixed small values of p, the function φ(m, n, p) reproduces familiar arithmetic operations: addition when p = 0, multiplication when p = 1, and exponentiation when p = 2, with higher p leading to more rapid growth through iterated applications. The widely used two-argument Ackermann function A(m, n) emerged as a simplification of this three-argument version. It is defined recursively as: This formulation retains the nested recursive structure of φ, where each increment in m shifts the recursion to a higher level: A(1, n) yields multiplication-like behavior (n + 2), A(2, n) exponentiation-like (2n + 3), and A(3, n) tetration-like (2^{n+3} - 3), and higher m produce even faster-growing functions. The nesting in A(m + 1, n + 1) = A(m, A(m + 1, n)) embeds multiple evaluations of lower-level functions within higher ones, mirroring the hyperoperation hierarchy and demonstrating how the multi-argument setup generalizes to encode increasingly powerful operations through recursion depth.Iterated Unary Formulation
The iterated unary formulation presents the Ackermann function as a hierarchy of unary functions , defined recursively by and , where denotes the -fold iteration of the unary function applied to .[3] This construction builds each subsequent function by applying the previous one a number of times that depends on the input , starting from itself, thereby encapsulating increasingly rapid growth through repeated composition. This unary hierarchy is equivalent to the multi-argument formulation , in the sense that for all . To see this, proceed by induction on . For the base case , holds directly from the multi-argument base clause. Assume it holds for some , so . For , the definition corresponds to iterating exactly times starting at , which matches the recursive unfolding of via the clauses and , adjusted for the iteration count and initial value.[3] For small values of , the unary functions admit closed forms that illustrate their growth. Specifically, , obtained by iterating the successor-like exactly times on , yielding after alignment with standard base adjustments. Similarly, , arising from iterating the linear times on , which produces exponential growth via repeated doubling plus offsets. These examples highlight how the formulation layers simple operations into hyper-exponential behavior.[15] This perspective underscores the Ackermann function's connection to transfinite recursion, as the hierarchy mirrors the fast-growing hierarchy of functions indexed by ordinals below , where each corresponds to recursion along finite ordinals, extending primitive recursion to transfinite levels while remaining computable.[16]Hyperoperation Interpretation
The hyperoperation sequence provides a unified framework for extending basic arithmetic operations into an infinite hierarchy of increasingly powerful functions. Defined recursively, the hyperoperations begin with the successor function as , followed by addition , multiplication , exponentiation , tetration , and higher operations such as pentation and beyond for , where each level iterates the previous one.[17] This sequence, introduced by Reuben Goodstein in 1947, generalizes arithmetic progression by nesting operations to produce functions of rapidly increasing growth rates.[17] The Ackermann function aligns closely with this hyperoperation hierarchy, offering an explicit diagonalization over the sequence for specific arguments. In the standard two-argument formulation , the values correspond to hyperoperations applied with base 2 and adjusted arguments: A(m, n) = H_m(2, n + 3) - 3.[18] This mapping positions the Ackermann function as a concrete realization of hyperoperations starting from successor, with each increment in elevating the operation level.[18] Particularly notable is the case for , where embodies tetration: , using Knuth's up-arrow notation in which denotes iterated exponentiation (tetration).[17] Here, stacks exponentiations of 2 to height , yielding values like A(4, 0) = 13 = 2^2^2^2 - 3 (with right-associativity) and exploding to immense scales for larger .[17] Despite this alignment, the Ackermann function exhibits limitations relative to the full hyperoperation framework, as it fixes the base at 2 and applies a linear adjustment (), effectively diagonalizing only a subset of the hierarchy rather than encompassing arbitrary bases or unadjusted heights.[18] Consequently, while captures the essence of hyperoperations up to level , it grows more slowly than generalized hyperoperations like for or without offsets, highlighting its role as a primitive recursive diagonal rather than the complete hierarchy.[18]Evaluation
Small Values Table
The Ackermann function produces small, manageable values for low arguments but escalates dramatically as inputs increase, highlighting its non-primitive recursive nature. The table below enumerates A(m, n) for m ranging from 0 to 4 and n from 0 to 6, based on the standard two-argument formulation. For m ≤ 3, all values are explicitly computable as integers; for m = 4 and n ≥ 2, the results exceed practical numerical representation and are expressed in closed form with approximate scale indicators.[1]| n \ m | 0 | 1 | 2 | 3 | 4 |
|---|---|---|---|---|---|
| 0 | 1 | 2 | 3 | 5 | 13 |
| 1 | 2 | 3 | 5 | 13 | 65,533 |
| 2 | 3 | 4 | 7 | 29 | 2^{65,536} - 3 (19,729 digits) |
| 3 | 4 | 5 | 9 | 61 | ^{6}2 - 3 (∼10^{19,728} digits) |
| 4 | 5 | 6 | 11 | 125 | ^{7}2 - 3 (incomprehensibly large) |
| 5 | 6 | 7 | 13 | 253 | ^{8}2 - 3 (incomprehensibly large) |
| 6 | 7 | 8 | 15 | 509 | ^{9}2 - 3 (incomprehensibly large) |
Computational Methods
The Ackermann function is most commonly evaluated using its standard recursive definition, which proceeds by structural induction on the first argument , with the base case yielding , the case reducing to a call on , and the general case nesting a recursive call on within another on .[6] This approach leverages tail recursion in certain branches, such as the inner recursion on , allowing optimization to an iterative loop in functional languages that support tail-call elimination.[5] However, the double recursion inherent in the definition leads to exponential call stack growth, causing stack overflow in naive implementations for values beyond small inputs like .[5] To address recursion depth issues, iterative implementations replace recursive calls with explicit loops, particularly for fixed small , where the function corresponds to basic hyperoperations. For instance, is the successor function, implemented as a single increment; as addition via a loop adding 1, times; as multiplication via a nested loop simulating repeated addition; and as exponentiation via further nesting for repeated multiplication.[5] These LOOP-program-style constructions build progressively toward the full Ackermann by composing bounded iterations for lower , though higher requires hybrid approaches combining loops with limited recursion or stack simulation to avoid overflow.[6] In formal verification and automated reasoning, the Ackermann function is often handled via term rewriting systems (TRS), where reduction rules mirror the recursive cases for stepwise simplification. A standard 2-ary TRS uses the signature with constants 0, successor S, and binary Ackermann symbol A, with rules , , and , enabling confluent normalization for ground terms.[19] Equivalent iterated unary or hyperoperation-based TRS variants rewrite terms by unfolding to successor chains or Knuth up-arrow notation, facilitating proof of termination and complexity analysis through orderings like lexicographic path order.[20] Practical computations for small values benefit from optimizations like memoization, caching intermediate results in a table indexed by to eliminate redundant subcomputations, as the function's tree of calls has significant overlap.[5] Incrementalization techniques further enhance efficiency by updating only changed parts in recursive self-calls, yielding programs up to 30 times faster than naive recursion for feasible inputs, often via loop unrolling or dynamic programming.[5]Growth Rate Analysis
The Ackermann function demonstrates extraordinarily rapid growth, outpacing any primitive recursive function asymptotically. For any primitive recursive function , the diagonal values eventually exceed for sufficiently large , as formalized by the majorization relation where dominates all such .[21] This property arises from the function's recursive structure, which allows it to iterate operations in a way that escapes the bounded recursion schema defining primitive recursiveness.[22] In terms of asymptotic behavior, the growth escalates dramatically with the first argument . For fixed , aligns with increasingly powerful hyperoperations as grows, but the overall function transcends primitive recursive bounds when considering varying . Specifically, grows comparably to tetration, approximately in Knuth's up-arrow notation, representing iterated exponentiation towers of height linear in .[1] This places the function beyond exponentiation (which corresponds roughly to ) at , and beyond tetration at , embedding it within the hyperoperation hierarchy where each increment in advances to the next level of iterated operations.[23] The explosive growth yields numbers vastly exceeding well-known large values. Such magnitudes highlight the function's role as a foundational benchmark in notations for enormous numbers, including extensions like Bowers' Exploding Array Function (BEAF) and the Subcubic Graph numbers (SCG(13) and beyond), which leverage or exceed Ackermann-level growth to define even larger constructs in combinatorial and googological contexts.[24] As a diagonalization over the primitive recursive functions, the Ackermann function systematically constructs values that outgrow any fixed level of the primitive recursive hierarchy, leading to extensions in higher computability theory where analogous diagonal arguments yield uncomputably large growth rates.[21] This diagonal nature underscores its significance in separating recursive from primitive recursive classes, with implications for understanding limits of computable growth.[22]Properties
Functional Characteristics
The Ackermann function is a total recursive function, defined for all natural numbers and , with its recursive computation guaranteed to terminate for every input pair due to the well-founded nature of the underlying recursion on natural numbers.[13] In general, the Ackermann function is not commutative, meaning for most pairs . For instance, under the standard definition, while , illustrating the asymmetry. However, equality holds for specific low values, such as where , where , and where ; for small fixed , the function reduces to commutative operations like successor or addition, yielding limited symmetries in those cases.[4] The Ackermann function exhibits extensional equivalence to certain fast-growing hierarchies, particularly corresponding to the ordinal level, where the diagonal aligns with in the standard fast-growing hierarchy. This placement underscores its role among computable fast-growing functions, though uncomputable ones like the Busy Beaver function eventually surpass it in growth for large arguments.[25]Non-Primitive Recursiveness
Primitive recursive functions form the smallest class of total functions from natural numbers to natural numbers that includes the constant zero function, the successor function, and the projection functions (which select arguments), and is closed under the operations of composition and primitive recursion.[26] Primitive recursion allows defining a function and for some previously defined functions and , corresponding intuitively to bounded for-loops with fixed nesting depth. The Ackermann function demonstrates non-primitive recursiveness by growing faster than any function obtainable through a fixed finite number of primitive recursions. In Wilhelm Ackermann's original 1928 formulation, a three-argument function was defined recursively on : , , , where , , and for . Ackermann argued that this function cannot be primitive recursive because, for any purported primitive recursive approximation with fixed recursion depth, the function's value at sufficiently large arguments exceeds the growth bound of that approximation, leading to a contradiction.[3] A modern simplification to a two-argument form, introduced by Rózsa Péter in 1935, defines with base cases and , and recursive case . The proof of non-primitive recursiveness proceeds by contradiction: assume is primitive recursive, so the diagonal is primitive recursive and thus majorized by some function with fixed primitive recursion nesting depth , equivalent to iterated exponentiation of height (as in Kalmár elementary functions for small ). However, grows like the -th hyperoperation applied to , outpacing any fixed-height iteration for , yielding a contradiction.[26] This separation highlights that while all primitive recursive functions are total and computable (in the sense of general recursive functions via the -operator), the converse does not hold; the Ackermann function is total recursive but not primitive recursive, delineating a fundamental boundary in recursion theory between these classes.Inverse
Definition
The inverse Ackermann function, commonly denoted , is a slow-growing function that serves as a partial inverse to the rapidly growing Ackermann function . The standard variant in theoretical computer science, known as the diagonal inverse, defines as the smallest non-negative integer such that , where the diagonal refers to evaluating the Ackermann function at equal arguments.[27] This diagonal form often simplifies analysis in contexts like algorithm complexity, as it captures the function's extreme slowness in a single-variable setup. The inverse Ackermann function can also be characterized recursively through a hierarchy of inverse operations aligned with the Ackermann hierarchy. For instance, level-wise inverses are defined as for each level , where denotes the -th row of the Ackermann function, building up to the full via minimization over .[27] These recursive structures underscore 's computability within primitive recursive bounds, contrasting sharply with the non-primitive recursive nature of the original Ackermann function. Both variants highlight 's role as an inverse: while the Ackermann function outpaces any primitive recursive function in growth rate, its inverse increases so gradually that it remains below 5 for all practically relevant values of up to enormous scales.[28][27]Asymptotic Behavior
The inverse Ackermann function exhibits extraordinarily slow growth, rendering it effectively constant for all computationally relevant values of . For the standard diagonal inverse using the Péter Ackermann function, for , for , and only beyond that immense threshold up to , which involves pentation. Note that in some computer science contexts, slightly modified Ackermann variants are used, leading to adjusted thresholds (e.g., up to scales like ), but the qualitative behavior of near-constancy remains the same.[29][28] Asymptotically, , where denotes the iterated logarithm, representing the number of times the base-2 logarithm must be applied to to reduce it below a constant threshold (typically 1 or 2). However, in practice, advances even more sluggishly than due to the specific alignment with the Ackermann function's diagonal growth, where the functional iterations align with hyperoperation levels rather than pure logarithmic reductions.[29] A proof of this slow growth proceeds by induction on the hyperoperation levels implicit in the Ackermann function's definition. The base cases for small verify that stabilizes at low values for modest , such as for . Assuming the induction hypothesis that for all below the threshold where the -th hyperoperation dominates, the next level requires to exceed the Ackermann value , which grows beyond any fixed iteration of the previous level—ensuring the increment occurs only at exponentially larger scales. This inductive step confirms that each increase in demands an escalation through the entire hyperoperation sequence, bounding the function tightly for practical domains.[8] In comparison to other slow-growing functions, increases more gradually than any logarithm iterated a fixed finite number of times (e.g., slower than or ), yet remains unbounded as , eventually surpassing any constant but only after traversing incomprehensible magnitudes. This positions as a canonical example of a function that is asymptotically negligible in complexity analyses while theoretically diverging.[29]Applications
Complexity Theory
The Ackermann function serves as a fundamental bound in defining complexity classes that extend beyond primitive recursive and elementary computations. The Ackermann class, often denoted as the set of problems solvable by deterministic Turing machines in time or space O(A(n, n)), where A(n, n) is the diagonal of the Ackermann function, captures computations that grow faster than any primitive recursive bound but remain within recursive functions. This class is equivalent to the union over all primitive recursive polynomials p of DTIME(F_ω(p(n))), where F_ω denotes the fast-growing hierarchy function at level ω, which coincides with the Ackermann function. Such classes are crucial for studying hierarchies in computational complexity that surpass polynomial or even exponential time. A key relation exists between the Ackermann function and elementary functions, which form the third level (ℰ³) of the Grzegorczyk hierarchy. Functions in ℰ³, known as Kalmár elementary functions, are computable in time bounded by iterated exponentials of fixed height, precisely upper-bounded by growth rates akin to A(3, n) ≈ 2↑↑(n + 3) − 3 in Knuth up-arrow notation. In contrast, the full Ackermann function A(m, n) for m ≥ 4 exceeds any fixed level of the Grzegorczyk hierarchy, defining non-elementary complexity classes that require hyper-exponential resources. This distinction highlights how A(3, n) marks the boundary for elementary time, while higher values of A(m, n) delineate super-elementary behaviors.[31] The Ackermann function also facilitates advanced techniques in complexity theory, including its application in oracle Turing machines and relativization arguments. Oracle machines equipped with oracles allowing Ackermann-level computations can relativize results about primitive recursive separations, enabling proofs of hierarchy theorems in relativized worlds. Specifically, diagonalization over the Grzegorczyk hierarchy constructs the Ackermann function itself, and similar diagonalization in time complexity shows that TIME(A(n)) strictly contains the union of TIME(f(n)) over all primitive recursive f, establishing proper separations in non-elementary time classes. These connections underscore the Ackermann function's role in bounding resources for oracle-augmented models and proving structural properties of complexity hierarchies.[32][33]Discrete Geometry
The inverse Ackermann function α(n) plays a crucial role in the analysis of union-find data structures, which are fundamental to many algorithms in discrete geometry, such as dynamic maintenance of connectivity in point sets or incremental construction of geometric graphs. In these structures, path compression with union by rank achieves an amortized time complexity of O(α(n)) per operation, where α(n) is the smallest integer k such that the Ackermann function A(k, 3) ≥ n; this bound effectively eliminates extra logarithmic factors beyond linear time in nearly all practical scenarios. This efficiency is particularly valuable in geometric applications, where union-find operations manage mergers and queries in evolving structures like Voronoi diagrams or arrangements, ensuring that the overall algorithm remains quasi-linear despite the super-exponential growth of the underlying Ackermann function. In higher-dimensional order types, Ackermann-like growth emerges in the combinatorial complexity of arrangements, where the number of regions or cells defined by hyperplane or pseudoline arrangements can exhibit rapid escalation as dimensionality increases, though practical constructions leverage inverse Ackermann bounds to achieve efficient enumeration. For instance, randomized incremental algorithms for building arrangements in d dimensions rely on union-find to track conflicts and adjacencies, yielding total time complexities of O(n^{d/2} α(n)) for n objects, which captures the subtle overhead from path compression in maintaining the order type's topological structure. This integration highlights how the slow growth of α(n) tames the otherwise explosive proliferation of regions, enabling computational feasibility in problems like motion planning or visibility decompositions in higher dimensions. Variants of Szemerédi's theorem in multidimensional settings invoke Ackermann hierarchies to quantify bounds on arithmetic progressions within geometric configurations, such as subsets of the integer lattice. In the proof of the multidimensional Szemerédi theorem, the regularity lemma for hypergraphs produces partition sizes that ascend levels in the Ackermann hierarchy with increasing progression length k, leading to quantitative bounds of the form exp_k (log n / log log n) or higher, where exp_k denotes iterated exponentials akin to Ackermann levels.[34] These hierarchy-based estimates are essential for geometric applications, like analyzing progression-free sets in grids, where the theorem ensures that dense point sets in Z^d contain k-term progressions, with proof complexities reflecting the non-primitive recursive nature of the Ackermann function. In geometric set theory, particularly Vapnik-Chervonenkis (VC) theory applied to range spaces, the Ackermann function indirectly influences bounds on shattering dimensions through its inverse in epsilon-net constructions for geometric objects. For range spaces of constant VC dimension, such as halfspaces or balls in Euclidean space, the minimal epsilon-net size is Θ( (1/ε) log(1/ε) ), but lower bounds for certain low-dimensional geometric families, such as axis-parallel rectangles in the plane, reach Ω(α(1/ε)), involving the inverse Ackermann function α to capture the overhead in shattering large point sets.[35] This connection underscores the role of fast-growing hierarchies in limiting the expressive power of geometric hypothesis classes, ensuring learnability while bounding the combinatorial explosion in shattered subsets.Benchmarking Computations
The Ackermann function is frequently employed as a stress test for programming languages, compilers, and runtime environments to evaluate their handling of deep recursion and stack management. Computations of values like A(4,1), which equals 65,533, or A(4,2), which equals 2^{65536} - 3, demand exceptionally high stack depths in naive recursive implementations, often exceeding default limits and causing stack overflows. This makes it an effective benchmark for assessing recursion optimization techniques, such as tail-call optimization, and the overall robustness of a language's call stack.[36] Historical benchmarks have highlighted the Ackermann function's utility in testing recursion handling in functional languages like Haskell and Scheme. In Haskell, optimized implementations using techniques like continuation-passing style (CPS) can compute A(3,11) in seconds, while non-optimized recursive versions may take minutes or fail due to stack exhaustion. Similarly, Scheme implementations in Lisp dialects, which guarantee proper tail-call optimization per the language standard, can handle larger arguments efficiently for many recursive functions. In contrast, Python's default recursion limit of 1,000 frames restricts it to smaller inputs like A(3,4) = 125, beyond which sys.setrecursionlimit() must be invoked to avoid RuntimeError, though this risks system instability.[36][37][38][39]| Language/Compiler | Implementation | Running Time for A(3, n) (approx.) |
|---|---|---|
| C (gcc) | Recursive | 3.76 seconds |
| C (gcc -O4) | Optimized | 0.35 seconds |
| Java (HotSpot) | Recursive | 2.35 seconds |
References
- -Sch.pdf
