卡特蘭數
關於擴展的卡特蘭數:
1.(n-m+1)/(n+1)*c(n+m,n)
2.c[n+m][n]-c[n+m][m-1]
Catalan,Eugene,Charles,卡特蘭(1814~1894)比利時數學家,生於布魯日(Brugge),早年在巴黎綜合工科學校就讀。1856年任列日(Liege)大學數學教授,並被選為比利時布魯塞爾科學院院士。
卡特蘭一生共發表200多種數學各領域的論著。在微分幾何中,他證明了下述所謂的卡特蘭定理:當一個直紋曲線是平面和一般的螺旋面時,他只能是實的極小曲面。他還和雅可比(Jacobi,C·G·J)同時解決了多重積分的變量替換問題,建立了有關的公式。
1842年,他提出了一種猜想:方程xz-yt=1沒有大於1的正整數解,除非平凡情形32-23=1。這一問題至今尚未解決。
(mathoe注:即除了8、9這兩個連續正整數都是正整數的方冪外,沒有其他。1962年我國數學家柯召以極其精湛的方法證明了不存在三個連續正整數,它們都是正整數的方冪,以及方程x2-yn=1,n>1,xy≠0無正整數解。並且還證明了如果卡特蘭猜想不成立,其最小的反例也得大於1016。)
此外,卡特蘭還在函數論、伯努利數和其他領域也做出了一定的貢獻。
卡特蘭通過解決凸n邊形的剖分得到了數列Cn。
凸n+2邊形用其n-1條對角線把此凸n+2邊形分割為互不重疊的三角形,這種分法的總數為Cn。
為紀念卡特蘭,人們使用“卡特蘭數”來命名這一數列。
據說有幾十種看上去毫不相干的組合計數問題的最終表達式都是卡特蘭數的形式。
卡特蘭數在數學競賽、信息學競賽、組合數學、計算機編程等都會有其不同側面的介紹。
前幾個卡特蘭數:規定C0=1,而
C1=1,C2=2,C3=5,C4=14,C5=42,
C6=132,C7=429,C8=1430,C9=4862,C10=16796,
C11=58786,C12=208012,C13=742900,C14=2674440,C15=9694845。
遞推公式
圓周上有標號為1,2,3,4,……,2n的共計2n個點,這2n個點配對可連成n條弦,且這些弦兩兩不相交的方式數為卡特蘭數Cn。
2003年浙江省小學數學夏令營競賽考了這個題:圓周上10個點可以連成既不相交,也沒有公共端點的5條線段,不同的連法共有_____種。
答:方法的種數是卡特蘭數C5=42,此題被收錄進單墫主編的知識出版社出版的《華數奧賽強化訓練》小學六年級冊的“計數問題”專題。
共六種類型,第1類有5種連法,第2類有2種連法,第3類有10種連法,第4類有10種連法,第5類有10種連法,第6類有5種連法。共有42種連法。
1994年《小學數學》有獎征答競賽:游樂園門票1元一張,每人限購一張。現在有10個小朋友排隊購票,其中5個小朋友每人只有1元的鈔票一張,另5個小朋友每人只有2元的鈔票一張,售票員沒有准備零錢。問:有多少種排隊方法,使售票員總能找的開零錢?
(此題也被許多奧數資料收錄為例題或習題,《華羅庚學校數學課本》小學六年級冊的思維訓練也收有此題)
答:現把拿1元的5個小朋友看成是相同的,把拿2元的5個小朋友也看成是相同的,使用我們常用的“逐點累加法”:
圖中每條小橫段表示拿1元的小朋友,每條小豎段表示拿2元的小朋友,要求從A走到B的過程中網格中任何點均有橫段數不小於豎段數:拿1元的要先,且人數不能少於拿2元的,即不能越過對角線AB:每個點所標的數即為從A走到此點的方法數。求從A到B的走法的方法數。逐點累加可求出為42,即卡特蘭數C5=42。
又由於每個小朋友是不相同的,所以共有42×5!×5!=42×120×120=604800種情況。
若把此題的10個人,拿1元的有5人,拿2元的有5人改為共有2n個人,拿1元的n人,拿2元的n人,則符合要求的排隊方法數為:
再一個卡特蘭數的例子:
甲乙兩人比賽乒乓球,最后結果為20∶20,問比賽過程中甲始終領先乙的計分情形的種數。
即甲在得到1分到19分的過程中始終領先乙,其種數是卡特蘭數
再一個卡特蘭數的例子
飯后,姐姐洗碗,妹妹把姐姐洗過的碗一個一個放進碗櫥摞成一摞。一共有n個不同的碗,洗前也是摞成一摞的,也許因為小妹貪玩而使碗拿進碗櫥不及時,姐姐則把洗過的碗摞在旁邊,問:小妹摞起的碗有多少種可能的方式?
答:得數是第n個卡特蘭數Cn。
再一個卡特蘭數的例子
一個汽車隊在狹窄的路面上行駛,不得超車,但可以進入一個死胡同去加油,然后再插隊行駛,共有n輛汽車,問共有多少種不同的方式使得車隊開出城去?
答:得數是第n個卡特蘭數Cn。
卡特蘭數
求證:卡特蘭數Cn是整數。
證明:
①取整函數不等式:對任意實數x,y有[x+y]≥[x]+[y]。這里[x]表示不大於實數x的最大整數。
解:由定義x≥[x]……(1)
y≥[y]……(2)以上兩式相加,得:x+y≥[x]+[y],
把上式再取整,得:[x+y]≥[[x]+[y]]=[x]+[y],即[x+y]≥[x]+[y]。
②1000!的末尾0的個數249個。(現在有的小學奧數書上出現了100!末尾有幾個零的題目:24個)
解:1000÷5=200,
200÷5=40,
40÷5=8,
8÷5=1……3
以上各商相加,即得1000!末尾0的個數=200+40+8+1=249個。
③n!的質因數分解式中質因子p的冪次數:
…………(1)
k!的質因數分解式中質因子p的冪次數
…………(2)
(n-k)!的質因數分解式中質因子p的冪次數
…………(3)
這里寫成西格馬求和式時使用了無窮的形式,但是從某一確定項之后的每項都是0,為了統一,都寫成了“∞”形式。
④組合數是整數
解:
⑤卡特蘭數是整數
⑥卡特蘭數是整數的另外一個證明
④組合數是整數
⑤卡特蘭數是整數
⑥卡特蘭數是整數的另一個證明
凸六邊形剖分成三角形的14種方法,是卡特蘭數C4
從左下角(0,0)走到右上角(4,4),只允許向上、向右走,但不允許穿過對角線的方法數是14種,是卡特蘭數C4
1936第40屆匈牙利奧林匹克數學競賽 第1題考了Catalan恆等式的證明。
1979第21屆國際數學奧林匹克 第1題考了一個卡特蘭恆等式的應用的題目
此題由1989年第1屆匈牙利-以色列數學競賽題改編。
Catalan數
中文:卡特蘭數
原理:
令h(1)=1,h(0)=1,catalan數滿足遞歸式:
h(n)= h(1)*h(n-1) + h(2)*h(n-2) + ... + h(n-1)h(1) (其中n>=2)
另類遞歸式:
h(n)=((4*n-2)/(n+1))*h(n-1);
該遞推關系的解為:
h(n+1)=C(2n,n)/(n+1) (n=1,2,3,...)
我並不關心其解是怎么求出來的,我只想知道怎么用catalan數分析問題。
我總結了一下,最典型的四類應用:(實質上卻都一樣,無非是遞歸等式的應用,就看你能不能分解問題寫出遞歸式了)
1.括號化問題。
矩陣鏈乘: P=a1×a2×a3×……×an,依據乘法結合律,不改變其順序,只用括號表示成對的乘積,試問有幾種括號化的方案?(h(n)種)
2.出棧次序問題。
一個棧(無窮大)的進棧序列為1,2,3,..n,有多少個不同的出棧序列?
類似:有2n個人排成一行進入劇場。入場費5元。其中只有n個人有一張5元鈔票,另外n人只有10元鈔票,劇院無其它鈔票,問有多少中方法使得只要有10元的人買票,售票處就有5元的鈔票找零?(將持5元者到達視作將5元入棧,持10元者到達視作使棧中某5元出棧)
3.將多邊行划分為三角形問題。
將一個凸多邊形區域分成三角形區域的方法數?
類似:一位大城市的律師在她住所以北n個街區和以東n個街區處工作。每天她走2n個街區去上班。如果她
從不穿越(但可以碰到)從家到辦公室的對角線,那么有多少條可能的道路?
類似:在圓上選擇2n個點,將這些點成對連接起來使得所得到的n條線段不相交的方法數?
4.給頂節點組成二叉樹的問題。
給定N個節點,能構成多少種不同的二叉樹?
(能構成h(N)個)
Catalan數的解法
Catalan數的組合公式為 Cn=C(2n,n) / (n+1);
此數的遞歸公式為 h(n ) = h(n-1)*(4*n-2) / (n+1)
/* 大數解
對於大數來說,就應該使用下面的大數算法。
使用的公式為:h(n) = h(n-1)*(4*n-2)/(n+1);
*/
// 0ms
#include<iostream>
using namespace std;
#define MAX 100
#define BASE 10000
void multiply(int a[],int Max,int b) //大數乘法,注意參數的傳遞
{
int i,array=0;
for (i = Max-1; i >= 0; i--)
{
array += b * a[i];
a[i] = array % BASE; // 數組每一位存放大數的四位數字
array /= BASE;
}
}
void divide(int a[], int Max, int b) //模擬大數除法
{
int i, div = 0;
for (i = 0; i < Max; i++)
{
div = div * BASE + a[i];
a[i] = div / b;
div %= b;
}
}
int main()
{
int a[101][MAX],i, n;
memset(a[1],0,MAX*sizeof(int));
for (i=2, a[1][MAX-1] = 1; i < 101; i++) // 高坐標存放大數低位
{
memcpy(a[i], a[i-1], MAX * sizeof(int)); //h[i] = h[i-1];
multiply(a[i], MAX, 4 * i - 2); //h[i] *= (4*i-2);
divide(a[i], MAX, i + 1); //h[i] /= (i+1);
}
while (cin >> n)
{
for (i = 0; i < MAX && a[n][i] == 0; i++); //去掉數組前為0的數字。
cout << a[n][i++]; //輸出第一個非0數
for (; i < MAX; i++)
{
printf("%04d",a[n][i]); //輸出后面的數,並每位都保持4位長度!(32767)
}
cout << endl;
}
return 0;
}
AC CODE 2:
//C(0) = 1 ; (n+2)*C(n+1) = (4n+2)*C(n); 也即是:h(n) = h(n-1) * (4 * n - 2)/(n+1);
// 0MS
#include<iostream>
using namespace std;
int a[101][101] = {0};
int main()
{
int n,i,j,len,r,temp,t;
int b[101];
a[1][0] = 1; // 低坐標存放大數的低位
len = 1;
b[1] = 1;
for (i = 2; i <= 100; i++)
{
t = i - 1;
for (j=0;j<len;j++) // 模擬乘法,從低位開始
{
a[i][j] = a[i-1][j] * (4 * t + 2);
}
for (r = j = 0; j < len; j++) // 處理相乘結果
{
temp = a[i][j] + r;
a[i][j] = temp % 10;
r = temp / 10;
}
while (r) // 進位處理
{
a[i][len++] = r % 10;
r /= 10;
}
for (j = len-1, r = 0; j >= 0; j--) // 模擬除法,從高位開始
{
temp = r * 10 + a[i][j];
a[i][j] = temp / (t+2);
r = temp % (t+2);
}
while (!a[i][len-1]) // 高位零處理
{
len--;
}
b[i] = len; // 記錄結果的長度
}
while (cin >> n)
{
for(j = b[n] - 1; j >= 0; j--)
{
printf("%d",a[n][j]);
}
printf("\n");
}
return 0;
《編程之美》中提到了“買票找零”問題,查閱了下資料,此問題和卡特蘭數 Cn有關,其定義如下:
卡特蘭數真是一個神奇的數字,很多組合問題的數量都和它有關系,例如:
- Cn= 長度為 2n的 Dyck words的數量。 Dyck words是由 n個 X和 n個 Y組成的字符串,並且從左往右數, Y的數量不超過 X,例如長度為 6的 Dyck words為:
XXXYYY XYXXYY XYXYXY XXYYXY XXYXYY
- Cn= n對括號正確匹配組成的字符串數,例如 3對括號能夠組成:
((())) ()(()) ()()() (())() (()())
- Cn= n+1個數相乘,所有的括號方案數。例如, 4個數相乘的括號方案為:
((ab)c)d (a(bc))d (ab)(cd) a((bc)d) a(b(cd))
- Cn= 擁有 n+1 個葉子節點的二叉樹的數量。例如 4個葉子節點的所有二叉樹形態:
- Cn=n*n的方格地圖中,從一個角到另外一個角,不跨越對角線的路徑數,例如, 4×4方格地圖中的路徑有:
- Cn= n+2條邊的多邊形,能被分割成三角形的方案數,例如 6邊型的分割方案有:
- Cn= 圓桌周圍有 2n個人,他們兩兩握手,但沒有交叉的方案數。
在《Enumerative Combinatorics》一書中,竟然提到了多達 66種組合問題和卡特蘭數有關。
分析
“卡特蘭數”除了可以使用公式計算,也可以采用“分級排列法”來求解。以 n對括弧的合法匹配為例,對於一個序列 (()而言,有兩個左括弧,和一個右括弧,可以看成“抵消了一對括弧,還剩下一個左括弧等待抵消”,那么說明還可以在末尾增加一個右括弧,或者一個左括弧,沒有左括弧剩余的時候,不能添加右括弧。
由此,問題可以理解為,總共 2n個括弧,求 1~2n級的情況,第 i 級保存所有剩余 i 個左括號的排列方案數。 1~8級的計算過程如下表:
計算過程解釋如下: 1級:只能放 1個“(”; 2級:可以在一級末尾增加一個“)”或者一個“ (”
以后每級計算時,如果遇到剩余 n>0個“(”的方案,可以在末尾增加一個“ (”或者“ )”進入下一級;遇到剩余 n=0個“(”的方案,可以在末尾增加一個“ (”進入下一級。
奇數級只能包含剩余奇數個“(”的排列方案
偶數級只能包含剩余偶數個“(”的排列方案
從表中可以看出,灰色部分可以不用計算。
解法
關鍵代碼為:
double Catalan(int n) { if (n == 0) return 1; for (int i = 2; i <= 2 * n; i++) { var m = i <= n ? i : 2 * n + 1 - i; for (int j = (i - 1) & 1; j <= m; j += 2) { if (j > 0) arr[j - 1] += arr[j]; if (j < n) arr[j + 1] += arr[j]; arr[j] = 0; } } return arr[0]; }
其中:
n為 Cn中的 n;
arr = new double[n + 1];//arr[i]代表有 k個括弧的時候,剩余 "("個數為 i的排列方案個數 arr[1] = 1;
討論
算法復雜度為 = O(n^2),空間復雜度為 O(n+1)。相對於利用公式計算而言,此方法的優勢在於——沒有乘除法,只有加法。