Dynamic Programming 求解第n个Bell数

        Bell数等价于第二类斯特林数,主要用于求集合划分问题:求将1到n个数划分成子集的排列组合数量,且子集之间无交集。

       根据Stirling组合原理可递推出Bell数的递推表达式,即从后往前推:假设已知第n-1个Bell数,那么第n个Bell数就是将第n个数插入到前面排好列的n-1个数的m个子集中;或者第n个数独立成一个堆,与前面将n-1个数划分成m-1堆共同构成n个数划分m个集合。因此:递推式为:B(n,m)=m*B(n-1,m)+B(n-1,m-1)  其中m*B(n-1,m)是插入,B(n-1,m-1)是单独成1堆合并到总堆数m里,且B(0)=1,B(1)=1,B(2)=2;

       特注:第n个Bell数为n个数可划分成1-n个集合。第i个Bell数与B(i,j)相区别,B(i,j)是将i个数划分成j个集合的组合数量,但是,请注意,第i个Bell数的含义是将i个数划分成1到i个集合的所有不同组合数量,所以要求第n个Bell数,就要将n个数从划分到1个集合一直到划分到n个集合的组合数,即用一个for循环来累加m=1,2,3,...n的划分数,第n个Bell数的递推式为:f(n)=∑B(n,i),i=1,2,3,...n。另外,由于B(n,m)表示将n个数划分成m个集合的排列个数,所以可用一个二维向量memorize来存储第i个数划分成第j个集合的组合排列,这样就可以利用DP思想来计算递推式,减少重复计算项由于B(n,m)的递推式中存在重复计算项),提高计算效率。

       主要算法如下:

int DynamicProgramming::bellNumber(int n)
{
	std::vector<std::vector<int>> memorize;                  //f为中间值
	memorize.resize(n + 1);
	for (int i = 0; i < memorize.size(); i++)
	{
		memorize[i].resize(n + 1);
	}

	for (int i = 0; i < memorize.size(); i++)
	{
		for (int j = 0; j < memorize[i].size(); j++)
		{
			memorize[i][j] = -1;
		}
	}



	int fn = 0;
	if (n == 1||n==0)
	{
		fn = 1;
	}

	for (int i = 1; i <= n;i++)
	{
		fn += i*conquerBell(memorize, n - 1, i) + conquerBell(memorize, n - 1, i - 1);
	}


	return fn;
}

int DynamicProgramming::conquerBell(std::vector<std::vector<int>> &memorize, int n, int m)
{
	//1,1,2,5
	int result=0;

	if (memorize[n][m] != -1)
	{
		return memorize[n][m];
	}

	if (n < m)
	{
		return 0;
	}

	if (m==0)
	{
		return 0;
	}
	if (n == 1)
	{
		result = 1;
	}
	else
	{
		result +=   m*conquerBell(memorize, n - 1, m) + conquerBell(memorize, n - 1, m - 1);
	}

	memorize[n][m] = result;
	return result;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值