卡特兰数

一月 1st, 2011 3 Comments »

算法课最后一节讲到了卡特兰数,总结和学到了很多以前不知道的东西。

卡特兰数的递推公式是F(n)=∑(k=1…n){F(k-1)*F(n-k)}=∑(k=0…n-1){F(k)*F(n-k-1)}
一般性公式为F(n)=C(2n,n)/(n+1)

可以描述的问题有
1、n个元素的二叉查找树有多少种。
2、n*n棋盘从左下角走到右上角而不穿过主对角线的走法。
3、2n个人排队买票问题,票价50,n个人拿50元,n个人拿100元,售票处无零钱,能顺利卖票的所有排队方式。
4、n个元素全部入栈并且全部出栈的所有可能顺序。

这些问题的答案都是卡特兰数F(n)。但是很明显可以看出后三个问题是同质的。
都可以抽象成2n个操作组成的操作链,其中A操作和B操作各n个,且要求截断到操作链的任何位置都有:A操作(向右走一步、收到50元、元素入栈)的个数不少于B操作(向上走一步、收到100元找出50元、元素出栈)的个数。故问题2、3、4其实是同一个问题。

下面先证明问题2、3、4和问题1同解,再证明问题2、3、4的解是F(n)=C(2n,n)/(n+1),从而证明问题1的解也是F(n)=C(2n,n)/(n+1)。 Continue reading »

组合数学 Pearls

四月 24th, 2010 No Comments »

1.任取黑白混杂的棋子21个,排成3行7列,证明无论怎样排列,都可以找到一个小长方形矩阵,使四个角上的棋子的颜色相同。

三行七列,每一列至少有两个颜色相同,六种情况:12同白,12同黑,13同白,13同黑,23同白,23同黑。六种情况、七列,必然有至少两列情况相同,故得到解,鸽笼原理,记得小时候书上管这叫抽屉原理来着。

2.从2n个连续整数中任取n+1个,证明:这n+1个数中必有两个互质。

按顺序两两分组,分成n组,每组中的k,k+1必互质,再次利用鸽笼原理,必有一组中的两个数同时被选中,两数互质,证毕。

3.8*8的棋盘,可以用32个多米诺骨牌(1*2的矩形)恰好覆盖。若去掉左上角和右下角的格子,证明用31块骨牌不能覆盖余下的棋盘。

将棋盘的行用i=1…8标记,列用j=1…8标记,则棋盘上的格子可按i+j是奇数还是偶数分成两类,各32个,且相间出现,即每个第一类格子的四周都是第二类格子,每个第二类格子的四周都是第一类格子。左上角和右下角的格子是同类格子,因此去掉后两类格子分别剩下30个和32个,而一个骨牌只能覆盖一块1*2区域,且该区域内必有两类格子各一个,故不能用31个骨牌将余下的棋盘恰好覆盖。

4.“门票问题”——2n个人排队购票入场,其中n个人手拿50元钞票,n个人手拿100元钞票,门票价格为50元,售票处没有任何零钱,问有多少种排队方式能顺利让2n个人都购票入场?n个0和n个1共2n个数进行排列,要求从头遍历到任何位置时0的个数都不小于1个个数,问有多少种排列方式?

答案为卡特兰数C(2n,n)-C(2n,n-1)=C(2n,n)/(n+1),证明如下:不考虑是否符合题目要求,n个0和n个1的排列方式为C(2n,n),再考虑不符合条件的排列方式有多少,减去即可。考虑每种不符合条件的排列,必存在一个奇数位置2m+1,在该处之前出现了m+1个1和m个0,该位置之后的2(n-m)-1个数中出现n-m个0和n-m-1个1,将后面这部分的0和1互换(0变成1,1变成0),这样每个不符合条件的序列都对应一个由n+1个1和n-1个0组成的2n长的序列;反过来,任何一个由n+1个1和n-1个0组成的2n长的序列,都能找到一个奇数位置2m+1使得从头至此有m+1个1和m个0,将后面的部分0和1互换,这样每个由n+1个1和n-1个0组成的2n长序列都对应一个不符合条件的序列。因此证明了不符合条件的序列个数为C(2n,n-1),故符合条件的序列个数为C(2n,n)-C(2n,n-1) = C(2n,n)/(n+1)。

5.“方程的解”问题——已知一个方程x1+x2+…+xn=P,其中x1>=a1,x2>=a2,…xn>=an,并且(a1+a2+…+an)<=P。问共有多少组解?

首先很容易分析出,问题等同于找到x1+x2+…+xn=P’,其中P’=P-(a1+a2+…+an),x1>=0,x2>=0,…xn>=0。

这个问题有个异常强大的思路:写一个由P’个1和n-1个0组成的序列,令xi等于第i-1个0和第i个0之间1的个数,且x0等于第一个0之前1的个数,xn等于第n-1个0后面的1个个数,这样每个P’个1和n-1个0组成的序列和每个方程的解就是一一对应的了。所以解的组数是C(P’+n-1,n-1)。强大吧?

重读中国剩余定理

四月 10th, 2010 No Comments »

今天一个高中同学的校内更新了一个状态“屋内有多张桌子,若每3人一桌,多2人;每5人一桌,多4人;每7人一桌,多6人;每9人一桌,多8人;11人一桌刚刚好;Q:共有多少人?”。第一反应自然是“中国剩余定理”,但是细细想来,第一次看到中国剩余定理的时候我并没有仔细看透,而只是以为它是一种预处理手段,今天上网查了一下,原来是一个O(N)的解法(N为条件个数),非常高效,自己也算了算,原理很直观,很漂亮。当初低估你了。

就用上面的例子来说明吧。

我们要求一个数,这个数除3余2,除5余4,除7余6,除9余8,除11余0。首先我们应该保证这些数互素,否则条件是冗余的,容易发现“除9余8”包含“除3余2”,因此去掉第一个条件。下一步求出剩下这些互素的数5,7,9,11的最小公倍数3465,然后分别考察这四个互素的数:

1.用3465除以5,得到693,693是除了5以外三个数的最小公倍数,因此在我们要求的结果里加上任意倍的693不会影响对另外三个数余数的情况,而只会影响5的余数。我们的目标是令对5余4,容易发现693*3是符合该条件的最小数,记下它。接下来考察其它数的时候都按照这个方法,因为都是5的倍数,所以不会影响除5余4这个结果。

2.再举一步,考察7,用3465除以7,得到495,因此我们在所要求的结果上加上任意倍的495,都不会影响除了7以外的三个数的余数的情况,自然也不会影响刚刚考察过的5。而偶们的目标是除7余6,容易得出495*4符合条件。

3.考察9得到385*5。考察11得315*11。

4.将上述结果相加693*3+495*4+385*5+315*11=9449,则9449一定满足题目的条件,但是却不是最小的答案,因为从9449中加上和减去任意倍的3465都不影响对各个数的余数,因此对3465取模得到2519为最终答案。

威佐夫博弈、黄金分割、POJ 1067

四月 3rd, 2010 2 Comments »
取石子游戏
Time Limit: 1000MS Memory Limit: 10000K
Total Submissions: 17630 Accepted: 5338

Description

有两堆石子,数量任意,可以不同。游戏开始由两个人轮流取石子。游戏规定,每次有两种不同的取法,一是可以在任意的一堆中取走任意多的石子;二是可以在两堆中同时取走相同数量的石子。最后把石子全部取完者为胜者。现在给出初始的两堆石子的数目,如果轮到你先取,假设双方都采取最好的策略,问最后你是胜者还是败者。

Input

输入包含若干行,表示若干种石子的初始情况,其中每一行包含两个非负整数a和b,表示两堆石子的数目,a和b都不大于1,000,000,000。

Output

输出对应也有若干行,每行包含一个数字1或0,如果最后你是胜者,则为1,反之,则为0。

Sample Input

2 1
8 4
4 7

Sample Output

0
1
0

Source

这个游戏就是所谓的威佐夫博弈(Wythoff Game),很有意思。

昨天就在做这道题目,思考成果是:用一个二维数组来表示玩家将面临的局面,即a[i][j]表示两堆石头分别有i个和j个,如果a[i][j]是必败局面的话,那么——这个点的右侧所有点a[i][k](k>j)都是必胜点,因为可以通过拿走第二堆的k-j个石子令对方面临必败局面a[i][j];这个点的下侧所有点a[k][j](k>i)都是必胜点,因为可以通过拿走第二堆的k-i个石子令对方面临必败局面a[i][j];这个点的右下方45度所有点a[i+k][j+k](k>0)都是必胜点,因为可以通过同时拿走两堆的k个石子令对方面临必败局面a[i][j]。这样如果在二维数组a[i][j]处标记”X”表示必败的话,在上面提到的三类点的位置都可以标记”O”表示必胜了,做完这项工作后,再挑选如今距离原点最近的未被标记的点,它一定是下一个必败点——因为它无法通过游戏规则移动到一个必败点,并且规则规定的动作都是朝向原点移动的,而它是距离原点最近的未被标记的点,因此它只能移动到一个必胜点从而让对方获胜,所以该点一定是下一个必败点。有了新必败点后就可以重复上述工作,直到找出问题范围内的所有必败点。

然而这样的模拟工作是不被允许的,因为数据量太大了,因此这道题要求的是给定两个数i,j,有办法立刻判断a[i][j]是不是必败点,在O(1)时间给出解答。

我试图找到必败点的规律,但是没成功,以上就是昨天的思考结果。因为这个问题够简洁,所以我坚信一定有简单规律的,因此在考察了很大数据量仍然没有发现规律后,我无比好奇这道题的解法。今天上网一搜才知道规律果然够简单够经典,竟然是“黄金分割”,以下内容来自对网上内容的总结:

前几个必败点如下:(0,0),(1,2),(3,5),(4,7),(6,10),(8,13)……可以发现,对于第k个必败点(m(k),n(k))来说,m(k)是前面没有出现过的最小自然数,n(k)=m(k)+k。一个必败点有如下性质:

1.所有自然数都会且仅会出现在一个必败点中;

证明:m(k)是前面没有出现过的最小自然数,自然与前k-1个必败点中的数字都不同;m(k)>m(k-1),否则违背m(k-1)的选择原则;n(k)=m(k)+k>m(k-1)+(k-1)=n(k-1)>m(k-1),因此n(k)比以往出现的任何数都大,即也没有出现过。又由于m(k)的选择原则,所有自然数都会出现在某个必败点中。性质1证毕。

2.规则允许的任意操作可将必败点移动到必胜点;

证明:以必败点(m(k),n(k))为例。若只改变两个数中的一个,由于性质1,则得到的点一定是必胜点;若同时增加两个数,由于不能改变两数之差,又有n(k)-m(k)=k,故得到的点也一定是必胜点。性质2证毕。

3.一定存在规则允许的某种操作可将必胜点移动到必败点;

证明:以某个必胜点(i,j)为例。因为所有自然数都会出现在某个必败点中,故要么i等于m(k),要么j等于n(k)。若i=m(k),j>n(k),可从j中取走j-n(k)个石子到达必败点;若i=m(k),j<n(k),可从两堆同时拿走m(k)-m(j-m(k)),从而到达必败点(m(j-m(k)),m(j-m(k))+j-m(k));若i>m(k),j=n(k),可从i中取走i-m(k)个石子到达必败点;若i<m(k),j=n(k),需要再分两种情况,因为i一定也出现在某个必败点中,若i=m(l),则从j中拿走j-n(l),若i=n(l),则从j中拿走j-m(l),从而到达必败点(m(l),n(l))。性质3证毕。

判断一个点是不是必败点的公式与黄金分割有关,为:

m(k) = k * (1 + sqrt(5))/2

n(k) = m(k) + k

至于为什么如此,我就不知道了,也没有查到,很好奇。

POJ1067就是这道题,代码如下:

#include 
#include 
 
using namespace std;
 
int main()
{
	int m,n;
	while(cin>>m>>n)
	{
		if (m > n)
		{
			int temp;
			temp = m;
			m = n;
			n =temp;
		}
		int k = n - m;
		int data = floor(k*(1.0+sqrt(5.0))/2.0);
		if (data == m)
			cout<<0<<endl;
        }
}
需要注意的是,由于数据量过大,float的精度不够,而只能用double,因此代码中的小数没有加“f”。

Mathematics Pearls

三月 24th, 2010 No Comments »

本篇不定时更新与数学有关的小point。

#1
(1+2+2^2+2^3+…+2^19)mod5=?

一共有20个数(2^0…2^19),每4个数分为一组:2^4n(1+2+4+8),0<=n<=4。1+2+4+8=15,15mod5=0,所以每一组模5都是0,故整体也是5的倍数,模5为0。

#2
哥德巴赫猜想及陈景润的工作

哥德巴赫猜想:任何一个偶数都能表示成两个素数的和。叙述简单,几百年来一直没有被成功证明过。因为要将一个偶数表示成一个素数加一个素数,因此称为“1+1”。

陈景润做的工作是证明了任何一个大偶数可以表示成两部分的和,一部分是一个素数,另一部分是两个素数之积,即所谓的“1+2”,距离“1+1”仅一步之遥。记得小学还是中学的某个老师讲过,陈景润最伟大的成就就是证明了1+1=2,当时我们都很纳闷,简单的1+1=2有什么可证明的,然后老师就故弄玄虚(应该是误弄玄虚)地说这里面其实学问可大了。所以这么多年来我一直认为1+1=2这个等式不是自然成立的,而它之所以成立的原因甚至可以称为数学的根基。今天想来,不免觉得好笑,呵呵。

#3
求最大公因子的欧几里德辗转相除法

记a,b(a>b)的最大公因子为gcd(a,b)——gcd for greatest common divisor。有a=bq+r,r为a除以b得到的余数,我们有gcd(a,b)=gcd(b,r)。因为任意a,b的公因子s,有a=ms,b=ns,r=a-bq=ms-qns=(m-qn)s,因此s也是b和r的公因子;反过来,对于任意b,r的公因子s’,有b=m’s',r=n’s',a=bq+r=qm’s'+n’s'=(qm’+n’)s’,因此s’也是a和b的公因子,综上,a,b公因子集合与b,r公因子集合相等,故gcd(a,b)=gcd(b,r)。这样一直进行求余数r的工作,就能一直将问题的规模缩小,直到r=0——gcd(b,0)=b。

#4
“2^n-1当且仅当n为偶数时是3的倍数”的证明

这个证明利用的是同余关系mod,首先同余有如下性质:a≡b,c≡d =>ac≡bd(证明:a≡b=>(a-b)=km,c≡d=>(c-d)=lm,ac-bd=(a-b)c+b(c-d)=(kc+bl)m,因此ac≡bd),由此性质得出a≡b=>a^n≡b^n,因为2≡-1(mod 3),令a=2,b=-1,则2^n≡(-1)^n,当n为偶数时,等式为2^n≡1(mod 3),则2^n-1=3k,证毕。

“等差数列(4n+3)中有无限多个素数”的证明

三月 24th, 2010 No Comments »

证明等差数列3,7,11,15,…中有无限多个素数

因为所有大于2的素数都是奇数,否则会被2整除,所以素数只有两种形式:4n+1、4n+3。
反证法。假设形为4n+3的素数是有限多的,不妨设它们为p1,p2,…,pn。考虑数A=4p1p2…pn-1=4(p1p2…pn-1)+3,要么它本身是素数,要么它有素数因子,而p1,p2,…,pn都不是它的素数因子,因为相除余-1,而p1,p2,…,pn是全部的4n+3形的素数,因此它的所有素数因子都只能是4n+1形的,但是我们又知道两个4n+1形的数相乘仍然是4n+1形——(4a+1)(4b+1)=4(4ab+a+b)+1,而A不是4n+1形的,因此产生矛盾,假设不成立,4n+3形的素数是无限多的。证毕。

“自然数中有无限多个素数”的证明,另几个相关定理的证明

三月 24th, 2010 1 Comment »

“自然数中有无限多个素数”的证明

欧几里德给出的反证法:

首先,任何一个正整数都能表示成素数的乘积。

假设自然数中只有有限多个素数,不妨设为n个,它们是q1,q2,q3,…,qn。现构造一个数A=(q1*q2*q3*…*qn)+1,A比q1,…qn中的任何一个都大,因此与它们都不同,按照假设A应该是合数。A不能被q1,…qn中的任何一个整除,因为会余1,所以q1,…qn都不是A的因子,即A不能表示成q1,…qn的乘积,这与我们假设的只有q1,…qn是素数是矛盾的,因此自然数中有无限多个素数,证毕。

由此得出的一个构造无穷多个素数的方法:

设已经构造出了n个素数,那么A=(q1*q2*…*qn)+1要么是新的素数,要么包含新的素数因子,如果是后者,通过不断试验也可以找到这个新素数因子,因此可以构造出q(n+1),如此反复,可构造出任意多个素数。

“任何比1大的整数N,在不考虑顺序的情况下,只能按唯一一种方式分解成素数的乘积”的证明

假设存在这样的大于1的、可以按照一种以上方式分解成素数乘积的整数,设满足此条件的最小整数为m,则两种分解方式可写作:

m=p1*p2*…*pr

m=q1*q2*…*qs

我们要求写成这样的形式,这是完全可以办到的:p1<=p2<=…<=pr;q1<=q2<=…<=qs。且所有因子都大于1,因为乘法里乘以1是可有可无的。肯定有p1!=q1,否则可以让m除以p1和q1,得到一个有两种不同素数分解形式的比m小的整数,这与对m的选择相矛盾。不妨设p1<q1,令m’=m-p1*q2*q3*…*qs,将m的两种表达式分别代入得到:

m’=p1*p2*…*pr-p1*q2*…*qs

     =p1*(p2*…*pr-q2*…*qs)                                 (1)

m’= q1*q2*…*qs-p1*q2*…*qs

     =(q1-p1)*q2*…*qs                                            (2)

因为q1>p1,所以由(2)知,m’是正整数,且m’<m,所以m’只能按一种方式写成素数的乘积,因此(1)中的第一项p1要么是(2)中(q1-p1)部分的素因子,要么是q2*…*qs部分的素因子,而后一部分中所有q都是素数、且都比p1大,因此不可能,故p1是(q1-p1)的素因子,即

(q1-p1)=h*p1

则q1=(h+1)p1,这与q1是素数是矛盾的,因此我们的假设不成立,不存在可以写成一种以上素数乘积形式的大于1的整数,证毕。

推论“如果一个素数p是a*b的因子,则要么它是a的因子,要么是b的因子”的证明:

反证法,如果p既不是a的因子也不是b的因子,将a和b的素数乘积形式分别写出,应该都找不到p,将两个乘积相乘得到a*b的乘积形式也找不到p,这样我们得到了将a*b进行素数分解的不包含p的形式。又因为p是a*b的因子,因此有a*b=p*t,将t写成素数分解的形式再乘以p,我们又得到了一个将a*b进行素数分解的包含p的形式,这与“ a*b的素数分解形式是唯一的”这一事实相矛盾,因此假设不成立,p要么是a的因子,要么是b的因子,证毕。

一个整数的所有因子(素因子与合因子)的个数

若a=p1^m1*p2^m2*…*pn^mn,p1,…,pn是a的所有素因子,每一个都有某次幂。则a的因子b应该有如下形式

b=p1^k1*p2*k2*…*pn^kn

其中,0<=k1<=m1,0<=k2<=m2,…0<=kn<=mn。不同的b就是从k1到kn的各种不同选择,因此a的因子b的个数为(m1+1)(m2+1)…(mn+1)。例如144=2^4*3^2,故它的因子个数为(4+1)(2+1)=15,分别是1,2,3,4,6,8,9,12,16,18,24,36,48,72,144。

Josephus问题的一个递推思路,复杂度O(n)

三月 23rd, 2010 1 Comment »

本思路可用O(n)的复杂度解决最原始的Josephus问题,即只找出最后一个幸免者。关于约瑟夫(Josephus)问题:

N个人围成一圈,从第一个开始报数,第M个将被杀掉,最后剩下一个,其余人都将被杀掉。例如N=6,M=5,被杀掉的人的序号为5,4,6,2,3。最后剩下1号。

为了方便使用mod处理数据,将N个人编号0-N-1,每次报数从0开始,报到M-1的被杀掉。

第一个被杀掉的一定是编号(M-1)mod(N)的人,他被杀掉后,令k=(M)mod(N),则剩下的问题为:编号为:k,k+1,k+2,…,n-1,0,1,…,k-2的N-1个人,从k开始从0进行报数,报到M-1的人被杀掉。所以如果做一个i’ = (i-k)mod(N)的映射,就可以真正将问题转换成J(N-1,M),解出答案后再做逆映射i = (i’+k)mod(N)即可得到J(N,M)的解,即J(N,M) = (J(N-1,M)+k)mod(N)。又知道k = (M)mod(N),所以递推公式的形式是J(N,M) = (J(N-1,M)+M)mod(N)。递推起点是J(1,M)=0。(注意k,k+1,k+2,…n-1,0,1,…k-2的序列在考虑mod操作后并没有数据上的跳跃,因此做了映射后才仍然是问题的原型,抹去了杀掉一个人的影响。)

J(1,M) = 0

J(N,M) = (J(N-1,M)+M)mod(N)

这样,只要从1推到N就可以得出问题的答案,复杂度O(N)。