当前位置:文档之家› C语言程序设计漫谈之从“杨辉三角形”谈起

C语言程序设计漫谈之从“杨辉三角形”谈起

从“杨辉三角形”谈起杨辉三角是二项式系数在三角形中的一种几何排列,中国南宋数学家杨辉1261年所著的《详解九章算法》一书中出现。

在欧洲,帕斯卡(1623~1662)在1654年发现这一规律,所以这个表又叫做帕斯卡三角形。

帕斯卡的发现比杨辉要迟393年。

如果将(a+b)n(n为非负整数)的每一项按字母a的次数由小到大排列,就可以得到下面的等式:(a+b)0=1 ,它只有一项,系数为1;(a+b)1=a+b ,它有两项,系数分别是1,1;(a+b)2=a2+2ab+b2,它有三项,系数分别是1,2,1;(a+b)3=a3+3a2b+3ab2+b3,它有四项,系数分别是1,3,3,1;……由此,可得下面的图表,这个图表就是杨辉三角形。

观察上图表,我们发现每一行的首末都是1,并且下一行的数比上一行多1个,中间各数都写在上一行两数中间,且等于它们的和,可以按照这个规律继续将这个表写下去。

【例1】杨辉三角形。

输入n(1<=n<=30),输出杨辉三角形的前n行。

(1)编程思路1。

用一个二维数组y[31][31] 来保存杨辉三角形每一行的值。

杨辉三角形第row行可以由第row-1行来生成。

例如:由上表知:当row=5时,y[5][1] = 1,y[5][2] = y[4][1] + y[4][2],y[5][3] = y[4][2] + y[4][3],y[5][4] = y[4][3] + y[4][4] ,y[5][5] = y[4][4] + y[4][5]一般的,对于第row(1~30)行,该行有row+1个元素,其中:y[row][1]=1第col(2~row+1)个元素为:y[row][col] = y[row-1][col-1] + y[row-1][col]。

(2)源程序1。

#include <stdio.h>int main(){int n,i,j,y[31][31]={0};for (i=1;i<=30;i++) // 赋行首与行尾元素值为1y[i][1]=y[i][i]=1;for (i=3;i<=30;i++) // 每行中间元素赋值for (j=2;j<i;j++)y[i][j]=y[i-1][j-1]+y[i-1][j];while (scanf("%d",&n)!=EOF){for (i=1;i<=n;i++){for (j=1;j<=i;j++){if (j!=1) printf(" ");printf("%d",y[i][j]);}printf("\n");}printf("\n");}return 0;}(3)编程思路2。

用一个一维数组y[30] 来保存杨辉三角形某一行的值。

杨辉三角形第row行可以由第row-1行来生成。

由上表知:当row=4时,y[4] = y[4]+y[3], y[3] = y[3]+y[2],y[2] = y[2]+y[1] , y[1] = y[1]+y[0],y[0]=1一般的,对于第row(0~9)行,该行有row+1个元素,第col(row~1)个元素为:y[col]=y[col]+y[col-1],y[0]=1(4)源程序2。

#include <stdio.h>#include <string.h>int main(){int y[30],row,col,n;while (scanf("%d",&n)!=EOF){memset(y,0,sizeof(y)); // 数组元素初始化为0y[0]=1;printf("%d\n",y[0]);for (row=1;row<n;row++){for (col=row;col>=1;col--)y[col]=y[col]+y[col -1];for (col=0;col<=row;col++){if (col!=0) printf(" ");printf("%d",y[col]);}printf("\n");}printf("\n");}return 0;}将上面的两个源程序提交给HDU 2032“杨辉三角”,均可以Accepted。

下面我们进一步讨论一下杨辉三角形。

我们根据杨辉三角形前16行中每个数的奇偶性决定是否输出一个特定字符。

比如如果是奇数,输出一个“*”号;是偶数,输出一个空格。

编写如下的程序:#include <stdio.h>int main(){int n,i,j,y[17][17]={0};for (i=1;i<=16;i++) // 赋行首与行尾元素值为1y[i][1]=y[i][i]=1;for (i=3;i<=16;i++) // 每行中间元素赋值for (j=2;j<i;j++)y[i][j]=y[i-1][j-1]+y[i-1][j];for (i=1;i<=16;i++){for (j=1;j<=i;j++)if (y[i][j]%2==1) printf("* ");else printf(" ");printf("\n");}return 0;}运行上面的程序,可以得到如下的运行结果。

运行结果的图形是一个递归深度为4的三角形。

通过这个图形,我们感觉杨辉三角形中每个数字的奇偶应该满足一定的规律。

组合数C(n,m)是指从n个元素中选出m个元素的所有组合个数。

其通用计算公式为:C(n,m)=n!/[m!*(n-m)!] C(0,0)=1 C(1,0)=1 C(1,1)=1从n个元素中取m个元素,考虑第n个元素,有两种情况:(1)不取。

则必须在前n-1个元素中取m个元素,方案数为C(n-1,m);(2)取。

则只需在前n-1个元素中取m-1个元素,方案数为C(n-1,m-1)。

因此, C(n,m)=C(n-1,m)+C(n-1,m-1)这正好符合杨辉三角形的递推公式。

即杨辉三角中第i行第j列的数字正是C(i,j)的结果。

因此,下面对杨辉三角形中各行各列数字的讨论转化为对组合数C(n,m)的讨论。

【例2】组合数的奇偶性。

(POJ 3219)二项式系数C(n, m)因它在组合数学中的重要性而被广泛地研究。

二项式系数可以如下递归的定义:C(1, 0) = C(1, 1) = 1;C(n, 0) = 1 对于所有n > 0;C(n, m) = C(n-1, m-1) + C(n-1, m) 对于所有0 < m ≤ n。

给出n和k,确定C(n, m)的奇偶性。

(1)编程思路1。

对于给定C(n,m),检查n!中2因子的个数与m!和(n-m)!中2因子个数和的关系,假设n!中2因子个数为a,m!中2因子个数为b,(n-m)!中2因子个数为c,则显然有a>=(b+c);并且当a==b+c时,一定为奇,否则为偶。

(2)源程序1。

#include <stdio.h>int getTwo(int x) // x!中2的因子的个数{int cnt=0;while (x/2!=0){cnt += x/2;x=x/2;}return cnt;}int main(){int n,k;while (scanf("%d%d", &n,&k)!=EOF){if (getTwo(n)-getTwo(k)-getTwo(n-k)>0)printf("0\n");elseprintf("1\n");}return 0;}(3)编程思路2。

前面通过杨辉三角形中数字的奇偶性输出“*”图时,我们感觉其数字的奇偶性与数字所在的行号和列号有一定的关系,即组合数C(n,m)的奇偶性与n和m有对应关系。

根据网络上的资料,给出结论如下:组合数的奇偶性判定方法为:对于C(n,m),若n&m == m 则C(n,m)为奇数,否则为偶数。

这个结论可以采用数学归纳法进行证明,在这里省略证明方法。

感兴趣的读者可以查阅相关资料。

在此知道结论好了!(4)源程序2。

#include <stdio.h>int main(){int n,k;while (scanf("%d%d", &n,&k)!=EOF){if ((n&k)==k)printf("1\n");elseprintf("0\n");}return 0;}根据组合数的奇偶性判定方法: 对于C(n,m),若n&m == m 则C(n,m)为奇数,否则为偶数。

可以写出如下一个程序。

#include <stdio.h>int main(){int n,i,j;while (scanf("%d",&n) && n!=0)for (i=0;i<(2<<(n-1));i++){for (j=0;j<=i;j++)if ((i&j)==j) printf("* ");else printf(" ");printf("\n");}}return 0;}运行这个程序,输入4,可以得到前面所示的星号图形。

有一次,我在网上随意浏览时,发现上面这个程序,当时觉得有些奇妙,有些小神奇。

因为,要输出一个递归形式的星号图形,我习惯性地采取递归的方法。

例如,为达到上面程序的功能,根据输入的n,输出相应的递归图形,我会编写如下的程序:#include <stdio.h>#define N 64void draw(char a[][N], int n, int row, int col){if(n==1){a[row][col] = '*';return;}int w = 1;int i;for(i=1; i<=n-2; i++) w *= 2;draw(a, n-1, row, col);draw(a, n-1, row+w, col+w);draw(a, n-1, row+w,col);}{char a[N][N];int n,w,i,j;while (scanf("%d",&n) && n!=0){for(i=0;i<N;i++)for(j=0;j<N;j++)a[i][j] = ' ';w=1;for(i=1; i<=n-1; i++) w *= 2;draw(a,n,0,0);for(i=0; i<w; i++){for(j=0; j<w; j++)printf("%c ",a[i][j]);printf("\n");}}return 0;}一个简单的二重循环即可完成递归图形的描绘,我当时还琢磨半天,怎么会这样?怎么想出来的?怎么会这样,我现在明白了,组合数的奇偶性判断规则。

相关主题