1. Введение: Что такое числа Каталана?
В мире математики и информатики мы часто наблюдаем красивое явление, когда несколько, казалось бы, совершенно разных задач на самом деле имеют абсолютно одинаковую внутреннюю структуру. Одним из ярких примеров являются числа Каталана.
Названная в честь бельгийского математика Эжена Шарля Каталана, эта последовательность начинается так:
$$ C_0 = 1, \quad C_1 = 1, \quad C_2 = 2, \quad C_3 = 5, \quad C_4 = 14, \quad C_5 = 42, \quad C_6 = 132, \quad C_7 = 429, \quad \dots $$Эта последовательность возникает как решение удивительно разнообразных комбинаторных задач. В этой статье мы представим четыре известных примера, связанных с числами Каталана (правильные скобочные последовательности, бинарные деревья, триангуляция многоугольника и пути Дика). Мы разберем их рекурсивную структуру, чтобы понять, почему все они сводятся к одной и той же последовательности. Кроме того, мы рассмотрим алгоритмы их вычисления с использованием динамического программирования (DP) и математический вывод через производящие функции.
2. Четыре конкретных примера чисел Каталана
Пример 1: Правильные скобочные последовательности
В программировании крайне важно убедиться, что скобки расставлены корректно. Количество «правильных скобочных последовательностей», которые можно составить, используя $n$ пар скобок (), в точности равно числу Каталана $C_n$.
Правильная скобочная последовательность — это такая строка, в которой при чтении слева направо количество закрывающих скобок ) ни в какой момент не превышает количество открывающих скобок (.
Давайте посмотрим на случай, когда $n = 3$. Существует 5 правильных способов расположить 3 пары скобок. Это идеально совпадает с $C_3 = 5$.
graph TD
Root["C_3 = 5"] --> P1["((()))"]
Root --> P2["(()())"]
Root --> P3["(())()"]
Root --> P4["()(())"]
Root --> P5["()()()"]
Пример 2: Структуры бинарных деревьев
Теперь рассмотрим бинарные деревья — хорошо известную структуру данных. Количество возможных форм бинарного дерева, имеющего $n$ внутренних узлов, также равно числу Каталана $C_n$.
Для $n = 3$ существует 5 различных форм бинарных деревьев. Они различаются тем, прикреплены ли узлы к левому или правому поддереву.
graph TD
T1["Tree 1"] --> N1_1["Node"]
N1_1 --> N1_2["Left"]
N1_2 --> N1_3["Left"]
T2["Tree 2"] --> N2_1["Node"]
N2_1 --> N2_2["Left"]
N2_2 --> N2_3["Right"]
T3["Tree 3"] --> N3_1["Node"]
N3_1 --> N3_2["Left"]
N3_1 --> N3_3["Right"]
T4["Tree 4"] --> N4_1["Node"]
N4_1 --> N4_2["Right"]
N4_2 --> N4_3["Left"]
T5["Tree 5"] --> N5_1["Node"]
N5_1 --> N5_2["Right"]
N5_2 --> N5_3["Right"]
Пример 3: Триангуляция многоугольника
Числа Каталана также встречаются в геометрии. Количество способов разбить выпуклый $(n+2)$-угольник на $n$ треугольников путем проведения непересекающихся диагоналей между вершинами в точности равно $C_n$.
Например, когда $n = 3$, мы рассматриваем способы триангуляции пятиугольника ($3+2=5$). Существует ровно 5 способов провести диагонали, чтобы образовать 3 треугольника. И снова мы видим число $C_3 = 5$.
Пример 4: Пути Дика
Числа Каталана также появляются в задачах о путях на сетке. На сетке размером $n \times n$ рассмотрим кратчайшие пути из левого нижнего угла $(0, 0)$ в правый верхний угол $(n, n)$, перемещаясь только вправо или вверх на одну единицу за шаг. Количество таких путей, которые никогда не пересекают диагональ $y = x$ (что означает, что они всегда удовлетворяют условию $y \le x$), равно $C_n$. Они называются путями Дика.
Если обозначить движение вправо как R, а вверх как U, то условие требует, чтобы в любом префиксе пути количество U никогда не превышало количество R. Это строго эквивалентно отношению между ( и ) в правильных скобочных последовательностях.
3. Почему они одинаковы? (Скрытая структура)
Почему все эти, казалось бы, не связанные между собой задачи приводят к одной и той же последовательности Каталана? Ответ кроется в том факте, что все они имеют абсолютно одинаковую рекурсивную структуру.
Число Каталана $C_n$ определяется следующим рекуррентным соотношением:
$$ C_0 = 1 $$$$ C_{n} = \sum_{i=0}^{n-1} C_i C_{n-1-i} \quad (n \ge 1) $$Давайте интуитивно поймем, как выводится это рекуррентное соотношение, на примере «правильных скобок».
Рассмотрим произвольную правильную скобочную последовательность $S$ длины $2n$. $S$ должна начинаться с открывающей скобки (. Где-то в строке должна существовать ровно одна парная ей закрывающая скобка ).
Сосредоточив внимание на этой конкретной паре, строку $S$ можно однозначно разбить на следующий вид:
Здесь $A$ и $B$ сами по себе являются правильными скобочными последовательностями (они могут быть и пустыми строками).
Предположим, что подстрока $A$, которая находится между начальной ( и соответствующей ей ), содержит $i$ пар скобок $(0 \le i \le n-1)$.
Поскольку во всей строке $n$ пар, а 1 пара занята внешними ( ), оставшаяся подстрока $B$ должна содержать $(n - 1 - i)$ пар.
- Количество способов сформировать $A$ равно $C_i$
- Количество способов сформировать $B$ равно $C_{n-1-i}$
Следовательно, для фиксированного значения $i$ количество возможных строк равно $C_i \times C_{n-1-i}$. Поскольку $i$ может принимать любое значение от $0$ до $n-1$, суммирование всех этих возможностей дает $C_n$. В этом и заключается смысл рекуррентного соотношения.
graph LR
S["S"] --> Split["( A ) B"]
Split --> A["A : i пар"]
Split --> B["B : n-1-i пар"]
A --> CountA["C_i способов"]
B --> CountB["C_{n-1-i} способов"]
Точно такое же разбиение работает и для «Бинарных деревьев». Если мы назначим один узел корнем и выделим $i$ узлов левому поддереву, то правое поддерево должно забрать оставшиеся $n-1-i$ узлов. Это дает идентичное рекуррентное соотношение.
4. Математический вывод явной формулы
Числа Каталана можно выразить очень простой явной формулой (Closed-form formula), используя комбинаторные обозначения:
$$ C_n = \frac{1}{n+1} \binom{2n}{n} = \frac{(2n)!}{(n+1)!n!} $$Как выводится эта элегантная формула? Давайте рассмотрим два основных подхода.
4.1. Доказательство с помощью принципа отражения
Мы можем доказать эту формулу с помощью путей Дика. Общее количество кратчайших путей от $(0,0)$ до $(n,n)$ равно $\binom{2n}{n}$, потому что из $2n$ шагов всего мы должны выбрать $n$ шагов для движения вправо.
Из этого мы должны вычесть пути, нарушающие условие (то есть те, которые пересекают прямую $y = x$ и касаются прямой $y = x + 1$). Пусть $P$ — первая точка, в которой нарушающий условие путь касается $y = x + 1$. Мы отражаем часть пути от точки $P$ до конечной точки $(n,n)$ относительно прямой $y = x + 1$. Первоначальная конечная точка $(n,n)$ отражается в новую конечную точку $(n-1, n+1)$.
Удивительно, но существует идеальное взаимно-однозначное соответствие (биекция) между «неправильными путями из $(0,0)$ в $(n,n)$» и «ВСЕМИ путями из $(0,0)$ в $(n-1, n+1)$». Общее количество путей из $(0,0)$ в $(n-1, n+1)$ равно $\binom{2n}{n-1}$.
Следовательно, количество правильных путей равно:
$$ C_n = \binom{2n}{n} - \binom{2n}{n-1} $$Мы можем алгебраически упростить это:
$$ C_n = \binom{2n}{n} - \frac{n}{n+1} \binom{2n}{n} = \left( 1 - \frac{n}{n+1} \right) \binom{2n}{n} = \frac{1}{n+1} \binom{2n}{n} $$4.2. Подход через производящие функции
Пусть производящая функция для чисел Каталана $C(x) = \sum_{n=0}^\infty C_n x^n$. Используя рекуррентное соотношение $C_{n} = \sum_{i=0}^{n-1} C_i C_{n-1-i}$, мы находим, что производящая функция удовлетворяет следующему уравнению:
$$ C(x) = 1 + x [C(x)]^2 $$Это можно рассматривать как квадратное уравнение относительно $C(x)$: $x [C(x)]^2 - C(x) + 1 = 0$. Применяя формулу корней квадратного уравнения, получаем:
$$ C(x) = \frac{1 \pm \sqrt{1 - 4x}}{2x} $$Чтобы удовлетворить условию $C(0) = 1$ при $x \to 0$, мы должны выбрать знак минус.
$$ C(x) = \frac{1 - \sqrt{1 - 4x}}{2x} $$Разлагая $\sqrt{1 - 4x} = (1 - 4x)^{1/2}$ с использованием обобщенного бинома Ньютона (ряд Тейлора) и приравнивая коэффициенты, мы приходим к $C_n = \frac{1}{n+1} \binom{2n}{n}$.
5. Алгоритмы вычисления чисел Каталана
При программном вычислении чисел Каталана используются в основном три подхода.
5.1. Простая рекурсия (Naive Recursion)
Это предполагает прямую реализацию рекуррентного соотношения. Однако из-за того, что одни и те же значения вычисляются многократно, временная сложность растет экспоненциально, что делает этот метод непригодным для больших значений $n$.
| |
5.2. Динамическое программирование
Используя мемоизацию (или динамическое программирование “снизу вверх”) для сохранения вычисленных результатов в массив, мы можем уменьшить временную сложность до $O(n^2)$.
| |
5.3. Явная формула
Используя формулу, мы можем вычислить значение с временной сложностью $O(n)$, просто выполняя вычисления факториалов.
| |
6. Заключение
Последовательность чисел Каталана $C_n$ — это захватывающая последовательность, которая единообразно проявляется во множестве, казалось бы, разных задач, таких как правильные скобочные последовательности, формы бинарных деревьев, триангуляция многоугольников и пути Дика. Причина, по которой эти задачи дают одинаковое количество, заключается в том, что все они воплощают общую рекурсивную структуру: «разбиение целого на две подзадачи и их объединение».
При изучении алгоритмов и структур данных понимание математической подоплеки развивает способность видеть суть проблемы. Это также служит отличным упражнением по динамическому программированию, поэтому обязательно попробуйте написать код и поэкспериментировать самостоятельно!
