现在的位置: 首页 > 综合 > 正文

卡塔兰数的应用

2013年10月04日 ⁄ 综合 ⁄ 共 2071字 ⁄ 字号 评论关闭

卡塔兰数

卡塔兰数的一般項公式為 C_n = \frac{1}{n+1}{2n \choose n} = \frac{(2n)!}{(n+1)!n!}

前幾項為 (OEIS中的数列A000108):
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, 18367353072152, 69533550916004, 263747951750360,
1002242216651368, 3814986502092304, 14544636039226909, 55534064877048198, 212336130412243110, 812944042149730764, 3116285494907301262, 11959798385860453492, 45950804324621742364, ...

性质

Cn的另一个表达形式为C_n = {2n\choose n} - {2n\choose n+1} \quad\mbox{ for }n\ge 1 所以,Cn是一个自然数;这一点在先前的通项公式中并不显而易见。这个表达形式也是André对前一公式证明的基础。

递推关系

C_0 = 1 \quad \mbox{and} \quad C_{n+1}=\sum_{i=0}^{n}C_i\,C_{n-i}\quad\mbox{for }n\ge 0.

它也满足

C_0 = 1 \quad \mbox{and} \quad C_{n+1}=\frac{2(2n+1)}{n+2}C_n,

这提供了一个更快速的方法来计算卡塔兰数。

卡塔兰数的渐近增长为

C_n \sim \frac{4^n}{n^{3/2}\sqrt{\pi}}

它的含义是左式除以右式的商趋向于1当n → ∞。(这可以用n!的斯特灵公式来证明。)

所有的奇卡塔兰数Cn都满足n=2^k-1。所有其他的卡塔兰数都是偶数。

应用


  • Cn表示长度2n的dyck word的个数。Dyck word是一个有n个X和n个Y组成的字串,且所有的前缀字串皆满足X的个数大于等于Y的个数。以下为长度为6的dyck words:

XXXYYY XYXXYY XYXYXY XXYYXY XXYXYY

  • 将上例的X换成左括号,Y换成右括号,Cn表示所有包含n组括号的合法运算式的个数:

((())) ()(()) ()()() (())() (()())

  • Cn表示有n个节点组成不同构二叉树的方案数。下图中,n等于3,圆形表示节点,月牙形表示什么都没有。
  • Cn表示有2n+1个节点组成不同构满二叉树(full binary tree)的方案数。下图中,n等于3,圆形表示内部节点,月牙形表示外部节点。本质同上。
Catalan number binary tree example.png

证明:

令1表示进栈,0表示出栈,则可转化为求一个2n位、含n个1、n个0的二进制数,满足从左往右扫描到任意一位时,经过的0数不多于1数。显然含n个1、n个0的2n位二进制数共有{2n \choose n}个,下面考虑不满足要求的数目。

考虑一个含n个1、n个0的2n位二进制数,扫描到第2m+1位上时有m+1个0和m个1(容易证明一定存在这样的情况),则后面的0-1排列中必有n-m个1和n-m-1个0。将2m+2及其以后的部分0变成1、1变成0,则对应一个n+1个0和n-1个1的二进制数。反之亦然(相似的思路证明两者一一对应)。

从而C_n = {2n \choose n} - {2n \choose n + 1} = \frac{1}{n+1}{2n \choose n}。证毕。

应用2的C++代码实现如下

#include <iostream>
#include <vector>
using namespace std;

/*
count[0]存着左括号数目,count[1]存着右括号数目。一开始kind中压入左括号,因为第一个肯定是左括号。然后count数组初始化为n-1个左括号,n个右括号。然后我们递归的处理。如果剩余左括号数count[0]大于0,就可以把左括号压栈。而对于右括号,栈中左括号个数必须多于右括号个数,也就是剩余右括号个数大于左括号个数,即count[1]>count[0]时,才能将右括号压栈。如果栈中元素个数达到2n时,就把栈中元素输出。
*/
void func(vector<char>kind,int count[],int n)
{
    static int cnt = 0;
    if(count[0]>=1)
    {
        kind.push_back('(');
        count[0]--;
        func(kind,count,n);
        count[0]++;
        kind.pop_back();
    }
    if((count[1]>=1) && (count[1]>count[0]))
    {
        kind.push_back(')');
        count[1]--;
        func(kind,count,n);
        count[1]++;
        kind.pop_back();
    }
    if(kind.size()==2*n)
    {
	cnt++;
        vector<char>::iterator iter;
        for(iter=kind.begin();iter!=kind.end();iter++)
        {
            cout<<(*iter)<<" ";
        }
		cout << "cnt = " << cnt << endl;
        cout<<endl;
    }
}


int main()
{
    int n;
    cout << "please input the number of ():" << endl;
    cin>>n;
    int count[2]={n-1,n};
    vector<char>kind;
    kind.push_back('(');
    func(kind,count,n);
    return 0;
}

注:上述部分内容摘抄与维基百科

抱歉!评论已关闭.