当组合结果限制在在给定的int类型内是,如果简单地按照阶乘方式来计算组合的话,有可能产生溢出。下面为两种不会溢出的计算组合方法
1. 模拟人工计算,先将分子、分母约分,然后再计算结果,因结果肯定是整数,所以分母一定可以完全被约掉。
//利用辗转相除法求两个自然数的最大公因数, a > b
int gcd(int a, int b)
{
int r;
while(b)
{
r = a%b;
a = b-r;
b = r;
}
return a;
}// 计算组合结果 c(n, r) r <= n
int CombinationCalc(int n, int r)
{
int loop;
int num;
int *buf;
int a, b, factor;
/* c(n, r) = c(n, n-r) */
if(r > n/2)
{
r = n-r;
}
/* 存储r个分子,约分过程中要使用 */
buf = (int *)malloc(r*sizeof(int));
if(NULL == buf)
{
printf("Allocate memory fail!\n");
return -1;
}
for(loop=0; loop<r; loop++)
{
buf[loop] = n-loop;
}
/* 进行约分 */
for(loop=2; loop<=r; loop++)
{
b = loop;
num = 0;
while(b != 1)
{
a = buf[num];
factor = gcd(a, b);
a = a/factor;
b = b/factor;
buf[num] = a;
num++;
}
}
/* 计算组合结果(各分子相乘) */
num = 1;
for(loop=0; loop<r; loop++)
{
num *= buf[loop];
}
free(buf);
return num;
}
2. 利用组合公式c(n+1, r) = c(n, r) + c(n, r-1)进行递归计算
// 计算组合结果 c(n, r) r <= n,利用c(n, r) = c(n-1, r) + c(n-1, r-1)
int CombinationCalc(int n, int r)
{
int ret;if(r > n/2)
{
r = n-r;
}if(0 == r)
{
ret = 1;
}
else if(1 == r)
{
ret = n;
}
else
{
ret = CombinationCalc(n-1, r) + digui(n-1, r-1);
}return ret;
}
小结:第二种方法编写简洁,但测试过程中由于递归不断地压栈出栈,速度要慢得多。第一种方法稍微有些复杂,但速度很快,malloc分配的内存还可以直接改成局部数组,应该更快一些。
********************************************************************************************************************************************************************************************************************************************************************
迭代求排列组合:
1.A(n,m) = n!/m!
2.A(n,m) = m*A(n-1,m-1) + A(n-1,m)
3.C(n,m) = n!/(m!*(n-m)!)
4.C(n,m) = C(n-1,m) + C(n-1,m-1)
以C(n, m)为例,由C(n,m) = C(n-1,m) + C(n-1,m-1)可知C(m)只与上一轮的C(m)和C(m-1)有关,则参考我的这一片博文http://www.cnblogs.com/ldjhust/p/3150604.html就可一很轻松的进行迭代编程。