Originally concieved in 1928, but I'll use the 1935 Rózsa Péter version (table):
It's the classic mad benchmark. This function will either overflow the stack or the 32-bit computer word size - if the cpu doesn't burn out from prolonged strain.
Characteristics:
Main program body:
'Subject: Ackermann's function - deep recursion 'Author : Sjoerd.J.Schaper 'Date : 05-20-2006 'Code : FreeBasic and all QBasic's DEFINT A-Z DECLARE FUNCTION A (m, n) CLEAR , , 28608 ' maximum stack space CONST tsize = 32764 ' stack array DIM SHARED s, d, c AS LONG DO PRINT : INPUT " m, n "; m, n IF m < 0 OR n < 0 THEN EXIT DO c = 0: d = 1: s = 0: tim! = TIMER PRINT " A(m, n) ="; A(m, n); " calls:"; c; " depth:"; d PRINT " Timer:"; CSNG(TIMER - tim!); "s" LOOP END
Naive implementation with three recursive calls:
FUNCTION A (m, n)
c = c + 1
s = s + 1
IF s > d THEN d = s
IF m = 0 THEN
A = n + 1
ELSEIF n = 0 THEN
A = A(m - 1, 1)
ELSE
A = A(m - 1, A(m, n - 1))
END IF
s = s - 1
END FUNCTION
A(3, 7) = 1021 calls: 693964 depth: 1023
Timer: 2.25s (QBasic 1.0, cpu 700 MHz)
Tip: compile w/FreeBasic
using -t 4096 to get a 4 MB stack, and you can even compute A(4, 1) = 65533;
this will easily take a quarter of an hour though.
Optimized using tail recursion:
FUNCTION A (m, n)
c = c + 1
s = s + 1
IF s > d THEN d = s
WHILE m > 0
c = c + 1
IF n = 0 THEN
n = 1
ELSE
t = m
n = A(t, n - 1)
END IF
m = m - 1
WEND
s = s - 1
A = n + 1
END FUNCTION
A(3, 7) = 1021 calls: 693964 depth: 1020
Timer: 1.70s (QBasic 1.0)
Iterative using a do-loop, stacking both m & n:
FUNCTION A (m, n)
DIM s(tsize + 1)
s(0) = m
s(1) = n: t = 1
DO
c = c + 1
IF s(t - 1) = 0 THEN ' m = 0
t = t - 1
s(t) = s(t + 1) + 1
ELSEIF s(t) = 0 THEN ' n = 0
s(t) = 1
s(t - 1) = s(t - 1) - 1
ELSE
s(t + 1) = s(t) - 1
s(t) = s(t - 1)
s(t - 1) = s(t - 1) - 1
t = t + 1
END IF
IF t > d THEN
d = t
IF d > tsize THEN
PRINT "failure": END
END IF
END IF
LOOP UNTIL t = 0
A = s(0)
END FUNCTION
A(3, 7) = 1021 calls: 693964 depth: 1020
Timer: 2.42s (QBasic 1.0)
Iterative using a do-loop, stacking m only:
FUNCTION A (m, n)
DIM s(tsize + 1)
t = 1: s(t) = m
DO
c = c + 1
m = s(t): t = t - 1
IF m = 0 THEN
n = n + 1
ELSEIF n = 0 THEN
t = t + 1: s(t) = m - 1
n = 1
ELSE
t = t + 1: s(t) = m - 1
t = t + 1: s(t) = m
n = n - 1
END IF
IF t > d THEN
d = t
IF d > tsize THEN
PRINT "failure": END
END IF
END IF
LOOP UNTIL t = 0
A = n
END FUNCTION
A(3, 7) = 1021 calls: 693964 depth: 1020
Timer: 2.14s (QBasic 1.0)
Cheat - direct computation for m < 4:
FUNCTION A& (m, n)
c = c + 1
s = s + 1
IF s > d THEN d = s
SELECT CASE m
CASE 0
A = n + 1
CASE 1
A = n + 2
CASE 2
A = 2 * n + 3
CASE 3
A = 2 ^ (n + 3) - 3
CASE ELSE
IF n = 0 THEN
A = A(m - 1, 1)
ELSE
A = A(m - 1, A(m, n - 1))
END IF
END SELECT
s = s - 1
END FUNCTION
A(4, 1) = 65533 calls: 4 depth: 3
Timer: 0s (QBasic 1.0)
Note that the function must be declared LONG here.
Using my large integer library, we can now evaluate A(4, 2):
'Subject: Ackermann's function
'Author : Sjoerd.J.Schaper
'Update : 01-06-2009
'Code : FreeBasic 0.20b
#INCLUDE "largeint.bi"
DECLARE SUB Ack (byVal m AS INTEGER, byVal n AS INTEGER)
CONST a = 0, m = 1, n = 2, tsize = 8 ' stack size 3 * 2
LargeInit(tsize, "ackerman")
DIM SHARED AS INTEGER c, d, s
DIM AS INTEGER x, y
DIM tim AS SINGLE
DO
INPUT " m, n "; x, y
IF x < 0 OR y < 0 THEN EXIT DO
c = 0: d = 3: s = d: tim = TIMER
Letf(m, x): Letf(n, y): Ack(m, n)
Printn(a, " A(" + STR$(x) + ", " + STR$(y) + ") = ", "", 0)
Prints(" calls " + STR$(c) + " depth " + STR$(d \ 2), 1)
Prints(" Timer: " + STR$(CSNG(TIMER - tim)) + " s", 2)
LOOP
Term()
END
SUB Ack (byVal mu AS INTEGER, byVal nu AS INTEGER)
DIM mv AS INTEGER, nv AS INTEGER
c = c + 1
Letf(a, 4)
IF Cmp(mu, a) < 0 THEN ' m < 4
SELECT CASE Getw(mu, 0)
CASE 0
Swp(a, nu): Inc(a, 1)
CASE 1
Swp(a, nu): Inc(a, 2)
CASE 2
Swp(a, nu)
Lsft(a, 1): Inc(a, 3)
CASE ELSE '3
Letf(a, 2): Inc(nu, 3): Letf(mu, 0)
Modpwr(a, nu, mu): Inc(a, -3)
END SELECT
ELSE ' enter recursion...
mv = s: nv = s + 1
s = s + 2 ' two-row stackframe
IF s > d THEN
d = s
IF nv > tsize THEN
Prints " out of stack space", 1: END
END IF
END IF
Dup(mu, mv)
IF Isf(nu, 0) THEN
Inc(mv, -1): Letf(nv, 1)
Ack(mv, nv)
ELSE
Dup(nu, nv): Inc(nv, -1)
Ack(mv, nv)
Inc(mv, -1): Swp(a, nv)
Ack(mv, nv)
END IF
s = s - 2
END IF
END SUB
A(4, 2) = 200352...156733
(19729 digits) calls: 6 depth: 4
Timer: 1.83s (FreeBasic 0.20b)
Note that this works only if module and largeint library are compiled with FreeBasic,
as the old DOS Basic's do not allow this big memory usage.