I wrote a recursive version of Ackermann Function, and it worked properly:
int ackermann_r(int m, int n) {
if(m == 0) {
return n + 1;
} else if(n == 0) {
return ackermann_r(m - 1, 1);
} else {
return ackermann_r(m - 1, ackermann_r(m, n - 1));
}
}
Then I tried to rewrite the code iteratively:
(I don't know how to use 2D array using malloc, so you could feel the code is dirty...)
int ackermann_i(int m, int n) {
int* A = (int*) malloc((m+1) * (n+1) * sizeof(int));
for(int i = 0; i <= m; i++) {
for(int j = 0; j <= n; j++) {
if(i == 0) {
A[i*(n+1) + j] = j + 1;
} else if(j == 0) {
A[i*(n+1) + j] = A[(i-1)*(n+1) + 1];
} else {
A[i*(n+1) + j] = A[(i-1)*(n+1) + A[i*(n+1) + (j-1)]];
}
}
}
return A[m*(n+1) + n];
}
But the iterative version printed a wrong answer. For example:
m: 3
n: 2
recursive: 29
iterative: 3
Why my iterative code doesn't work?
Unfortunately, your code shows undefined behaviour due to access on an uninitialized value and out-of-bounds access. The simplest test that shows this behaviour is m = 1, n = 0. This indicates only two iterations of the outer loop and one iteration of the inner loop and thus is easier to analyze:
int ackermann_i(int m, int n) {
int* A = (int*) malloc((m+1) * (n+1) * sizeof(int));
for(int i = 0; i <= m; i++) {
for(int j = 0; j <= n; j++) {
if(i == 0) {
A[i*(n+1) + j] = j + 1; // (1)
} else if(j == 0) {
A[i*(n+1) + j] = A[(i-1)*(n+1) + 1]; // (2)
} else {
A[i*(n+1) + j] = A[(i-1)*(n+1) + A[i*(n+1) + (j-1)]]; // (3)
}
}
}
return A[m*(n+1) + n];
}
So let's iterate by hand:
i = 0, j = 0. We enter (1) and set A[0 + 0] = 1.i = 1, j = 0. We enter (2) and set A[2 + 0] = A[0 + 1].j == 0, so we don't care about (3).But there's the issue: we never set A[0 + 1]. That value might be zero, it might as well be something else at random; undefined behaviour ensues. Even worse, our A is not large enough: (m+1)*(n+1) is only 2 here, so A[2] is an out-of-bound array access.
This indicate two issues:
a(m, a(m-1,n)) can grow much larger than n.if we had a solution for that, we'd need to handle the trivial cases first, e.g.
for(int j = 0; j <= (n+1); ++j) {
A[0 + j] = j + 1; // set all A[i,j] where i = 0
}
There is however one deeper issue. Your code implies that the Ackermann function can be calculated in θ(m * n). That's however impossible. Instead, you need at least a stack or something similar that can grow in size to calculate the result. This implementation in Java provides some inspiration.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With