How to Compute the Catalan Numbers using Dynamic Programming Alg

  • Time:2020-09-15 16:10:27
  • Class:Weblog
  • Read:32

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:

  • The number of different ways to cut a convex polygon into triangles i.e. n triangles – Catalan(n) ways
  • The number of different ways i.e. Catalan(n) to form a full binary tree with n + 1 leaves
  • The number of monotonic lattice paths (which do not pass above the diagonal) along the edges of a grid on a N x N square cells.
  • Different ways to generate the parentheses: How to Generate Parentheses using Bruteforce or Depth First Search (Backtracking) Algorithms?
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) —

Recommend:
Summits set epoch-making milestone in history of China-Arab ties
In the face of COVID-19 pandemic, China and Arab countries have
15 Macao residents qualify as candidates for deputies to nationa
Study finds genetic solution to pre-harvest sprouting in rice, w
Bodybuilders dying as coaches, judges encourage extreme measures
Malta's Marsaskala, China's Dujiangyan sign sister city agreemen
U.S. mortgage applications continue slide amid surging interest
Russian, UAE presidents discuss bilateral cooperation over phone
Hate crimes in U.S. Los Angeles County rise to highest level sin
Chinese mainland reports 4,031 new local confirmed COVID-19 case
Share:Facebook Twitter
Comment list
Comment add