组合数快速计算-尽量小的数组

经常会用到组合数计算,利用数学公式想了一些巧妙的方法,但是如果不加数据结构或者算法的优化,很容易耗时过长或者越界。以下有三种方法。

1.直接利用公式comb(m,n)=comb(m-1,n)+comb(m-1,n-1)进行递归运算。

	int comb(int m,int n)
	{
		if(m==n)return 1;
		if(n==1)return m;
		if(n==0)return 1;
		if(n>m/2)n=m-n;
		return comb(m-1,n)+comb(m-1,n-1);
	}
考虑边界条件,与简单的n>m/2时的优化。这样算有很多冗余,会导致comb(m-1,n)后面重复调用计算。一种简单的方法是用数组A[m][n]存下计算过的comb(m-1,n-1);这个数据其实很不规整。部分用到容易出错,速度当然没问题。

2.利用指数运算

	double lnchoose(int n, int m)
	{
		if (m > n)return 0;
		if (m < n/2.0)m = n-m;
		double s1 = 0;
		for (int i=m+1; i<=n; i++)
			s1 += log((double)i);

		double s2 = 0;
		int ub = n-m;
		for (int i=2; i<=ub; i++)
			s2 += log((double)i);

		return exp(s1-s2);
	}
没有任何递归或者数组缓存,用指数运算,先计算m+1到n的连乘,除以n-m的阶乘。exp转成线性。除了精度损失外还有在较小的数或者有,0,1的边界有bug。

3.利用公式与合适的数据结构进行动态规划

	int fastcombine(int m,int n)
	{
		if(m==n)return 1;
		if(n==0)return 1;
		if(n>m/2)n=m-n;
		m = m - n;//利用公式comb(m,n)=comb(m-1,n)+comb(m-1,n-1); 转变在(m-n)*n的矩阵中计算。两项分别对应左边和下边相邻的位置
		vector<vector<int> > c(m+1,vector<int>(n+1,0));
		c[0][0] = 1;
		for(int i = 0; i <= m; i++)
			c[i][0] =  1;
		for(int j = 0; j <= n; j++)
			c[0][j] =  1;

		for(int i = 1; i <= m; i++)
			for(int j = 1; j <= n; j++)
				c[i][j] = c[i][j-1]+c[i-1][j];
		return c[m][n];	
	}

利用公式comb(m,n)=comb(m-1,n)+comb(m-1,n-1); 转变到(m-n)*n的矩阵中计算。

   
  c[i-1[j]
c[i[j-2]c[i[j-1]c[i][j]
对应组合数:
  
 comb(i+j-2,j-1)comb(i+j-1,j)
comb(i+j-2,j-2)comb(i+j-1,j-1)comb(i+j,j)

两项comb(m-1,n)+comb(m-1,n-1);分别对应左边和下边相邻的位置。这样用最少的规整的空间一次递推计算出组合数。时间O((m-n)*n).

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值