How to Compute the Catalan Numbers using Dynamic Programming Algorithm?


Catalan numbers are popular in Combinatoric Mathematics. Many interesting counting problems tend to be solved using the Catalan numbers. The first few Catalan numbers are:

1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862, 16796, 58786, 208012, 742900, 2674440, 9694845, 35357670, 129644790, 477638700, 1767263190, 6564120420, 24466267020, 91482563640, 343059613650, 1289904147324, 4861946401452

A few interesting Catalan counting problems:

catalan-numbers-applications How to Compute the Catalan Numbers using Dynamic Programming Algorithm? algorithms c / c++ Combinatoric Mathematics math

catalan-numbers-applications

Catalan Numbers Math Formula

The Catalan number can be computed in the simple form:

catalan-numbers-formula How to Compute the Catalan Numbers using Dynamic Programming Algorithm? algorithms c / c++ Combinatoric Mathematics math

catalan-numbers-formula

There are several ways to compute the nth Catalan number. In order to compute the Catalan numbers in Dynamic Programming, we can use the following recurrence relation:

catalan-numbers-recurrence-relations How to Compute the Catalan Numbers using Dynamic Programming Algorithm? algorithms c / c++ Combinatoric Mathematics math

catalan-numbers-recurrence-relations

Alternatively, the following is a simple recurrence relation between Catalan number nth and nth+1.

catalan-numbers-recurrence-relations-simple How to Compute the Catalan Numbers using Dynamic Programming Algorithm? algorithms c / c++ Combinatoric Mathematics math

catalan-numbers-recurrence-relations-simple

See this implementation based on the above simple Catalan formula: How to Compute the Catalan Number?

How to Compute the Catalan using Recursions?

The Catalan numbers grow quickly (exponentially) and will overflow if the N is large. The following is a naive implementation of the Catalan formula.

1
2
3
4
5
6
7
8
int64_t catlan(int n) {
    if (n <= 1) return 1;
    int res = 0;
    for (int i = 0; i < n; i ++) {
        res += catlan(i) * catlan(n - i - 1);
    }
    return res;
}
int64_t catlan(int n) {
    if (n <= 1) return 1;
    int res = 0;
    for (int i = 0; i < n; i ++) {
        res += catlan(i) * catlan(n - i - 1);
    }
    return res;
}

However, this implementation is not practically usable as the performance complexity is exponential. The intermediate catalan numbers are computed over and over again.

Dynamic Programming: Computing Catalan Numbers using Recursion + Memoization

We can improve the above recursive implementation into Dynamic Programming by simply using a hash map to store the intermediate calculations e.g. the Memoization.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
unordered_map<int, int64_t> memo;
 
int64_t catlan(int n) {
    if (n <= 1) return 1;
    if (memo.find(n) != memo.end()) {
        // avoid duplicate calculations       
        return memo[n];
    }
    int res = 0;
    for (int i = 0; i < n; i ++) {
        res += catlan(i) * catlan(n - i - 1);
    }
    memo[n] = res; // store into the memo
    return res;
}
unordered_map<int, int64_t> memo;

int64_t catlan(int n) {
    if (n <= 1) return 1;
    if (memo.find(n) != memo.end()) {
        // avoid duplicate calculations       
        return memo[n];
    }
    int res = 0;
    for (int i = 0; i < n; i ++) {
        res += catlan(i) * catlan(n - i - 1);
    }
    memo[n] = res; // store into the memo
    return res;
}

The intermediate calculations are computed and stored into the hash map for later retrieval. The overall time complexity is N square and the space requirement is O(N).

Dynamic Programming: Computing Catalan Numbers using Iterative Approach

Requiring a O(N) vector/array to store the Catalan numbers, we can do this purely iteratively in O(N^2) time complexity. This implementation eliminates the calling stacks due to recursion and is the most efficient (in terms of time and space) among the three implementations presented in this post.

1
2
3
4
5
6
7
8
9
10
11
int catlan(int n) {
    vector<int64_t> dp(n + 1, 0);
    dp[0] = 1;        
    dp[1] = 1;
    for (int i = 2; i <= n; i ++) {
        for (int j = 0; j < i; ++ j) {
            dp[i] += dp[j] * dp[i - j - 1];
        }
    }
    return (int)dp.back();
}
int catlan(int n) {
    vector<int64_t> dp(n + 1, 0);
    dp[0] = 1;        
    dp[1] = 1;
    for (int i = 2; i <= n; i ++) {
        for (int j = 0; j < i; ++ j) {
            dp[i] += dp[j] * dp[i - j - 1];
        }
    }
    return (int)dp.back();
}

You may be interested in this post: How to Compute the Catalan Number?

–EOF (The Ultimate Computing & Technology Blog) —

GD Star Rating
loading...
758 words
Last Post: C++ Coding Reference: Partial Sorting with nth_element from Algorithm header
Next Post: 5 Innovative Ways Ecommerce Businesses Are Leveraging Machine Learning

The Permanent URL is: How to Compute the Catalan Numbers using Dynamic Programming Algorithm?

Leave a Reply