|
| 1 | +""" |
| 2 | +Print all the Catalan numbers from 0 to n, n being the user input. |
| 3 | +
|
| 4 | + * The Catalan numbers are a sequence of positive integers that |
| 5 | + * appear in many counting problems in combinatorics [1]. Such |
| 6 | + * problems include counting [2]: |
| 7 | + * - The number of Dyck words of length 2n |
| 8 | + * - The number well-formed expressions with n pairs of parentheses |
| 9 | + * (e.g., `()()` is valid but `())(` is not) |
| 10 | + * - The number of different ways n + 1 factors can be completely |
| 11 | + * parenthesized (e.g., for n = 2, C(n) = 2 and (ab)c and a(bc) |
| 12 | + * are the two valid ways to parenthesize. |
| 13 | + * - The number of full binary trees with n + 1 leaves |
| 14 | +
|
| 15 | + * A Catalan number satisfies the following recurrence relation |
| 16 | + * which we will use in this algorithm [1]. |
| 17 | + * C(0) = C(1) = 1 |
| 18 | + * C(n) = sum(C(i).C(n-i-1)), from i = 0 to n-1 |
| 19 | +
|
| 20 | + * In addition, the n-th Catalan number can be calculated using |
| 21 | + * the closed form formula below [1]: |
| 22 | + * C(n) = (1 / (n + 1)) * (2n choose n) |
| 23 | +
|
| 24 | + * Sources: |
| 25 | + * [1] https://brilliant.org/wiki/catalan-numbers/ |
| 26 | + * [2] https://en.wikipedia.org/wiki/Catalan_number |
| 27 | +""" |
| 28 | + |
| 29 | + |
| 30 | +def catalan_numbers(upper_limit: int) -> "list[int]": |
| 31 | + """ |
| 32 | + Return a list of the Catalan number sequence from 0 through `upper_limit`. |
| 33 | +
|
| 34 | + >>> catalan_numbers(5) |
| 35 | + [1, 1, 2, 5, 14, 42] |
| 36 | + >>> catalan_numbers(2) |
| 37 | + [1, 1, 2] |
| 38 | + >>> catalan_numbers(-1) |
| 39 | + Traceback (most recent call last): |
| 40 | + ValueError: Limit for the Catalan sequence must be ≥ 0 |
| 41 | + """ |
| 42 | + if upper_limit < 0: |
| 43 | + raise ValueError("Limit for the Catalan sequence must be ≥ 0") |
| 44 | + |
| 45 | + catalan_list = [0] * (upper_limit + 1) |
| 46 | + |
| 47 | + # Base case: C(0) = C(1) = 1 |
| 48 | + catalan_list[0] = 1 |
| 49 | + if upper_limit > 0: |
| 50 | + catalan_list[1] = 1 |
| 51 | + |
| 52 | + # Recurrence relation: C(i) = sum(C(j).C(i-j-1)), from j = 0 to i |
| 53 | + for i in range(2, upper_limit + 1): |
| 54 | + for j in range(i): |
| 55 | + catalan_list[i] += catalan_list[j] * catalan_list[i - j - 1] |
| 56 | + |
| 57 | + return catalan_list |
| 58 | + |
| 59 | + |
| 60 | +if __name__ == "__main__": |
| 61 | + print("\n********* Catalan Numbers Using Dynamic Programming ************\n") |
| 62 | + print("\n*** Enter -1 at any time to quit ***") |
| 63 | + print("\nEnter the upper limit (≥ 0) for the Catalan number sequence: ", end="") |
| 64 | + try: |
| 65 | + while True: |
| 66 | + N = int(input().strip()) |
| 67 | + if N < 0: |
| 68 | + print("\n********* Goodbye!! ************") |
| 69 | + break |
| 70 | + else: |
| 71 | + print(f"The Catalan numbers from 0 through {N} are:") |
| 72 | + print(catalan_numbers(N)) |
| 73 | + print("Try another upper limit for the sequence: ", end="") |
| 74 | + except (NameError, ValueError): |
| 75 | + print("\n********* Invalid input, goodbye! ************\n") |
| 76 | + |
| 77 | + import doctest |
| 78 | + |
| 79 | + doctest.testmod() |
0 commit comments