n个元素的集合{1,2,…,n}可以划分若干个非空子集。例如,当n=4时,集合{1,2,3,4}可以划分为15个不同的非空子集如下:
{{1},{2},{3},{4}},
{{1,2},{3},{4}},
{{1,3},{2},{4}},
{{1,4},{2},{3}},
{{2,3},{1},{4}},
{{2,4},{1},{3}},
{{3,4},{1},{2}},
{{1,2},{3,4}},
{{1,3},{2,4}},
{{1,4},{2,3}},
{{1,2,3},{4}},
{{1,2,4},{3}},
{{1,3,4},{2}},
{{2,3,4},{1}},
{{1,2,3,4}}
给定正整数n(1<=n<=20),计算出n个元素的集合{1,2,…,n} 可以化为多少个不同的非空子集。
Bell数的定义:第n个Bell数表示集合{1,2,3,…,n}的划分方案数,B(0)=1;
B(n)=∑n−1i=0(n−1i)B(i) B ( n ) = ∑ i = 0 n − 1 ( i n − 1 ) B ( i )
每一个Bell数都是第二类Stirling数的和,即:
B(n)=∑nm=1S(n,m) B ( n ) = ∑ m = 1 n S ( n , m )
第二类Stirling数的意义是:S(n,m)表示将n个物体划分成m个非空的不可辨别的(可以理解为盒子没有编号)集合的方法数。
很明显,每一个Bell数是对应的第二类Stirling数之和。
Bell数问题:
B(n)=∑nm=1S(n,m) B ( n ) = ∑ m = 1 n S ( n , m )
可以转化为求 第2类Stirling数问题 参考集合划分问题(第2类Stirling数)
S(n,m)=S(n−1,m−1)+m∗S(n−1,m) S ( n , m ) = S ( n − 1 , m − 1 ) + m ∗ S ( n − 1 , m )
理解:
设n个元素的集合可以划分为S(n,m)个不同的由m个非空子集组成的集合。
S(n,m)的两种情况:
一种是独自组成一个集合,另一种是和别的元素混在一起。
对於第一种情况,等价于把前n-1个元素分成m-1份,然后n号元素单独放。
对於第二种情况,等价于把前n-1个元素分成m份,然后把n号元素放入这m个集合中的一个(有m种放法)
推出:
S(n,m) = S(n-1,m-1) + m * S(n-1,m)
示例:
考虑3个元素的集合,可划分为
① 1个子集的集合:{{1,2,3}}
② 2个子集的集合:{{1,2},{3}},{{1,3},{2}},{{2,3},{1}}
③ 3个子集的集合:{{1},{2},{3}}
S(3,1)=1; S(3,2)=3; S(3,3)=1;
如何求S(4,2)?
A.往①里添一个元素{4},得到{{1,2,3},{4}}
B.往②里的任意一个子集添一个4,得到
{{1,2,4},{3}},{{1,2},{3,4}},
{{1,3,4},{2}},{{1,3},{2,4}},
{{2,3,4},{1}},{{2,3},{1,4}}
S(4,2) = S(3,1) + 2*S(3,2) = 1+2*3 = 7
推广,亦可得S(n,m) = S(n-1,m-1) + m*S(n-1,m)
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
while (true){
long bellNumber = 0;
System.out.println("Input a number: ");
int n = input.nextInt();
for(int i=1; i<=n; i++){
bellNumber += S(n, i);
}
System.out.println("There are "+bellNumber+" different sets!");
System.out.println("----------------------------------------");
}
}
private static long S(int n, int m){
if((n < m) || (m == 0))
return 0;
if((m == 1) || (m == n))
return 1;
return m*S(n-1, m) + S(n-1, m-1);
}
}
Input a number:
20
There are 51724158235372 different sets!
----------------------------------------
Input a number:
5
There are 52 different sets!
----------------------------------------
Input a number:
4
There are 15 different sets!
----------------------------------------
Input a number:
3
There are 5 different sets!
----------------------------------------
Input a number:
2
There are 2 different sets!
----------------------------------------
Input a number:
1
There are 1 different sets!
----------------------------------------
Input a number:
import java.util.Scanner;
public class Main {
private static long[][] S = new long[100][100];
private static long[] B = new long[100];
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
while (true){
long bellNumber = 0;
System.out.println("Input a number: ");
int n = input.nextInt();
bellNumber = Bell(n);
System.out.println("There are "+bellNumber+" different sets!");
System.out.println("----------------------------------------");
}
}
private static long Bell(int n){
Stirling2(n, n);
for(int i=1; i<=n; i++)
B[i] = 0;
for(int i=1; i<=n; i++){
for(int j=0; j<=i; j++)
B[i] += S[i][j];
}
return B[n];
}
private static void Stirling2(int n, int m){
int min;
S[0][0] = 1;
for(int i=1; i<=n; i++)
S[i][0] = 0;
for(int i=0; i1] = 0;
for(int i=1; i<=n; i++){
if(i < m)
min = i;
else
min = m;
for(int j=1; j<=min; j++)
S[i][j] = j*S[i-1][j] + S[i-1][j-1];
}
}
}
Input a number:
20
There are 51724158235372 different sets!
----------------------------------------
Input a number:
5
There are 52 different sets!
----------------------------------------
Input a number:
4
There are 15 different sets!
----------------------------------------
Input a number:
3
There are 5 different sets!
----------------------------------------
Input a number:
2
There are 2 different sets!
----------------------------------------
Input a number:
1
There are 1 different sets!
----------------------------------------
Input a number:
王晓东《计算机算法设计与分析》(第3版)P45