在组合数学,Stirling数可指两类数,都是由18世纪数学家James Stirling提出的。
第一类Stirling数是有正负的,其绝对值是个元素的项目分作个环排列的方法数目。常用的表示方法有。
换个较生活化的说法,就是有个人分成组,每组内再按特定顺序围圈的分组方法的数目。例如:
这可以用有向图来表示。
递推关系的说明:考虑第n+1个物品,n+1可以单独构成一个非空循环排列,这样前n种物品构成k-1个非空循环排列,方法数为s(n,k-1);也可以前n种物品构成k个非空循环排列,而第n+1个物品插入第i个物品的左边,这有n*s(n,k)种方法。
是调和数的推广。
是递降阶乘多项式的系数:
第二类Stirling数是个元素的集定义k个等价类的方法数目。常用的表示方法有。
换个较生活化的说法,就是有个人分成组的分组方法的数目。例如有甲、乙、丙、丁四人,若所有人分成1组,只有所有人在同一组这个方法,因此;若所有人分成4组,只可以人人独立一组,因此;若分成2组,可以是甲乙一组、丙丁一组,或甲丙一组、乙丁一组,或甲丁一组、乙丙一组,或其中三人同一组另一人独立一组,即是:
因此。
是二项式系数,B_n是贝尔数。
是克罗内克尔δ。
4
Posted by: spoiler in: 斯特林数的各种应用
题目:
Problem Description
In many applications very large integers numbers are required. Some of these applications are using keys for secure transmission of data, encryption, etc. In this problem you are given a number, you have to determine the number of digits in the factorial of the number.
|
Input
Input consists of several lines of integer numbers. The first line contains an integer n, which is the number of cases to be tested, followed by n lines, one integer 1 ≤ n ≤ 10
7 on each line.
|
Output
The output contains the number of digits in the factorial of the integers appearing in the input. |
Sample Input
2 10 20 |
Sample Output
7 19 |
题目分析:
第一种做法:
N!=1*2*3....*n
求位数我们一般用对一个数取对数就可以了 ,
log10(n!)=log10(1)+ log10(2) +log10(3)...+log10(n);
所以循环求和就可以了!
但是这里注意一点 结果要加1!因为这里计算出来的 log10(1)=0 !
所以结果要加上这个误差 ‘1’
第二种做法:
这就是我最近研究的斯特林数,第一类斯特林数就可以做这个!
补充一点,斯特林数能够做一切关于阶乘有关的大数运算 要深入学习!
这里给出递归公式:
log10(n!)=1.0/2*log10(2*pi*n)+n*log10(n/e)
然后我就附上代码了;
两种做法都有!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
#
include
<iostream>
#
include
<cmath>
#
include
<cstdio>
#define e
2.7182818284590452354
#define pi acos(-
1
)
using
namespace
std;
int
main(){
int
cas,ans,n;
cin>>cas;
while
(cas--){
scanf(
"%d"
,&n);
ans=(
int
)(
1.0
/
2.0
*log(
2.0
*pi*n)/log(
10.0
)+
1.0
*n*log(n/e)/log(
10.0
)+
1
);
printf(
"%d\n"
,ans);
}
return
0
;
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
#
include
<iostream>
#
include
<cmath>
#
include
<cstdio>
#define e
2.7182818284590452354
#define pi acos(-
1
)
using
namespace
std;
int
main(){
int
cas,ans,i,n;
double sum;
cin>>cas;
while
(cas--){
scanf(
"%d"
,&n);
sum=
1
;
for
(i=
1
;i<=n;i++)
sum+=log10(i);
printf(
"%d\n"
,((
int
)sum));
}
return
0
;
}
|
204
Posted by: spoiler in: 斯特林数的各种应用
题目:
就是给你N个房间,然后每个房间1把钥匙,你最初手里没有任何钥匙,要靠破门而入!这里只有第一个房间不能破门进去,其他都可以,
给你房间数N,和最多能破门的个数,让你求能全部把房间打开的概率!
题目分析:
又是是我的第一次啊!受教育了?有木有?这种题目是斯特林第一类数的应用,虽然很裸,但是很经典啊 !
首先这题其实让我们求的是给 N个元素,让我们求K个环排列的 方法数。
斯特林第一类数的第推公式:
S(N,0)=0;
S(N,N)=1;
S(0,0)=0;
S(N,K)=S(N-1,K-1)+S(N-1,K)*(N-1);
这个公式的意思是:
当前N-1个数构成K-1 个环的时候,加入第N个 ,N只能构成单环!---S(N-1,K-1)
如果N-1个数构成K个环的时候,加入第N个,N可以任意加入,N-1内的一个环里,所以是--(N-1)*S(N-1,K)
这个题目里,因为不能破坏第1个门:
所以
S(N,K)-S(N-1,K-1)才是能算构成K个环的方法数!就是去掉1自己成环的情况!
代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
#
include
<iostream>
#
include
<cstdio>
#
include
<cstring>
using
namespace
std;
const
int
maxn=
20
;
long long f[
25
],stir[
25
][
25
];
int
solve(){
int
i,j;
f[
0
]=
1
;
for
(i=
1
;i<=maxn;i++)
f[i]=i*f[i-
1
];
//因为N有N!种排列顺序,这作为总数
//计算概率
for
(i=
1
;i<=maxn;i++)
stir[i][
0
]=
0
;
stir[
1
][
1
]=
1
;
for
(i=
1
;i<=maxn;i++)
for
(j=
1
;j<=i;j++){
if
(i==j)
stir[i][j]=
1
;
else
stir[i][j]=stir[i-
1
][j-
1
]+(i-
1
)*stir[i-
1
][j];
}
for
(i=
1
;i<=maxn;i++)
for
(j=
1
;j<=maxn;j++)
if
(stir[i][j]<
0
)
stir[i][j]=-stir[i][j];
return
0
;
}
int
main(){
int
cas,n,i,k;
long long sum;
solve();
scanf(
"%d"
,&cas);
while
(cas--){
scanf(
"%d %d"
,&n,&k);
sum=
0
;
for
(i=
1
;i<=k;i++)
sum+=stir[n][i]-stir[n-
1
][i-
1
];
printf(
"%.4lf\n"
,
1.0
*sum/f[n]);
//因为写成printf("%.4lf\n",(double)sum/f[n]);
//run time error! 下次一定记好了!
}
return
0
;
}
|
4
Posted by: spoiler in: 斯特林数的各种应用
题目:
Problem Description
In many applications very large integers numbers are required. Some of these applications are using keys for secure transmission of data, encryption, etc. In this problem you are given a number, you have to determine the number of digits in the factorial of the number.
|
Input
Input consists of several lines of integer numbers. The first line contains an integer n, which is the number of cases to be tested, followed by n lines, one integer 1 ≤ n ≤ 10
7 on each line.
|
Output
The output contains the number of digits in the factorial of the integers appearing in the input. |
Sample Input
2 10 20 |
Sample Output
7 19 |
题目分析:
第一种做法:
N!=1*2*3....*n
求位数我们一般用对一个数取对数就可以了 ,
log10(n!)=log10(1)+ log10(2) +log10(3)...+log10(n);
所以循环求和就可以了!
但是这里注意一点 结果要加1!因为这里计算出来的 log10(1)=0 !
所以结果要加上这个误差 ‘1’
第二种做法:
这就是我最近研究的斯特林数,第一类斯特林数就可以做这个!
补充一点,斯特林数能够做一切关于阶乘有关的大数运算 要深入学习!
这里给出递归公式:
log10(n!)=1.0/2*log10(2*pi*n)+n*log10(n/e)
然后我就附上代码了;
两种做法都有!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
#
include
<iostream>
#
include
<cmath>
#
include
<cstdio>
#define e
2.7182818284590452354
#define pi acos(-
1
)
using
namespace
std;
int
main(){
int
cas,ans,n;
cin>>cas;
while
(cas--){
scanf(
"%d"
,&n);
ans=(
int
)(
1.0
/
2.0
*log(
2.0
*pi*n)/log(
10.0
)+
1.0
*n*log(n/e)/log(
10.0
)+
1
);
printf(
"%d\n"
,ans);
}
return
0
;
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
#
include
<iostream>
#
include
<cmath>
#
include
<cstdio>
#define e
2.7182818284590452354
#define pi acos(-
1
)
using
namespace
std;
int
main(){
int
cas,ans,i,n;
double sum;
cin>>cas;
while
(cas--){
scanf(
"%d"
,&n);
sum=
1
;
for
(i=
1
;i<=n;i++)
sum+=log10(i);
printf(
"%d\n"
,((
int
)sum));
}
return
0
;
}
|
204
Posted by: spoiler in: 斯特林数的各种应用
题目:
就是给你N个房间,然后每个房间1把钥匙,你最初手里没有任何钥匙,要靠破门而入!这里只有第一个房间不能破门进去,其他都可以,
给你房间数N,和最多能破门的个数,让你求能全部把房间打开的概率!
题目分析:
又是是我的第一次啊!受教育了?有木有?这种题目是斯特林第一类数的应用,虽然很裸,但是很经典啊 !
首先这题其实让我们求的是给 N个元素,让我们求K个环排列的 方法数。
斯特林第一类数的第推公式:
S(N,0)=0;
S(N,N)=1;
S(0,0)=0;
S(N,K)=S(N-1,K-1)+S(N-1,K)*(N-1);
这个公式的意思是:
当前N-1个数构成K-1 个环的时候,加入第N个 ,N只能构成单环!---S(N-1,K-1)
如果N-1个数构成K个环的时候,加入第N个,N可以任意加入,N-1内的一个环里,所以是--(N-1)*S(N-1,K)
这个题目里,因为不能破坏第1个门:
所以
S(N,K)-S(N-1,K-1)才是能算构成K个环的方法数!就是去掉1自己成环的情况!
代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
#
include
<iostream>
#
include
<cstdio>
#
include
<cstring>
using
namespace
std;
const
int
maxn=
20
;
long long f[
25
],stir[
25
][
25
];
int
solve(){
int
i,j;
f[
0
]=
1
;
for
(i=
1
;i<=maxn;i++)
f[i]=i*f[i-
1
];
//因为N有N!种排列顺序,这作为总数
//计算概率
for
(i=
1
;i<=maxn;i++)
stir[i][
0
]=
0
;
stir[
1
][
1
]=
1
;
for
(i=
1
;i<=maxn;i++)
for
(j=
1
;j<=i;j++){
if
(i==j)
stir[i][j]=
1
;
else
stir[i][j]=stir[i-
1
][j-
1
]+(i-
1
)*stir[i-
1
][j];
}
for
(i=
1
;i<=maxn;i++)
for
(j=
1
;j<=maxn;j++)
if
(stir[i][j]<
0
)
stir[i][j]=-stir[i][j];
return
0
;
}
int
main(){
int
cas,n,i,k;
long long sum;
solve();
scanf(
"%d"
,&cas);
while
(cas--){
scanf(
"%d %d"
,&n,&k);
sum=
0
;
for
(i=
1
;i<=k;i++)
sum+=stir[n][i]-stir[n-
1
][i-
1
];
printf(
"%.4lf\n"
,
1.0
*sum/f[n]);
//因为写成printf("%.4lf\n",(double)sum/f[n]);
//run time error! 下次一定记好了!
}
return
0
;
}
|