求集合所有K划分的算法

在准备一篇论文的过程中,需要将一个集合进行k划分,并求出所有划分,网上搜了下并没有直接可用的算法,只好自己动手,特分享出来,转载请注明出处。

首先分析将包含n个元素的集合进行k划分,假定总的划分次数为F(n,k),则其所有的划分可以由kF(n-1,k)和F(n-1,k-1)得到,前者是指将集合的前n-1个元素划分成k个子集,则将第k个元素可以加入其中的任一个子集形成一个新的划分,后者是指集合的前n-1个元素划分成k-1个子集,则第k个元素作为一个单独的元素组成一个子集形成k划分,基于动态规划算法,建立二维矩阵subsets[n][k]保存所有划分。

随着k的增加,所有划分的集合空间迅速增大,需要找到一种高效的划分表示方法,我使用了一种称为“restricted growthfunction”的方法表示,对于大小为4的集合,RG=[0,0,0,0]表示集合划分{1,2,3,4} , RG=[0,1,1,0]表示{1,4},{2,3},具体见网页: http://delphiforfun.org/programs/Math_Topics/set_partitions.htm

实现的java代码如下:

import java.util.Random;
import java.lang.String;
import java.util.*;

public class cmf {

public static void main(String[] args) {

int[] n = new int[5];
for (int i = 0; i < n.length; i++){
n[i] = i+1;//(int) (Math.random()*100);
}
System.out.print("The sequence:");         
for(int i = 0; i < n.length; ++i){
System.out.print(n[i]+" ");
}
System.out.println("");
//      BigInteger l =getLCM(n);      
//      System.out.println("Their LCM is " +l.toString());

int maxk = 3;
int k = 0;
ArrayList[][] subsets = new ArrayList[n.length][maxk];
for (int i = 0; i < n.length; i++){
//free the unuseful objects to JVM 
if(i > 2)
for(int x = 0; x < maxk; x++){
subsets[i-2][x] =null;            
}

for(int j = 0; j < maxk; j++){
if(i >= j){
subsets[i][j] = newArrayList();
}    
if(j == 0){
int[] s = new int[i+1];
for(k = 0; k < i+1; k++){
s[k] = 0;
}               
subsets[i][j].add(s);
}
else if(i == j){
int[] s = new int[i+1];
int rgstart = 0;
for(k = 0; k < i+1; k++){
s[k] = rgstart++;
}
subsets[i][j].add(s);            
}
else if ( i >j){               
ArrayList p = subsets[i-1][j];
for (k=0; k < p.size(); k++){
int[] aboveRG = (int[]) p.get(k);
int setsize = getMax(aboveRG);
for(int l = 0; l <= setsize; l++){
int[] s = new int[aboveRG.length+1];
for(int m = 0; m < aboveRG.length; m++)
s[m]=aboveRG[m];
s[aboveRG.length] = l;

subsets[i][j].add(s);
}
}
p =  subsets[i-1][j-1];
for (k=0; k < p.size(); k++){
int[] aboveRG = (int[]) p.get(k);
int[] s = new int[aboveRG.length+1];
for(int m = 0; m < aboveRG.length; m++)
s[m]=aboveRG[m];
s[aboveRG.length] = getMax(aboveRG)+1;
subsets[i][j].add(s);                                
}
}
if(i >= j)
System.out.println(""+(i+1)+","+(j+1)+""+subsets[i][j].size());
}
}
ArrayList p = subsets[n.length-1][maxk-1];

//    for (inti=0; i < p.size(); i++){
//      int[] setRG = (int[]) p.get(i);
//System.out.println(setRG.toString());
//   System.out.println(readPartition(setRG,n));
//}
System.out.println("Total number: "+p.size()+""+maxk+"-partitions");

}

public static String readPartition(int[] setRG, int[] n)
{
String ret = "";
int setsize = getMax(setRG);
for(int j =0; j <= setsize; j++){
for(int i = 0; i < setRG.length; i++){
//      System.out.print(""+setRG[i]);
if(setRG[i] == j)
ret += n[i]+" ";
}
ret +=",";         
}
return ret;
}
}

这个算法只是最原始最暴力最野蛮的穷举算法,在内存4G的机器上,对30个元素的集合,只能算到4以内的划分,后面就会导致JVM内存溢出,一般来说获取集合的划分并不会真的去得到所有划分,而只是寻找其中一些子集,就需要针对具体优化目标结合上述过程进行剪枝。

你可能感兴趣的:(CS)