Stirling公式的应用

Stirling公式也叫做斯特林公式,是用来取N! 的近似值。
在编程中,也用到了Stirling数的思想来解决以下问题。

第一类Stirling公式

题目

把n个物体排成k个非空循环的方法数目。
S(n,0)=0(n>=1)

n个物体不可能不形成环,因为一个物体就能形成环

S(1,1)=1(n>=1)

一个物体能形成一个环

S[i][j]=S(i-1,j-1)+(i-1)S(i-1,j) (1<=j<=i<=n)

具体看例子

对于第一类stirling公式递推关系的理解
第n个物体单独占一个环时其他元素的情况:S(n-1,k-1);第n个物体加入其他环时的情况,(n-1)S(n-1,k),这个物体可以加到第i个物体的左边,所以有n-1种可能。

代码(递归算法)

#include
long long arr[25][25];
long long Stirling1(int n,int k)
{
	if(k==0 || k>n)
		return 0;
	if(n==1 && k==1)
		return 1;
	return Stirling1(n-1,k-1)+(n-1)*Stirling1(n-1,k);
}
int main()
{
	int n,k;
	scanf("%d%d",&n,&k);
	long long ans=Stirling1(n,k);
	printf("%lld",ans);
	return 0;
} 

代码(非递归算法)

#include
long long S[25][25];
int main()
{
	int n,k;
	scanf("%d%d",&n,&k);
	S[1][1]=1;
	for(int i=2;i<=n;i++)
	{
		for(int j=1;j<=i;j++)
			S[i][j]=S[i-1][j-1]+(i-1)*S[i-1][j];
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			if(j<n)	printf("%lld ",S[i][j]);
			else	printf("%lld\n",S[i][j]);
		}
	}
	printf("%lld",S[n][k]);
	return 0;
}

第二类Stirling公式

题目

把n个苹果放入m个相同的盘子中,不允许盘子为空,请问总共有多少种方法?
S(n,k)=0 (k>n or k=0)

可以想到,将10个苹果放入100个盘是不现实的(k>n),没有盘子也是不现实的(k=0)

S(n,1)=S(n,n)=1

将所有苹果放入一个盘子只有一种情况(k=1),将2个苹果放入2个盘子只有一种情况(n=k)

S(n,k)=S(n-1,k-1)+kS(n-1,k)

这个递推方程下面有解释。

对于第二类stirling公式递推关系的理解:
第n个元素单独占一个子集时其他元素的情况:S(n-1,k-1);第n个元素和其他元素呆在同一个子集时有kS(n-1,k)种情况。所以结果就是S(n-1,k-1)+kS(n-1,k)

举个例子来说,当n=4,m=3时,S(4,3)=?。
根据Stirling数公式得S(4,3)=S(3,2)+3*S(3,3)。
(1).分析拿3个苹果放到2个盘子的可能,
{1,{2,3}}————{1,{2,3},4}在将苹果4放入一个盘中
{{1,3},2}————{{1,3},2,4}在将苹果4放入一个盘中
{{1,2},3}————{{1,2},3,4}在将苹果4放入一个盘中

(2).分析拿3个苹果放入3个盘子的可能,
{1,2,3}————{{1,4},2,3}现在是三个盘子都装满,还有一个苹果放入三个盘中的情况
————{1,{2,4},3}
————{1,2,{3,4}}
注意:这里是用Stirling数来模拟N!算法,当然数字不能超过25,如果超过25,题目都会说要对结果进行取模处理

代码(分治递归法)

#include
long long Stirling(int n,int m)
{
	if(m==0 || n<m)//不符合题意 
		return 0;
	if(m==1 || n==m)//只有一种方法.
		return 1;
	return Stirling(n-1,m-1)+m*Stirling(n-1,m); //递归分治 
}
int main()
{
	int n,m;//n表示苹果数,m表示盘子数。 
	scanf("%d%d",&n,&m);
	long long ans=Stirling(n,m);
	printf("%lld",ans);
	return 0;
}
 

时间复杂度为2的n次方

代码(空间换时间的算法)

#include
long long s[50][50];//表示s[n][m] 
int main()
{
	int n,m;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
	{
		s[i][1]=1;
		s[i][i]=1;
		for(int j=1;j<i;j++)
			s[i][j]=s[i-1][j-1]+(long long)j*s[i-1][j];//这里的n不能超过25,由于这里算的是一个组合 
	}
	printf("%lld\n",s[n][m]);
	return 0;
} 

时间复杂度为n²

你可能感兴趣的:(算法与数据结构)