该问题出自《C语言名题精选百则技巧篇》
题目:编写一个程序,只用加法,求出n中取r个的组合系数C(n,r),并且尽可能的使加法数目降低。
我们先来了解一下二项式系数的性质:
(1) C(m,n) = C(n,n-m)
(2) C(n,r) = C(n-1,r)+C(n-1,r-1)
(3) C(n,0) = 1,C(n,1) = n, C(n,n) = 1
第一种方法:递归函数
int cnr(int n,int r)
{
if(n==r || r ==0)
return 1;
else
return cnr(n-1,r)+cnr(n-1,r-1);
}
我们来分析一下,上面这种方法用到的加法的个数。
用A(n,r)表示在计算C(n,r)时所用到的加法的个数,从if部分看,当n=r以及r=0时没有用到加法,因此A(n,n) = A(n,0) = 0;从else的部分看,要先计算C(n-1,r)和C(n-1,r-1), 一共用了A(n-1,r)和A(n-1,r-1)个加法,最后又多了一个加法,因此A(n,r)可以表示为:
A(n,r) = 0 n==r或者r==0
A(n,r) = A(n-1,r)+A(n-1,r-1)+1.
由二项式性质的(2)和(3)很容易想到A(n,r) = C(n,r) -1 = n(n-1)...(n-r+1)/[r(r-1)...*3*2*1]-1.与n^r成正比。
第二种方法:杨辉三角(Pascal)
n=0 1
n=1 1 1
n=2 1 2 2
n=3 1 3 3 1
杨辉三角程序
int cnr(int n,int r)
{
int answer[MAXSIZE];
int i,j;
answer[0] =1;
for(i=1;i<=n;i++)
for(answer[i]=1,j=i-1;j>=1;j--)
answer[j]+=answer[j-1];
return answer[r];
}
i = 2时1个加法,i=3时2个加法,i=n时n-1个加法。总共n(n-1)/2个加法。
第三种方法,根据杨辉三角演变出来的
根据杨辉三角演变出来的,但是不按三角形形状顺序进行加法的计算。
C(0,0) C(1,1) C(2,1) C(3,3)
C(1,0) C(2,1) C(3,2) C(4,3)
C(2,0) C(3,1) C(4,2) C(5,3)
C(3,0) C(4,1) C(5,2) C(6,3)
C(4,0) C(5,1) C(6,2) C(7,3)
C(5,0) C(6,1) C(7,2) C(8,3)
先把所有元素都初始化为1,从C(2,1)开始,每一个元素等于它的左边的元素和上边的元素的值相加。所以一共只需要加15次就可以得到结果C(8,3). 可以先计算行也可以先计算列,我们下边按行的顺序相加。
i | j | c[j] | 对应 |
1 | 1 | c[1] = c[1] +c[0] =2 | C(2,1) |
1 | 2 | c[2] = c[2] +c[1] =3 | C(3,2) |
1 | 3 | c[3] = c[3] +c[2] = 4 | C(4,3) |
2 | 1 | c[1] = c[1] +c[0] = 3 | C(3,1) |
2 | 2 | c[2] = c[2] +c[1] =6 | C(4,2) |
2 | 3 | c[3] = c[3] +c[2] =10 | C(5,3) |
3 | 1 | c[1] = c[1] +c[0] = 4 | C(4,1) |
3 | 2 | c[2] = c[2] + c[1] = 10 | C(5,2) |
3 | 3 | c[3] = c[3] + c[2] = 20 | C(6,3) |
4 | 1 | c[1] = c[1] +c[0] = 5 | C(5,1) |
4 | 2 | c[2] = c[2] + c[1] = 15 | C(6,2) |
4 | 3 | c[3] = c[3] +c[2] = 35 | C(7,3) |
5 | 1 | c[1] = c[1] + c[0] = 6 | C(6,1) |
5 | 2 | c[2] = c[2] + c[1] =21 | C(7,2) |
代码如下:
#include <stdio.h>
#include <stdlib.h>
#define MAXSIZE 100
unsigned long cnr(int n,int r)
{
unsigned long c[MAXSIZE];
int i,j;
for(i=0;i<=r;i++)
c[i] = 1UL;
for(i=1;i<=n-r;i++)
for(j=1;j<=r;j++)
c[j]+=c[j-1];
return c[r];
}
int main(int argc,char *argv[])
{
int n,r;
unsigned long answer;
printf("\nCnr Program:m^n");
printf("\nInput n -->");
scanf("%d",&n);
printf("\nInput r -->");
scanf("%d",&r);
answer = cnr(n,r);
printf("The answer of Cnr is:Cnr = %ld",answer);
while(1)
getchar();
return 0;
}