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.