算法设计与分析: 2-10 集合划分问题(Bell数)

2-10 集合划分问题(Bell数)


问题描述

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)=n1i=0(n1i)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(n1,m1)+mS(n1,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)


递归

Java: version 1

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 & Output

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: 

数组

Java: version 2

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 & Output

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: 

Reference

王晓东《计算机算法设计与分析》(第3版)P45

你可能感兴趣的:(Algorithm,Java,分治递归,计算机算法设计与分析,计算机算法设计与分析)