[Search for users] [Overall Top Noters] [List of all Conferences] [Download this site]

Conference turris::languages

Title:Languages
Notice:Speaking In Tongues
Moderator:TLE::TOKLAS::FELDMAN
Created:Sat Jan 25 1986
Last Modified:Thu May 22 1997
Last Successful Update:Fri Jun 06 1997
Number of topics:394
Total number of notes:2683

108.0. "Recursive function..." by COOKIE::ROLLOW (Redneck cookin'... Fryit!) Sun Oct 05 1986 23:10

    Does anybody know the definition of Ackerman's function?  A
    friend of mine once it had assigned as an exercise in a Data
    Structures class while they where discussing recursion.  The
    only thing I remember about is that it was recursively defined
    and took 2 parameters.  It turned that one of the problems
    assigned (ackerman(4,2)) was (virtually) unsovable using the
    recursive definition.

    He was able to work out that ackerman(4,2) was (2 ** 65534) - 3,
    and I was able to get a value for it using the U*IX multiple
    precision math functions (libmp).  It ends up being a 19,000+
    digit number.
    
    					Alan (nabeth::alan)
    
T.RTitleUserPersonal
Name
DateLines
108.1HYDRA::ECKERTJerry EckertMon Oct 06 1986 04:3520
    I have a data structures book ("Fundamentals of Data Structures"
    by Horowitz and Sahni, 1976) with two different definitions of
    Ackermann's function:
    
    1)	A(m,n) =	n+1			, if m = 0
    			A(m-1, 1)		, if n = 0
    			A(m-1, A(m, n-1))	, otherwise
    
    
    2)	A(p,q) =	2q			, if p = 0
    			0			, q = 0 and p >= 1
    			2			, p >= 1 and q = 1
    			A(p-1, A(p, q-1))	, p >= 1 and q >= 2
    
    
    I believe (1) is the actual definitions of Ackermann's function;
    (2) appears to be a modification designed to simplify the ensuing
    discussion.
    
    	- Jerry
108.2QUARK::LIONELReality is frequently inaccurateMon Oct 06 1986 11:4510
    One of my most bitter memories of CS classes (and there are few)
    was near the beginning of my course on "Programming Languages"
    (mainly LISP and SNOBOL) where the instructor described Ackermann's
    function to us (not by name) and gave as a homework assignment to
    come up with an iterative algorithm for it.  I beat my brains against
    the wall for days, and felt like shooting the instructor when he
    told us that it took some fifty years (or more) for someone to
    finally come up with an iterative solution.  He must have thought it 
    was funny.
    					Steve
108.3Pointless exercises...COOKIE::ROLLOWRedneck cookin'... Fryit!Mon Oct 06 1986 15:4411
    I think the reason this was given to the particular class while
    studying recursion, was to show that recursive algorithms, some-
    times aren't the best.  The class being made up of summer sophmores,
    didn't realize that A(4,2) was impossible to solve on the 11/70
    they were using.  (Berkeley 2.9 UNIX and Pascal).  I don't think
    they were ever told either.
    
    re: .1  Thanks for the info.
    
    					Alan
    
108.4QUARK::LIONELReality is frequently inaccurateMon Oct 06 1986 16:016
    As I recall, Ackermann's function was devised to prove that all
    iterative algorithms could be written recursively, but that the
    converse was not true.  As I said, it took a LONG time for someone
    to come up with a clever (and simple, once you saw it) way to
    compute Ackermann's iteratively.
    				Steve
108.5Ack!TLE::AMARTINAlan H. MartinWed Oct 08 1986 14:0730
Re .1:

FWIW, while I've seen several definitions, your #1 is the one I am used
to dealing with.

Re .2:

I once discovered out that for each value of m, you get a relatively simple
closed form expression in n.  But for each increase in m, a higher-order
arithmetic operator is used (addition, multiplication, exponentiation,
...).  Since the only way I know to describe the higher-order operators
is recursive, that begs the question.

Re .3:

I don't remember the magnitude of A(4,2), but if it didn't require multiple
precision arithmetic, the problem was quite solvable on an 11/70.  Most
of the recusive calls are for values that have already been computed.
If you simply cache the values of A(m,n) on the fly, and look them up
before attempting to compute them, it greatly speeds up the algorithm,
at least in the small region I have experience with.


A professor at my college unknowingly assigned something that was not
representable in 36 bits (A(4,3)?) in an introduction to assembly language
course.  Since overflow from addition was not detected by default, no one
could figure out why they were getting partial results that were negative
when the numbers wrapped around.  (Yes, it was during the section on
recursion that the assignment was made).
				/AHM
108.6Looking for the iterative algorithmDSSDEV::REINIGAugust G. ReinigWed Oct 08 1986 16:526
    So, what is the iterative algorithm?  
    
    I remember writing Ackermann's function in UNIX shell once.  Talk
    about slow. 
    
                                        August G. Reinig
108.7QUARK::LIONELReality is frequently inaccurateWed Oct 08 1986 17:443
    Sorry, I don't recall the iterative solution.  It was thirteen years
    ago!
    			Steve
108.8Ackermann(3,n) = 2 ** (n + 3) - 3COOKIE::ROLLOWRedneck cookin'... Fryit!Wed Oct 08 1986 21:1818
    After looking at some results of the recursive version, I've
    notice that:
    
    	Ackermann(3,n) = ( 2 ** ( n + 3 )) - 3
    
    I'm running Ackermann(4,1) on my workstation and the local
    8650 now.  If I remember correctly it is 65533 ( +/- a
    couple).  It looks like Ackermann(4,2) really will in the
    neighborhood of:
    
    	2 ** 65534 - 3
    
    My recursive version is braindamaged, because I didn't put
    in any value cacheing.  I'll probably add a table of inter-
    mediate values to the next version, so it won't have to
    recalculate each one.

    					Alan
108.9Ackermann.cCOOKIE::ROLLOWRedneck cookin'... Fryit!Wed Oct 08 1986 23:32101
    Below is the "smart" version of my program to calculate a value for
    Ackermann's function.  It doesn't do any bounds checking for "known"
    maximium values (Ackermann(3,big)).
    
    It was written in about two hours and compiles and runs on Ultrix
    and VMS.  It hasn't had any lint dusted off yet.  The next version
    will have comments.  It is also reasonably fast.  My Ultrix work-
    station found Ackermann(4,1) in 12.8 seconds (9.3 user, 3.5 system).
    The selection of 65536 for N was arbitrary.
    
    					Alan
    
- - - - - - - - - - - - - - - - CUT HERE - - - - - - - - - - - - - - - - - - - -
/*
 *	Author:  Alan Rollow, EIS/CXO, Digital Equipment Corp.
 *	File:	 Ackermann.c
 *	Date:	 10/8/86
 *	Version: 1.2
 *
 *	Ackermann.c - Recursive version of Ackermann's function.  After
 *	Ackermann(4, 0) it really gets nasty.
 */
#ifndef	lint
static	char	SccsId[] = "@(#)Ackermann.c	1.2 10/8/86" ;
#endif

#include <stdio.h>
#include <errno.h>
#include <signal.h>

int	depth = 0, lookup = 0 ;

#define	M	4
#define	N	65536

/*
 *	Tacky humor has no place in serious programing...  But
 *	whoever said that was serious...
 */
struct	bill_the_cat {
	int	ack_value ;
	int	ack_flag ;
} table[M][N] ;

main(argc, argv)
int	argc ;
char	*argv[] ;
{
	register m, n ;
	int	 catch() ;

	if( argc != 3 ) {
		printf("usage: Ackermann m n\n") ;
		exit(EINVAL);
	}

	m = atoi(argv[1]) ;
	n = atoi(argv[2]) ;

	(void)signal(SIGINT, catch) ;
	(void)signal(SIGTERM, catch) ;

	printf("Ackermann(%d,%d) = %d\n", m, n, Ackermann(m, n));
	printf("Depth: %d, Lookup: %d\n", depth, lookup);
}

Ackermann(m, n)
register m, n ;
{
	register value ;

	if( m < M && n < N && table[m][n].ack_flag ) {
		lookup++ ;
		return table[m][n].ack_value ;
	}

	depth++ ;

	if( m == 0 )
		value = n + 1 ;
	else if( n == 0 )
		value = Ackermann(m - 1, 1) ;
	else
		value = Ackermann(m - 1, Ackermann(m, n - 1)) ;

	if( m < M && n < N ) {
		table[m][n].ack_value = value ;
		table[m][n].ack_flag = 1 ;
	}

	return value ;
}

catch()
{
	printf("\nDepth of \"recursion\" when stopped: %d\n", depth) ;
	printf("Number of lookups: %d\n", lookup);
	fflush(stdout) ;
	exit(0) ;
}
    
108.10Reduce time and space cost at expense of tasteTLE::AMARTINAlan H. MartinThu Oct 09 1986 11:4619
You can halve your space usage by using a zero cache entry as the flag that
no number is stored there yet.  This also results in fewer instructions to
access the entries of table[][], although changing table to a struct
of two arrays instead of an array of two-member structs would probably
accomplish the same thing.

I was going to claim it makes a significant speedup in A(4,1), but I only
tried a half a dozen times, using my cursor blink as a timer, and the times
for the same version of the program on the same data seem to have a lot of
deviation.  I'm on an 8800, maybe it depends on which processor I get
scheduled on.

Unfortunately, Vax C doesn't CSE the two accesses to table[][] in the
revised program, probably because it doesn't realize that a test against
zero could fetch the value of the table into an AC.  What's worse, it
doesn't CSE the index computations, either.  However, with the large cache,
and highly recursive nature of the algorithm, I assume the program spends
most of its time waiting for memory, rather than doing address arithmetic.
				/AHM
108.11<sigh...>COOKIE::ROLLOWRedneck cookin'... Fryit!Thu Oct 09 1986 14:3228
    re: 10
    
    Since I know that a value for Ackermann(m,n) will never be zero
    (for non-negitive m and n), then using a zero value as a flag
    seems very reasonable.  I'll add this to the next version and
    provide a pointer to the source in a future note.
    
    I spent a couple of hours last night looking for an iterative
    solution and only succeeded in realizing that I'm not sure that
    Ackermann(4,2) is ( 2 ** 65533 - 3 ).  Looking at the other
    values of Ackermann(4,n) that I can find, it might be ( 2 ** 65536
    - 3 ).  Either one I can find the value for using the Ultrix
    libmp functions.  If anybody knows which of those is correct
    please let me know.
   
    The problem has me interested again, for the simple reasons that
    the problem is there (like Mt. Everest) it is there, I CAN find
    the solution, but I'm not sure if it is right.
     
    Other interesting Ackermann's:
    
    	Ackermann(5,0) = 65533
    	Ackermann(3,n) = ( 2 ** ( m + n )) - 3
    	Ackermann(2,n) = ( 2 * ( m + n + 1 )) - 3
    
    					Alan
    
   
108.12The answer is in your handTLE::AMARTINAlan H. MartinThu Oct 09 1986 14:409
Re .11:

It is possible to derive a closed-form expression for A(4,n) from your
closed form expression of A(3,n) (although it may require a new arithmetic
operator to express it properly).

Note that you haven't removed all the m's from your closed form expressions
for A(2,n) and A(3,n).
				/AHM
108.13A snake in hand...COOKIE::ROLLOWRedneck cookin'... Fryit!Fri Oct 10 1986 03:0946
	Something about the proverbial snake comes to mind...

	The closed forms:

		Ackermann(0,n) = n + 1 (the definition)
		Ackermann(1,n) = 2 + ( n + 3 ) - 3 = n + 2
		Ackermann(2,n) = 2 * ( n + 3 ) - 3 = 2n + 3
		Ackermann(3,n) = 2 ** ( n + 3 ) - 3

	Ackermann(4,n) I still haven't gotten.  I know that for
	(4,0) it is 13 and for (4,1) it is 65533.  I believe
	(from previous exposure to the problem) that (4,2) is
	2 ** 65534 - 3, but I can't prove it (1).  Possible
	solutions are:

		2 *** ( n + 3 ) - 3   (2)
		f(n) - 3
		2 ** g(n) - 3

	If anybody knows the iterative solution, let me know.  I'm
	better at finding the value, then the algorithm.

	I'm taking a couple of days vacation to go watch Oklahoma
	beat saxeT in Dallas, but will look in again Monday night.

	The source to the current version of my program will be
	in:
		COOKIE::DISK$NEWTON_2:[ROLLOW.SRC.ACKERMANN]ACKERMANN.C
	or:
		NABETH::"/usr/users/alan/src/Ackermann/Ackermann.c"
		(I think DECNET/Ultrix understands ~, so you might
		 be able to use ~alan for less typeing.)

				* * *

	Footnotes:

	1.  I had previously written this as 2 ** 65533 - 3, but
	2 ** 65534 - 3 was the value a found a couple of years ago
	so it is the one I'll go with.  It was induced from a sum-
	mation worked out by the person who had originally had the
	problem as an assignment.

	2.  The other Alan's "new" operator.
    
108.14A snake in the bush...COOKIE::ROLLOWRedneck cookin'... Fryit!Fri Oct 10 1986 14:1613
    Another version of Ackerman(4,0) and (4,1)
    
    	Ackermann(4,0) = ( Summation of 2 ** n for n = 1 to 3 ) - 1
    	Ackermann(4,1) = ( Summation of 2 ** n for n = 1 to 15 ) - 1
    
    My suggestion for Ackermann(4,2) was induced from:
    
    	( Summation of 2 ** n for n = 1 to 65533 ) - 1

    Well gotta leave before the phone line noise, blows me up...
    
    					Alan
    
108.15ack(4,2)=20035299... in a new (old) language.UTRUST::DEHARTOGAI is better than none!Tue Jul 21 1987 09:3616
If anyone is interested, I have the answer in a file that I can mail you.
Its 19729 digits and starts with 20035299... and ends with ...19156733.
To come back to the original subject of this notesfile: it was done in a
language that wasn't mentioned before. It's called TRAC, invented in the
early sixties by Calvin Mooers. Features: infinite length arithmetic,
recursion, powerful patternmatching (like Snobol), code=data (like Lisp),
no difference in deproceduring, evalutation, control, declaration and so on.
I have an implementation that runs under VMS, Ultrix and Venix.
Using the closed formula's for m=1,2,3, it looks as follows in TRAC:
 
:(ds,ackerman,(:(gr,1,#1,(:(ad,#2,1)),(:(eq,#1,1,(:(ad,#2,2)),(:(eq,#1,2,
(:(ad,#2,:(ad,#2,3))),(:(eq,#1,3,(:(su,:(pow,2,:(ad,#2,3)),3)),
(:(gr,#2,0,(:(ackerman,:(su,#1,1),:(ackerman,#1,:(su,#2,1)))),
(:(ackerman,:(su,#1,1),1))))))))))))):(ss,ackerman,#1,#2)

								Hans.
108.16RUSURE::EDPAlways mount a scratch monkey.Wed May 21 1997 15:2170
    Re .4:

    > As I recall, Ackermann's function was devised to prove that all
    > iterative algorithms could be written recursively, but that the
    > converse was not true.

    Ackermann's function is an example of a function that cannot be
    computed with bounded loops.  A bounded loop is one for which the
    number of iterations can be computed before the loop begins (like "for"
    loops with a variable going from 1 to n).  By contrast, unbounded loops
    iterate until some condition is reached (like "while" loops with a
    general Boolean expression).  No number of nested bounded loops can
    compute Ackermann's function.

    This is not about recursion versus iteration.  Every recursive function
    can be computed iteratively, with unbounded loops.

    > As I said, it took a LONG time for someone to come up with a clever
    > (and simple, once you saw it) way to compute Ackermann's iteratively.

    I don't think this was reported correctly.  There are standard
    techniques for converting recursive functions to iterative functions. 
    The demonstrations used in theory classes aren't necessarily efficient,
    but they could be made efficient without too much trouble.  Here is an
    example.  It uses arbitrarily large integers to store a stack.  We push
    a number on the stack by raising 2 to the power of the stack and
    multiplying by an odd number.  We pop a number from the stack by
    finding the largest power of 2 that divides the stack -- the quotient
    is the pushed odd number, and the power is the previous stack.  Since
    we do not always want to push an odd number, we use 2*k+1 as the odd
    multiplier when we want to push k.
    
    Given this stack, we simply make a loop that computes A(m,n).  When m
    and n are not zero, this is defined as A(m-1,A(m,n-1)), which we
    compute by pushing m-1 onto the stack and then computing A(m,n-1). 
    When the computation of A(m,n-1) is done, we pop the stack -- putting
    the computed value into n and restoring m by popping it from the stack.

    Compute A(m,n):

    stack=1;
    while (stack > 0) {
    	if (m==0) {
    		/* We are done computing something.  The answer is n+1,
    		   which we put in n, after popping the stack. */
    		t = largest power of 2 that divides stack;
    		m = (stack/t-1)/2;
    		stack = log base 2 of t;
    		n = n+1;
    	}
        else if (n==0) {
    		/* A(m,0) is just A(m-1,1). */
    		m = m-1;
    		n = 1;
    	}
    	else {
    		/* A(m,n) is A(m-1,A(m,n-1)).  Push m-1 and then set
    		   m and n to compute A(m,n-1). */
    		stack = (2 to the power of stack) * (2*m-1);
    		n = n-1;
    	}
    }
    return n;


    				-- edp


Public key fingerprint:  8e ad 63 61 ba 0c 26 86  32 0a 7d 28 db e7 6f 75
To find PGP, read note 2688.4 in Humane::IBMPC_Shareware.
108.17RUSURE::EDPAlways mount a scratch monkey.Thu May 22 1997 00:0412
    Here's a short loop to compute Ackermann's function, in C with very
    long integers:
    
    	s = t = m+2;
        while (s >= t)
            if (!m--)     m = s%t, s /= t, n++;
            else if (n++) s = s*t+m++, n -= 2;
    
    When the loop exits, n is the value of A(m,n).
    
    
    				-- edp