Which Way Did the Bicycle Go 趣题选(中)

14. 有意思的是,在数学历史上,一些很简单的结论竟然几百年来都未曾发现。直到 1977 年, Paul Erdős 和 George Szekeres 才发现,除了两头的 1 以外,杨辉三角同一行内的任意两个数都有公因数。证明这个结论。

答案:只需要注意到, a 乘以一个比 b 小的数之后还能成为 b 的倍数,这说明 a 和 b 一定有公因数。不妨设 0 < i < j < n ,则 C(j, i) < C(n, i) 。我们的命题可以由下述关系直接推出。      C(n, j) · C(j, i) = n! / (j! (n - j)!) · j! / (i! (j - i)!) = n! / (i! (n - j)! (j - i)!) = n! / (i! (n - i)!) · (n - i)! / ((j - i)! (n - j)!) = C(n, i) · C(n-i, j-i)

Read more…

神奇的分形艺术(三):Sierpinski三角形

    在所有的分形图形中,Sierpinski三角形可能是大家最熟悉的了,因为它在OI题目中经常出现,OJ上的题目省选题目中都有它的身影。这篇文章将简单介绍Sierpinski三角形的几个惊人性质。如果你以前就对Sierpinski三角形有一些了解,这篇文章带给你的震撼将更大,因为你会发现Sierpinski三角形竟然还有这些用途。

Sierpinski三角形的构造
      
    和之前介绍的两种图形一样,Sierpinski三角形也是一种分形图形,它是递归地构造的。最常见的构造方法如上图所示:把一个三角形分成四等份,挖掉中间那一份,然后继续对另外三个三角形进行这样的操作,并且无限地递归下去。每一次迭代后整个图形的面积都会减小到原来的3/4,因此最终得到的图形面积显然为0。这也就是说,Sierpinski三角形其实是一条曲线,它的Hausdorff维度介于1和2之间。

    Sierpinski三角形的另一种构造方法如下图所示。把正方形分成四等份,去掉右下角的那一份,并且对另外三个正方形递归地操作下去。挖个几次后把脑袋一歪,你就可以看到一个等腰直角的Sierpinski三角形。

      

    Sierpinski三角形有一个神奇的性质:如果某一个位置上有点(没被挖去),那么它与原三角形顶点的连线上的中点处也有点。这给出另一个诡异的Sierpinski三角形构造方法:给出三角形的三个顶点,然后从其中一个顶点出发,每次随机向任意一个顶点移动1/2的距离(走到与那个顶点的连线的中点上),并在该位置作一个标记;无限次操作后所有的标记就组成了Sierpinski三角形。下面的程序演示了这一过程,程序在fpc 2.0下通过编译。对不起用C语言的兄弟了,我不会C语言的图形操作。
{$ASSERTIONS+}

uses graph,crt;

const
   x1=320;  y1=20;
   x2=90;   y2=420;
   x3=550;  y3=420;
   density=2500;
   timestep=10;

var
   gd,gm,i,r:integer;
   x,y:real;

begin
   gd:=D8bit;
   gm:=m640x480;
   InitGraph(gd,gm,'');
   Assert(graphResult=grOk);

   x:=x1;
   y:=y1;
   for i:=1 to density do
   begin
      r:=random(3);
      if r=0 then
      begin
         x:=(x+x1)/2;
         y:=(y+y1)/2;
      end
      else if r=1 then
      begin
         x:=(x+x2)/2;
         y:=(y+y2)/2;
      end
      else begin
         x:=(x+x3)/2;
         y:=(y+y3)/2;
      end;
      PutPixel(round(x),round(y),white);
      Delay(timestep);
   end;
   CloseGraph;
end.

Sierpinski三角形与杨辉三角
    第一次发现Sierpinski三角形与杨辉三角的关系时,你会发现这玩意儿不是一般的牛。写出8行或者16行的杨辉三角,然后把杨辉三角中的奇数和偶数用不同的颜色区别开来,你会发现杨辉三角模2与Sierpinski三角形是等价的。也就是说,二项式系数(组合数)的奇偶性竟然可以表现为一个分形图形!在感到诧异的同时,冷静下来仔细想想,你会发现这并不难理解。
      
    我们下面说明,如何通过杨辉三角奇偶表的前四行推出后四行来。可以看到杨辉三角的前四行是一个二阶的Sierpinski三角形,它的第四行全是奇数。由于奇数加奇数等于偶数,那么第五行中除了首尾两项为1外其余项都是偶数。而偶数加偶数还是偶数,因此中间那一排连续的偶数不断地两两相加必然得到一个全是偶数项的“倒三角”。同时,第五行首尾的两个1将分别产生两个和杨辉三角前四行一样的二阶Sierpinski三角形。这正好组成了一个三阶的Sierpinski三角形。显然它的最末行仍然均为奇数,那么对于更大规模的杨辉三角,结论将继续成立。

Sierpinski三角形与Hanoi塔
    有没有想过,把Hanoi塔的所有状态画出来,可以转移的状态间连一条线,最后得到的是一个什么样的图形?二阶Hanoi塔反正也只有9个节点,你可以自己试着画一下。不断调整节点的位置后,得到的图形大概就像这个样子:
      
    如果把三阶的Hanoi塔表示成无向图的话,得到的结果就是三阶的Sierpinski三角形。下面的这张图说明了这一点。把二阶Hanoi塔对应的无向图复制两份放在下面,然后在不同的柱子上为每个子图的每个状态添加一个更大的盘子。新的图中原来可以互相转移的状态现在仍然可以转移,同时还出现了三个新的转移关系将三个子图连接在了一起。重新调整一下各个节点的位置,我们可以得到一个三阶的Sierpinski三角形。
  
    显然,对于更大规模的Hanoi塔问题,结论仍然成立。

Sierpinski三角形与位运算
    编程画出Sierpinski三角形比想象中的更简单。下面的两个代码(实质相同,仅语言不同)可以打印出一个Sierpinski三角形来。
const
   n=1 shl 5-1;
var
   i,j:integer;
begin
   for i:=0 to n do
   begin
      for j:=0 to n do
         if i and j = j then write('#')
         else write(' ');
      writeln;
   end;
   readln;
end.

#include <stdio.h>
int main()
{
    const int n=(1<<5)-1;
    int i,j;
    for (i=0; i<=n; i++)
    {
        for (j=0; j<=n; j++)
           printf( (i&j)==j ? "#" : " ");
        printf("n");
    }    
    getchar();
 &n
bsp;  return 0;
}

    上面两个程序是一样的。程序将输出:
#                              
##                              
# #                            
####                            
#   #                          
##  ##                          
# # # #                        
########                        
#       #                      
##      ##                      
# #     # #                    
####    ####                    
#   #   #   #                  
##  ##  ##  ##                  
# # # # # # # #                
################                
#               #              
##              ##              
# #             # #            
####            ####            
#   #           #   #          
##  ##          ##  ##          
# # # #         # # # #        
########        ########        
#       #       #       #      
##      ##      ##      ##      
# #     # #     # #     # #    
####    ####    ####    ####    
#   #   #   #   #   #   #   #  
##  ##  ##  ##  ##  ##  ##  ##  
# # # # # # # # # # # # # # # #
################################

    这个程序告诉我们:在第i行第j列上打一个点当且仅当i and j=j,这样最后得到的图形就是一个Sierpinski三角形。这是为什么呢?其实原因很简单。把i和j写成二进制(添加前导0使它们位数相同),由于j不能大于i,因此只有下面三种情况:
    情况一:
    i = 1?????
    j = 1?????
    问号部分i大于等于j
    i的问号部分记作i',j的问号部分记作j'。此时i and j=j当且仅当i' and j'=j'

    情况二:
    i = 1?????
    j = 0?????
    问号部分i大于等于j
    i的问号部分记作i',j的问号部分记作j'。此时i and j=j当且仅当i' and j'=j'

    情况三:
    i = 1?????
    j = 0?????
    问号部分i小于j
    此时i and j永远不可能等于j。i' < j'意味着i'和j'中首次出现数字不同的那一位上前者为0,后者为1,那么i和j做and运算时这一位的结果是0,与j不等。

    注意到,去掉一个二进制数最高位上的“1”,相当于从这个数中减去不超过它的最大的2的幂。观察每一种情况中i,j和i',j'的实际位置,不难发现这三种情况递归地定义出了整个Sierpinski三角形。
    嘿!发现没有,我通过Sierpinski三角形证明了这个结论:组合数C(N,K)为奇数当且仅当N and K=K。这篇文章很早之前就计划在写了,前几天有人问到这个东西,今天顺便也写进来。
    另外,把i and j=j 换成i or j=n也可以打印出Sierpinski三角形来。i and j=j表示j的二进制中有1的位置上i也有个1,那么此时i or (not j)结果一定全为1(相当于程序中的常量n),因此打印出来的结果与原来的输出正好左右镜像。

Matrix67原创
转贴请注明出处

网友Voldemort在12楼和13楼很辛苦地帖了一个杨辉三角模2问题的扩展,大家可以看看

什么是生成函数?

    我们年级有许多漂亮的MM。一班有7个左右吧,二班大概有4个,三班最多,16个,四班最可怜,一个漂亮的MM都没有,五班据说有1个。如果用一个函数“f(班级)=漂亮MM的个数”,那么我们可以把上述信息表示成:f(1)=7,f(2)=4,f(3)=16,f(4)=0,f(5)=1,等等。
    生成函数(也有叫做“母函数”的,但是我觉得母函数不太好听)是说,构造这么一个多项式函数g(x),使得x的n次方系数为f(n)。于是,上面的f函数的生成函数g(x)=7x+4x^2+16x^3+x^5+…。这就是传说中的生成函数了。关键是,这个有什么用呢?一会儿要慢慢说。我敢打赌这绝对会是我写过的最长的一篇文章。

    生成函数最绝妙的是,某些生成函数可以化简为一个很简单的函数。也就是说,不一定每个生成函数都是用一长串多项式来表示的。比如,这个函数f(n)=1 (n当然是属于自然数的),它的生成函数就应该是g(x)=1+x+x^2+x^3+x^4+…(每一项都是一,即使n=0时也有x^0系数为1,所以有常数项)。再仔细一看,这就是一个有无穷多项的等比数列求和嘛。如果-1<x<1,那么g(x)就等于1/(1-x)了。在研究生成函数时,我们都假设级数收敛,因为生成函数的x没有实际意义,我们可以任意取值。于是,我们就说,f(n)=1的生成函数是g(x)=1/(1-x)。

    我们举一个例子说明,一些具有实际意义的组合问题也可以用像这样简单的一个函数全部表示出来。
    考虑这个问题:从二班选n个MM出来有多少种选法。学过简单的排列与组合的同学都知道,答案就是C(4,n)。也就是说。从n=0开始,问题的答案分别是1,4,6,4,1,0,0,0,…(从4个MM中选出4个以上的人来方案数当然为0喽)。那么它的生成函数g(x)就应该是g(x)=1+4x+6x^2+4x^3+x^4。这不就是……二项式展开吗?于是,g(x)=(1+x)^4。
    你或许应该知道,(1+x)^k=C(k,0)x^0+C(k,1)x^1+…+C(k,k)x^k;但你或许不知道,即使k为负数和小数的时候,也有类似的结论:(1+x)^k=C(k,0)x^0+C(k,1)x^1+…+C(k,k)x^k+C(k,k+1)x^(k+1)+C(k,k+2)x^(k+2)+…(一直加到无穷;式子看着很别扭,自己写到草稿纸上吧,毕竟这里输入数学式子很麻烦)。其中,广义的组合数C(k,i)就等于k(k-1)(k-2)…(k-i+1)/i!,比如C(4,6)=4*3*2*1*0*(-1)/6!=0,再比如C(-1.4,2)=(-1.4)*(-2.4)/2!=1.68。后面这个就叫做牛顿二项式定理。当k为整数时,所有i>k时的C(k,i)中分子都要“越过”0这一项,因此后面C(k,k+1),C(k,k+2)之类的都为0了,与我们的经典二项式定理结论相同;不同的是,牛顿二项式定理中的指数k可以是任意实数。

    我们再举一个例子说明一些更复杂的生成函数。n=x1+x2+x3+…+xk有多少个非负整数解?这道题是学排列与组合的经典例题了。把每组解的每个数都加1,就变成n+k=x1+x2+x3+…+xk的正整数解的个数了。教材上或许会出现这么一个难听的名字叫“隔板法”:把n+k个东西排成一排,在n+k-1个空格中插入k-1个“隔板”。答案我们总是知道的,就是C(n+k-1,k-1)。它就等于C(n+k-1,n)。它关于n的生成函数是g(x)=1/(1-x)^k。这个生成函数是怎么来的呢?其实,它就是(1-x)的-k次方。把(1-x)^(-k)按照刚才的牛顿二项式展开,我们就得到了x^n的系数恰好是C(n+k-1,n),因为C(-k,n)*(-x)^n=[(-1)^n*C(n+k-1,n)]*[(-1)^n*x^n]=C(n+k-1,n)x^n。这里看晕了不要紧,后文有另一种方法可以推导出一模一样的公式。事实上,我们有一个纯组合数学的更简单的解释方法。因为我们刚才的几何级数1+x+x^2+x^3+x^4+…=1/(1-x),那么(1+x+x^2+x^3+x^4+…)^k就等于1/(1-x)^k。仔细想想k个(1+x+x^2+x^3+x^4+…)相乘是什么意思。(1+x+x^2+x^3+x^4+…)^k的展开式中,n次项的系数就是我们的答案,因为它的这个系数是由原式完全展开后k个指数加起来恰好等于n的项合并起来得到的。

    现在我们引用《组合数学》上暴经典的一个例题。很多书上都会有这类题。
    我们要从苹果、香蕉、橘子和梨中拿一些水果出来,要求苹果只能拿偶数个,香蕉的个数要是5的倍数,橘子最多拿4个,梨要么不拿,要么只能拿一个。问按这样的要求拿n个水果的方案数。
    结合刚才的k个(1+x+x^2+x^3+x^4+…)相乘,我们也可以算出这个问题的生成函数。

g(x)=(1+x^2+x^4+…)(1+x^5+x^10+..)(1+x+x^2+x^3+x^4)(1+x)
    =[1/(1-x^2)]*[1/(1-x^5)]*[(1-x^5)/(1-x)]*(1+x) (前两个分别是公比为2和5的几何级数,
                                                     第三个嘛,(1+x+x^2+x^3+x^4)*(1-x)不就是1-x^5了吗)
    =1/(1-x)^2   (约分,把一大半都约掉了)
    =(1-x)^(-2)=C(1,0)+C(2,1)x+C(3,2)x^2+C(4,3)x^3…   (参见刚才对1/(1-x)^k的展开)
    =1+2x+3x^2+4x^3+5x^4+….

    于是,拿n个水果有n+1种方法。我们利用生成函数,完全使用代数手段得到了答案!
    如果你对1/(1-x)^k的展开还不熟悉,我们这里再介绍一个更加简单和精妙的手段来解释1/(1-x)^2=1+2x+3x^2+4x^3+5x^4+….。
    1/(1-x)=1+x+x^2+x^3+x^4+…是前面说过的。我们对这个式子等号两边同时求导数。于是,1/(1-x)^2=1+2x+3x^2+4x^3+5x^4+….。一步就得到了我们所需要的东西!不断地再求导数,我们同样可以得到刚才用复杂的牛顿二项式定理得到的那个结论(自己试试吧)。生成函数还有很多其它的处理手段,比如等式两边同时乘以、除以常数(相当于等式右边每一项乘以、除以常数),等式两边同时乘以、除以一个x(相当于等式右边的系数“移一位”),以及求微分积分等。神奇的生成函数啊。
    我们用两种方法得到了这样一个公式:1/(1-x)^n=1+C(n,1)x^1+C(n+1,2)x^2+C(n+2,3)x^3+…+C(n+k-1,k)x^k+…。这个公式非常有用,是把一个生成函数还原为数列的武器。而且还是核武器。

    接下来我们要演示如何使用生成函数求出Fibonacci数列的通项公式。
    Fibonacci数列是这样一个递推数列:f(n)=f(n-1)+f(n-2)。现在我们需要求出它的生成函数g(x)。g(x)应该是一个这样的函数:
    g(x)=x+x^2+2x^3+3x^4+5x^5+8x^6+13x^7+…
    等式两边同时乘以x,我们得到:
    x*g(x)=x^2+x^3+2x^4+3x^5+5x^6+8x^7+…
    就像我们前面说过的一样,这相当于等式右边的所有系数向右移动了一位。
    现在我们把前面的式子和后面的式子相加,我们得到:
    g(x)+x*g(x)=x+2x^2+3x^3+5x^4+8x^5+…
    把这最后一个式子和第一个式子好好对比一下。如果第一个式子的系数往左边移动一位,然后把多余的“1”去掉,就变成了最后一个式子了。由于递推函数的性质,我们神奇地得到了:g(x)+x*g(x)=g(x)/x-1。也就是说,g(x)*x^2+g(x)*x-g(x)=-x。把左边的g(x)提出来,我们有:g(x)(x^2+x-1)=-x。于是,我们得