17

I have function

public static int func(int M,int N){
    if(M == 0 || N == 0) return M+N+1;
    return func(M-1, func(M, N-1));
}

How to rewrite it in non-recursive style ? Maybe, is it implementation some algorithm?

Bill the Lizard
  • 398,270
  • 210
  • 566
  • 880
BILL
  • 4,711
  • 10
  • 57
  • 96
  • 1
    `while(M != 0 || N != 0){\\todo}` – BILL May 24 '12 at 17:23
  • 3
    Smells like homework. What have you tried? – David B May 24 '12 at 17:23
  • 1
    This should give you ideas: http://stackoverflow.com/questions/616416/is-it-possible-to-remove-recursion-from-this-function – assylias May 24 '12 at 17:24
  • 3
    Due to the nested `func` call this is way from trivial to un-recurse. Good luck :) – Marko Topolnik May 24 '12 at 17:59
  • 1
    @MarkoTopolnik I agree, I've been staring at it for a while and can't come up with a good solution without just declaring a stack and using it to simulate the recursion as assylias link describes. Very hard assignment if this is indeed homework. – Kevin DiTraglia May 24 '12 at 18:02
  • 1
    @KDiTraglia This looks more like practical joke, a well-known (in "certain" circles) uncomputable function. I can't even get it to terminate for something as simple as (4,2). I bet that "func" has a real name, but it's next to un-googlable. – Marko Topolnik May 24 '12 at 18:13
  • 1
    evil professor? haha. I did notice it stack overflows pretty quickly with pretty small values for M and N – Kevin DiTraglia May 24 '12 at 18:15
  • 1
    It is http://en.wikipedia.org/wiki/Double_recursion – BILL May 24 '12 at 18:16
  • 3
    It seems to me, or this function is similar to http://en.wikipedia.org/wiki/Ackermann_function ? – BILL May 24 '12 at 18:18
  • 1
    @KDiTraglia When I was working it out in my answer, I noticed that this function is indeed quite a toughy. The math isn't difficult, its that it has a very big-O. – David B May 24 '12 at 18:28
  • 1
    Victor, I think you've got it. This is clearly a variation on the Ackermann function. The WP page even says that all the variations still go by the same name. And it even escapes for the same arguments (4,2) as mine. – Marko Topolnik May 24 '12 at 18:28
  • @MarkoTopolnik: can't be implemented on a computer??? According to Wikipedia, the Ackermann function *is* computable, and illustrates that not all total computable functions are primitive recursive. Doesn't it just mean that it can't be implemented without some kind of stack? (either a call stack or a manual stack data structure like the accepted answer). – Peter Cordes Oct 22 '17 at 01:52

8 Answers8

16

Not quite O(1) but definitely non-recursive.

public static int itFunc(int m, int n){
    Stack<Integer> s = new Stack<Integer>;
    s.add(m);
    while(!s.isEmpty()){
        m=s.pop();
        if(m==0||n==0)
            n+=m+1;
        else{
            s.add(--m);
            s.add(++m);
            n--;
        }
    }
    return n;
}
Lightyear Buzz
  • 796
  • 7
  • 28
  • I get 4 for m = 2, n = 1. n=1,s = {2} -> n=1, m=2 -> n=0, s={3,1} -> n=0, m=3 -> n = 3 + 1 = 4. Unless my math was wrong on f(2,1) on my answer? – David B May 24 '12 at 20:16
  • Yeah your math is wrong. I checked it against the code he provided. – Lightyear Buzz May 24 '12 at 20:18
  • Where did I go wrong? I must be seeing a disconnect, because going over my math gives me a correct answer. :S – David B May 24 '12 at 20:21
  • I've run my answer through [WolframAlpha](http://www.wolframalpha.com/input/?i=ackermann%282%2C1%29&dataset=) and I'm not wrong. This answer is *not* incorrect. EDIT: My following of your code was wrong, I apologize. I thought you meant my math in my answer was wrong. – David B May 24 '12 at 20:41
  • I implemented recursive way, ack(3,4) = 125, but your algorithm implementation give ack(3,4) = 109. Is this right? – Andiana Feb 21 '16 at 09:33
  • The implementation is somehow wrong. ack(4,1) returns 221 while it should return 65533. – Konrad Apr 11 '16 at 20:31
  • 2
    @Lightyear Buzz This implementation seems to go wrong from m=3, n=0 onwards. (Answer from Wolfram Alpha is 5, this code returns 4) I'd really appreciate an update, I don't understand the code above enough to be able to do it myself - any help appreciated, Thanks! – Steve Moseley Jun 17 '17 at 00:05
7

All the answers posted previously don't properly implement Ackermann.

def acker_mstack(m, n)
  stack = [m]
  until stack.empty?
    m = stack.pop

    if m.zero?
      n += 1
    elsif n.zero?
      stack << m - 1
      n = 1
    else
      stack << m - 1 << m
      n -= 1
    end
  end
  n
end
Kache
  • 15,647
  • 12
  • 51
  • 79
  • Thanks! Could you explain how you achieved this? I'm not sure how this can be generalized to a function with a different arity, for example factorial takes only one arg. How would that translate to a looping version with a custom stack? – divs1210 Jul 15 '19 at 11:07
  • First, understand recursion. Second, reuse the body of a recursive function, refactored to have only one entry and exit point. Third, implement the call stack manually by pushing and popping inputs and outputs for every function call, which is what the language runtime normally does for you. For a non-linear algorithm, you'll need a non-linear data structure. Lastly, refactor and simplify. – Kache Jul 16 '19 at 19:14
6

This looks like homework, so I won't give you the answer but I will lead you in the right direction:

If you want to breakdown the recursion, it might be useful for you to list out all the values as they progress, letting m = {0...x} n = {0...y}.

For example:

m = 0, n = 0 = f(0,0) = M+N+1 = 1
m = 1, n = 0 = f(1,0) = M+N+1 = 2
m = 1, n = 1 = f(1,1) = f(0,f(1,0)) = f(0,2) = 3
m = 2, n = 1 = f(2,1) = f(1,f(2,0)) = f(1,3) = f(0,f(1,2)) = f(0,f(0,f(1,1))
             = f(0,f(0,3))          = f(0,4) = 5

With this, you can come up with a non-recursive relationship (a non-recursive function definition) that you can use.

Edit: So it looks like this is the Ackermann function, a total computable function that is not primitive recursive.

David B
  • 2,688
  • 18
  • 25
2

This is the a correct version which already examined by myself.

public static int Ackermann(int m, int n){
Stack<Integer> s = new Stack<Integer>;
s.add(m);
while(!s.isEmpty()){
    m=s.pop();
    if(m==0) { n+=m+1; }
    else if(n==0)
    {
       n += 1;
       s.add(--m);
    }
    else{
        s.add(--m);
        s.add(++m);
        n--;
    }
}
return n;
}
Ray Xie
  • 171
  • 1
  • 1
  • 8
  • This seems to be a very minor variation on @LightyearBuzz's answer, where you de-optimized by using the stack for the `n==0` case to do each increment one at a time, instead of Lightyear's `n+=m+1;`. Did you really write the whole thing yourself, or is this mostly copied (without credit) from the other answer? – Peter Cordes Oct 22 '17 at 01:58
  • This answer just makes a slightly change of LightyearBuzz`s answer, and Im sorry for using his code without informing him. Because I saw his code cant pass every examples so I did a little change to correct it. If you dont like it , I can delete it. But I just want people can see the correct answer^^ – Ray Xie Oct 23 '17 at 05:26
  • What you *should* do is [edit] your answer to say what bug it fixes. (i.e. which input gives the wrong answer). e.g. "LightyearBuzz's answer is wrong for `A(blah, blah)`. I fixed it by changing blah blah. Here's the full code:" – Peter Cordes Oct 23 '17 at 05:33
  • This fixes the problem with the accepted answer. For reference, `A(4, 1)` takes ~ 60 seconds to compute. – BurnsBA Oct 05 '20 at 19:20
0

I couldn't get @LightyearBuzz's answer to work, but I found this Java 5 code from WikiWikiWeb that worked for me:

import java.util.HashMap;
import java.util.Stack;

public class Ackerman {
  static class  Pair <T1,T2>{
    T1 x; T2 y;
    Pair(T1 x_,T2 y_) {x=x_; y=y_;}
    public int hashCode() {return x.hashCode() ^ y.hashCode();}
    public boolean equals(Object o_) {Pair o= (Pair) o_; return x.equals(o.x) && y.equals(o.y);}
  }

  /**
   * @param args
   */
  public static int ack_iter(int m, int n) {
    HashMap<Pair<Integer,Integer>,Integer> solved_set= new HashMap<Pair<Integer,Integer>,Integer>(120000);
    Stack<Pair<Integer,Integer>> to_solve= new Stack<Pair<Integer,Integer>>();
    to_solve.push(new Pair<Integer,Integer>(m,n));

    while (!to_solve.isEmpty()) {
      Pair<Integer,Integer> head= to_solve.peek();
      if (head.x.equals(0) ) {
        solved_set.put(head,head.y + 1);
        to_solve.pop();
      }
      else if (head.y.equals(0)) {
        Pair<Integer,Integer> next= new Pair<Integer,Integer> (head.x-1,1);
        Integer result= solved_set.get(next);
        if(result==null){
          to_solve.push(next);
        } 
        else {
          solved_set.put(head, result);
          to_solve.pop();
        }
      }
      else {
        Pair<Integer,Integer> next0= new Pair<Integer,Integer>(head.x, head.y-1);
        Integer result0= solved_set.get(next0);
        if(result0 == null) {
          to_solve.push(next0);
        }
        else {
          Pair<Integer,Integer> next= new Pair<Integer,Integer>(head.x-1,result0);
          Integer result= solved_set.get(next);
          if (result == null) {
            to_solve.push(next);
          }
          else {
            solved_set.put(head,result);
            to_solve.pop();
          }
        }
      }
    }
    System.out.println("hash size: "+solved_set.size());
    System.out.println("consumed heap: "+ (Runtime.getRuntime().totalMemory()/(1024*1024)) + "m");
    return solved_set.get(new Pair<Integer,Integer>(m,n));
  }
}
Nick S
  • 555
  • 4
  • 17
0

Written in python, using only 1 array and 1 variable, hope this helps!

def acker(m,n):

    right = [m]
    result = n
    i = 0

    while True:
        if len(right) == 0:
            break

        if right[i] > 0 and result > 0:
            right.append(right[i])
            right[i] -= 1
            result -= 1
            i += 1

        elif right[i] > 0 and result == 0:
            right[i] -= 1
            result = 1

        elif right[i] == 0:
            result += 1
            right.pop()
            i -=1

    return result
Darwin Harianto
  • 435
  • 4
  • 15
0

Well I came here to find the answer of this question. But could not write a code even after looking at the answers. So, I tried it myself and after some struggle built the code. So, I will give you a hint (because I feel etiquettes here are that the homework questions are not meant to be fully answered). So you can use a single stack to compute the function without using recursion. Just look at the flow of control in David's answer. You have to use that. Just start a while(1) loop and inside that check for the case your arguments are satisfying. Let the desired block amongst if-else blocks execute.Then push the two latest arguments of ackerman function into the stack. Then at the end of the loop pop them and let the cycle repeat till an end condition is reached where no further arguments of ackermann function are generated. You have to put a for statement inside the while loop to keep checking it. And finally get the final results. I don't know how much of this is understandable, but I wish I could have some idea to start with. So, just shared the way.

Bhuvnesh
  • 101
  • 4
0

Written using C++. Stack stores only m values and works fine for all inputs

int ackermann(int m, int n) {
    stack<int> s;
    s.push(m);
    while(!s.empty()) {
        m = s.top();
        s.pop();
        if(m == 0) {
            n++;
        }
        else if(n == 0) {
            s.push(--m);
            n = 1;
        }
        else {
            s.push(m-1);
            s.push(m);
            n--;
        }
    }
    return n;
}
Diego Borba
  • 1,282
  • 8
  • 22
  • I see this is your first answer, which is awesome. But since there's already an accepted answer, new answers should try to improve on it, or at least update outdated information. Your code is in another language, so why post the answer? – Diego Borba Aug 10 '23 at 19:08