For this level of response, use dynamic programming: memoize the function. This means that you keep a table of previous results. If you find a result that's already been computed, then you return it from the table. Only when it's a new call, do you do the computation -- and in that case, most or all of the recursive calls will be in the table. For instance:
import sys
sys.setrecursionlimit(30000)
memo = {}
def ack(m, n):
if not (m, n) in memo:
result = (n + 1) if m == 0 else (
ack(m-1, 1) if n == 0 else ack(m-1, ack(m, n-1)))
memo[(m, n)] = result
return memo[(m, n)]
print ack(3, 4)
print ack(4, 1)
print ack(4, 2)
You'll still have trouble with anything as large as ack(4, 2), due to memory use.
125
65533
Segmentation fault (core dumped)