Ackermann's function using Dynamic programming
Last Updated :
26 Sep, 2022
Given two non-zero integers M and N, the problem is to compute the result of the Ackermann function based on some particular equations.
Ackermann function is defined as: 
Examples:
Input: M = 2, N = 2
Output: 7
Input: M = 2, N = 7
Output: 6141004759
The approach for Ackermann function described in this article, takes a very huge amount of time to compute the value for even small values of (M, N) or in most cases doesn't result in anything.
Dynamic Programming approach:
Here are the following Ackermann equations that would be used to come up with efficient solution.
A(m, n) = A(m-1, A(m, n-1)) ----- (Eq 1)
A(0, n) = n+1 ----- (Eq 2)
A(m, 0) = A(m-1, 1) ----- (Eq 3)
Let's assume the value of m = 2 and n = 2
A 2d DP table of size ( (m+1) x (n+1) ) is created for storing the result of each sub-problem.
Following are the steps demonstrated to fill up the table.
- Empty Table - Initial Step

Filled using A ( 0, n ) = n + 1 The very next method is to fill all the base values, by taking the help of equation-2.

In the next step the whole 1st row would be filled,
A ( 1, 0 ) = A ( 0, 1 ) ----- (refer Eq (3)) Since A ( 0, 1 ) = 2 Therefore, A ( 1, 0 ) = 2 -----(Eq 4)
A ( 1, 1 ) = A ( 0, A ( 1, 0 ) ) ----- refer Eq (1) = A ( 0, 2 ) ----- refer Eq (4) = 3 ----- refer Eq (2) So, A ( 1, 1 ) = 3 -----(Eq 5)
A ( 1, 2 ) = A ( 0, A ( 1, 1 ) ) ----- refer equation (1) = A ( 0, 3 ) ----- refer equation (5) = 4 ----- refer equation (2) So, A ( 1, 2 ) = 4 -----(Eq 6)
- Fill the table using equations and stored values

Let's just fill the first column of the last row i.e (2, 0) in the same manner as above, because for the other two columns there are some unsolved values.
A ( 2, 0 ) = A ( 1, 1 ) ----- refer equation (3) A ( 1, 1 ) = 3 So, A ( 2, 0 ) = 3 ----- (Eq. 7)

Solving for A ( 2, 1 ) and A ( 2, 2 ).
For simplicity the process for solving above functions is divided into two steps,
- In the first one, the problem is identified.
A ( 2, 1 ) = A ( 1, A ( 2, 0 ) ) ----- refer equation (1) A ( 2, 0 ) = 3 A ( 2, 1 ) = A ( 1, 3 ) So to compute this value, again use equation (1) A ( 1, 3 ) = A ( 0, A ( 1, 2 ) ) A ( 1, 2 ) = 4 A ( 1, 3 ) = A ( 0, 4 ) ----- refer equation(2) = 5 Therefore A ( 2, 1 ) = A ( 1, 3 ) = 5 --- (Eq 7)

- In the next one, methodology is described in detail and a generic formula is obtained to be logical while being used in program
Let's solve A ( 2, 2 ) , with a theory upfront
A ( 2, 2 ) = A ( 1, A ( 2, 1 ) ) ----- refer equation (1) A ( 2, 1) = 5 A ( 2, 2 ) = A ( 1, 5 )
To compute A(1, 5) in a generic manner, observe how it reduces itself!
A ( 1, 5 ) = A ( 0, A ( 1, 4 ) ) A ( 1, 4 ) = A( 0, A ( 1, 3 ) ) A ( 1, 3 ) = A ( 0, A ( 1, 2 ) ) A ( 1, 2 ) = 4 Returning back from the function we get, A ( 1, 3 ) = A ( 0, 4 ) = 5 ----- refer equation (2) A ( 1, 4 ) = A ( 0, A (1, 3 ) ) = A ( 0, 5 ) = 6 -----Since A ( 1, 3 ) = 5 A ( 1, 5 ) = A ( 0, A ( 1, 4 ) ) = A ( 0, 6 ) = 7 So, A ( 2, 2 ) = 7 ------- (Eq 9)
- Final Table with result of each sub-problem

Below is the implementation of the above approach:
C++
#include <iostream>
#include <string>
#include <vector>
using namespace std;
// Bottom Up Approach
long long int Ackermann(int m, int n){
// creating 2D LIST
vector<vector<long long int>> cache(m + 1, vector<long long int>(n + 1, 0));
for (int rows = 0 ; rows < m + 1 ; rows++){
for (int cols = 0 ; cols < n + 1 ; cols++){
// base case A ( 0, n ) = n + 1
if (rows == 0){
cache[rows][cols] = cols + 1;
}
// base case A ( m, 0 ) =
// A ( m-1, 1) [Computed already]
else if (cols == 0)
{
cache[rows][cols] = cache[rows-1][1];
}
else
{
// if rows and cols > 0
// then applying A ( m, n ) =
// A ( m-1, A ( m, n-1 ) )
long long int r = rows - 1;
long long int c = cache[rows][cols-1];
long long int ans;
// applying equation (2)
// here A ( 0, n ) = n + 1
if (r == 0){
ans = c + 1;
}else if (c <= n){
// using stored value in cache
ans = cache[rows-1][cache[rows][cols-1]];
}else{
// Using the Derived Formula
// to compute mystery values in O(1) time
ans = (c-n)*(r) + cache[r][n];
}
cache[rows][cols] = ans;
}
}
}
return cache[m][n];
}
// Driver code
int main()
{
// very small values
int m = 2;
int n = 2;
// a bit higher value
int m1 = 5;
int n1 = 7;
cout << "Ackermann value for m = " << m <<
" and n = " << n << " is -> " <<
Ackermann(m, n) << endl;
cout << "Ackermann value for m = " <<
m1 << " and n = " << n1 << " is -> " <<
Ackermann(m1, n1) << endl;
return 0;
}
// This code is contributed by subhamgoyal2014.
Java
// Java program to implement the approach
import java.util.*;
class GFG {
// Bottom Up Approach
static long Ackermann(int m, int n)
{
// creating 2D LIST
ArrayList<ArrayList<Long> > cache
= new ArrayList<ArrayList<Long> >();
for (int i = 0; i <= m; i++) {
cache.add(new ArrayList<Long>());
for (int j = 0; j <= n; j++) {
ArrayList<Long> l1 = cache.get(i);
l1.add(0L);
cache.set(i, l1);
}
}
for (int rows = 0; rows < m + 1; rows++) {
for (int cols = 0; cols < n + 1; cols++) {
// base case A ( 0, n ) = n + 1
if (rows == 0) {
ArrayList<Long> l1 = cache.get(rows);
l1.set(cols, cols + 1L);
cache.set(rows, l1);
}
// base case A ( m, 0 ) =
// A ( m-1, 1) [Computed already]
else if (cols == 0) {
ArrayList<Long> l1 = cache.get(rows);
l1.set(cols,
cache.get(rows - 1).get(1));
cache.set(rows, l1);
}
else {
// if rows and cols > 0
// then applying A ( m, n ) =
// A ( m-1, A ( m, n-1 ) )
long r = rows - 1;
long c = cache.get(rows).get(cols - 1);
long ans;
// applying equation (2)
// here A ( 0, n ) = n + 1
if (r == 0) {
ans = c + 1;
}
else if (c <= n) {
// using stored value in cache
ans = cache.get(rows - 1).get(
Math.toIntExact(
cache.get(rows).get(
(int)cols - 1)));
}
else {
// Using the Derived Formula
// to compute mystery values in O(1)
// time
ans = (c - n) * (r)
+ cache.get((int)r).get(n);
}
ArrayList<Long> l1 = cache.get(rows);
l1.set(cols, ans);
cache.set(rows, l1);
}
}
}
return cache.get(m).get(n);
}
// Driver code
public static void main(String[] args)
{
// very small values
int m = 2;
int n = 2;
// a bit higher value
int m1 = 5;
int n1 = 7;
System.out.println("Ackermann value for m = " + m
+ " and n = " + n + " is -> "
+ Ackermann(m, n));
System.out.println("Ackermann value for m = " + m1
+ " and n = " + n1 + " is -> "
+ Ackermann(m1, n1));
}
}
// This code is contributed by phasing17.
Python3
# Python code for the above approach
# Bottom Up Approach
def Ackermann(m, n):
# creating 2D LIST
cache = [[0 for i in range(n + 1)] for j in range(m + 1)]
for rows in range(m + 1):
for cols in range(n + 1):
# base case A ( 0, n ) = n + 1
if rows == 0:
cache[rows][cols] = cols + 1
# base case A ( m, 0 ) =
# A ( m-1, 1) [Computed already]
elif cols == 0:
cache[rows][cols] = cache[rows-1][1]
else:
# if rows and cols > 0
# then applying A ( m, n ) =
# A ( m-1, A ( m, n-1 ) )
r = rows - 1
c = cache[rows][cols-1]
# applying equation (2)
# here A ( 0, n ) = n + 1
if r == 0:
ans = c + 1
elif c <= n:
# using stored value in cache
ans = cache[rows-1][cache[rows][cols-1]]
else:
# Using the Derived Formula
# to compute mystery values in O(1) time
ans = (c-n)*(r) + cache[r][n]
cache[rows][cols] = ans
return cache[m][n]
# very small values
m = 2
n = 2
# a bit higher value
m1 = 5
n1 = 7
print("Ackermann value for m = ", m,
" and n = ", n, "is -> ",
Ackermann(m, n))
print("Ackermann value for m = ", m1,
" and n = ", n1, "is -> ",
Ackermann(m1, n1))
C#
// C# program to implement the approach
using System;
using System.Collections.Generic;
class GFG {
// Bottom Up Approach
static long Ackermann(int m, int n)
{
// creating 2D LIST
List<List<long> > cache = new List<List<long> >();
for (int i = 0; i <= m; i++) {
cache.Add(new List<long>());
for (int j = 0; j <= n; j++)
cache[i].Add(0);
}
for (int rows = 0; rows < m + 1; rows++) {
for (int cols = 0; cols < n + 1; cols++) {
// base case A ( 0, n ) = n + 1
if (rows == 0) {
cache[rows][cols] = cols + 1;
}
// base case A ( m, 0 ) =
// A ( m-1, 1) [Computed already]
else if (cols == 0) {
cache[rows][cols] = cache[rows - 1][1];
}
else {
// if rows and cols > 0
// then applying A ( m, n ) =
// A ( m-1, A ( m, n-1 ) )
long r = rows - 1;
long c = cache[rows][cols - 1];
long ans;
// applying equation (2)
// here A ( 0, n ) = n + 1
if (r == 0) {
ans = c + 1;
}
else if (c <= n) {
// using stored value in cache
ans = cache[rows - 1][(
int)cache[rows][cols - 1]];
}
else {
// Using the Derived Formula
// to compute mystery values in O(1)
// time
ans = (c - n) * (r)
+ cache[(int)r][n];
}
cache[rows][cols] = ans;
}
}
}
return cache[m][n];
}
// Driver code
public static void Main(string[] args)
{
// very small values
int m = 2;
int n = 2;
// a bit higher value
int m1 = 5;
int n1 = 7;
Console.WriteLine("Ackermann value for m = " + m
+ " and n = " + n + " is -> "
+ Ackermann(m, n));
Console.WriteLine("Ackermann value for m = " + m1
+ " and n = " + n1 + " is -> "
+ Ackermann(m1, n1));
}
}
// This code is contributed by phasing17.
JavaScript
// JS program to implement the approach
// Bottom Up Approach
function Ackermann(m, n)
{
// creating 2D LIST
let cache = new Array(m + 1);
for (var i = 0; i <= m; i++)
cache[i] = new Array(n + 1).fill(0);
for (var rows = 0 ; rows < m + 1 ; rows++){
for (var cols = 0 ; cols < n + 1 ; cols++){
// base case A ( 0, n ) = n + 1
if (rows == 0){
cache[rows][cols] = cols + 1;
}
// base case A ( m, 0 ) =
// A ( m-1, 1) [Computed already]
else if (cols == 0)
{
cache[rows][cols] = cache[rows-1][1];
}
else
{
// if rows and cols > 0
// then applying A ( m, n ) =
// A ( m-1, A ( m, n-1 ) )
let r = rows - 1;
let c = cache[rows][cols-1];
let ans;
// applying equation (2)
// here A ( 0, n ) = n + 1
if (r == 0){
ans = c + 1;
}else if (c <= n){
// using stored value in cache
ans = cache[rows-1][cache[rows][cols-1]];
}else{
// Using the Derived Formula
// to compute mystery values in O(1) time
ans = (c-n)*(r) + cache[r][n];
}
cache[rows][cols] = ans;
}
}
}
return cache[m][n];
}
// Driver code
// very small values
let m = 2;
let n = 2;
// a bit higher value
let m1 = 5;
let n1 = 7;
console.log("Ackermann value for m = " + m +
" and n = " + n + " is -> " +
Ackermann(m, n));
console.log("Ackermann value for m = " +
m1 + " and n = " + n1 + " is -> " +
Ackermann(m1, n1));
// This code is contributed by phasing17.
OutputAckermann value for m = 2 and n = 2 is -> 7
Ackermann value for m = 5 and n = 7 is -> 6141004759
Time complexity: O( M * N )
Auxiliary Space: O( M * N )
Similar Reads
Dynamic Programming (DP) Introduction
Dynamic Programming is a commonly used algorithmic technique used to optimize recursive solutions when same subproblems are called again.The core idea behind DP is to store solutions to subproblems so that each is solved only once. To solve DP problems, we first write a recursive solution in a way t
15+ min read
Dynamic Programming (DP) on Arrays Tutorial
We know that Dynamic Programming is a way to reduce the time complexity of a problem using memoization or tabulation of the overlapping states. While applying DP on arrays the array indices act as DP states and transitions occurs between indices. Table of Content How to Identify if Array problem has
6 min read
Convert N to M with given operations using dynamic programming
Given two integers N and M and the task is to convert N to M with the following operations: Multiply N by 2 i.e. N = N * 2.Subtract 1 from N i.e. N = N - 1. Examples: Input: N = 4, M = 6 Output: 2 Perform operation 2: N = N - 1 = 4 - 1 = 3 Perform operation 1: N = N * 2 = 3 * 2 = 6 Input: N = 10, M
7 min read
Python | Implementing Dynamic programming using Dictionary
Dynamic Programming is one way which can be used as an optimization over plain recursion. Wherever we see a recursive solution that has repeated calls for the same inputs, we can optimize it using Dynamic Programming. The idea is to simply store the results of subproblems so that we do not have to r
3 min read
Introduction to Dynamic Programming on Trees
Dynamic Programming is a technique to solve problems by breaking them down into overlapping sub-problems which follows the optimal substructure. There are various problems using DP like subset sum, knapsack, coin change etc. DP can also be applied to trees to solve some specific problems.Given a bin
13 min read
Dynamic Programming in Python
Dynamic Programming is a commonly used algorithmic technique used to optimize recursive solutions when same subproblems are called again.The core idea behind DP is to store solutions to subproblems so that each is solved only once.To solve DP problems, we first write a recursive solution in a way th
7 min read
Dynamic Programming (DP) on Grids
Grid problems involve a 2D grid of cells, often representing a map or graph. We can apply Dynamic Programming on Grids when the solution for a cell is dependent on solutions of previously traversed cells like to find a path or count number of paths or solve an optimization problem across the grid, w
11 min read
Dynamic Programming or DP
Dynamic Programming is an algorithmic technique with the following properties.It is mainly an optimization over plain recursion. Wherever we see a recursive solution that has repeated calls for the same inputs, we can optimize it using Dynamic Programming. The idea is to simply store the results of
3 min read
Dynamic Programming meaning in DSA
Dynamic Programming is defined as an algorithmic technique that is used to solve problems by breaking them into smaller subproblems and avoiding repeated calculation of overlapping subproblems and using the property that the solution of the problem depends on the optimal solution of the subproblems
2 min read
How Does Dynamic Programming Work?
Dynamic programming, popularly known as DP, is a method of solving problems by breaking them down into simple, overlapping subproblems and then solving each of the subproblems only once, storing the solutions to the subproblems that are solved to avoid redundant computations. This technique is usefu
15 min read