|
| 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 | +class Catalan: |
| 31 | + def __init__(self, N: int): |
| 32 | + self.catalan_numbers = [0] * (N + 1) |
| 33 | + if N: |
| 34 | + N = int(N) |
| 35 | + # Base case: C(0) = C(1) = 1 |
| 36 | + self.catalan_numbers[0] = 1 |
| 37 | + self.catalan_numbers[1] = 1 |
| 38 | + |
| 39 | + # Recurrence relation: C(i) = sum(C(j).C(i-j-1)), from j = 0 to i |
| 40 | + for i in range(2, N + 1): |
| 41 | + for j in range(i): |
| 42 | + self.catalan_numbers[i] += ( |
| 43 | + self.catalan_numbers[j] * self.catalan_numbers[i - j - 1] |
| 44 | + ) |
| 45 | + elif N == 0: |
| 46 | + self.catalan_numbers[0] = 1 |
| 47 | + print(self.catalan_numbers) |
| 48 | + |
| 49 | + def get(self, sequence_no=None): |
| 50 | + """ |
| 51 | + >>> Catalan(5).get(3) |
| 52 | + [1, 1, 2, 5, 14, 42] |
| 53 | + [1, 1, 2, 5] |
| 54 | + >>> Catalan(5).get(6) |
| 55 | + [1, 1, 2, 5, 14, 42] |
| 56 | + Out of bound. |
| 57 | + >>> Catalan(5).get(-1) |
| 58 | + [1, 1, 2, 5, 14, 42] |
| 59 | + [] |
| 60 | + """ |
| 61 | + if sequence_no is not None: |
| 62 | + if sequence_no < len(self.catalan_numbers): |
| 63 | + return print(self.catalan_numbers[: sequence_no + 1]) |
| 64 | + else: |
| 65 | + print("Out of bound.") |
| 66 | + else: |
| 67 | + print("Please specify a value") |
| 68 | + |
| 69 | + |
| 70 | +if __name__ == "__main__": |
| 71 | + print("\n********* Catalan Numbers Using Dynamic Programming ************\n") |
| 72 | + print("\n Enter the upper limit for the Catalan number sequence: ", end="") |
| 73 | + try: |
| 74 | + N = int(input().strip()) |
| 75 | + cat = Catalan(N) |
| 76 | + print( |
| 77 | + "\n********* Enter different values to get the corresponding Catalan " |
| 78 | + "Number sequence, enter any negative number to exit. ************\n" |
| 79 | + ) |
| 80 | + while True: |
| 81 | + try: |
| 82 | + i = int(input("Enter value: ").strip()) |
| 83 | + if i < 0: |
| 84 | + print("\n********* Good Bye!! ************\n") |
| 85 | + break |
| 86 | + cat.get(i) |
| 87 | + except NameError: |
| 88 | + print("\nInvalid input, please try again.") |
| 89 | + except NameError: |
| 90 | + print("\n********* Invalid input, good bye!! ************\n") |
| 91 | + |
| 92 | + import doctest |
| 93 | + |
| 94 | + doctest.testmod() |
0 commit comments