Java 获取集合中所有可能的分区

Java 获取集合中所有可能的分区,java,collections,set,partitioning,Java,Collections,Set,Partitioning,在Java中,我有一个集合,我想获得所有可能的子集组合,它们的并集构成了主集合。(对集合进行分区) 例如,假设: set={1,2,3} 结果应该是: { {{1,2,3}} , {{1},{2,3}} , {{1,2},{3}} , {{1,3},{2}}, {{1},{2},{3}}} 一组n元素的可能分区数为B(n)称为 迄今为止的守则: public static <T> Set<Set<T>> powerSet(Set<T> myse

在Java中,我有一个集合,我想获得所有可能的子集组合,它们的并集构成了主集合。(对集合进行分区) 例如,假设:

set={1,2,3}
结果应该是:

{ {{1,2,3}} , {{1},{2,3}} , {{1,2},{3}} , {{1,3},{2}}, {{1},{2},{3}}}
一组
n
元素的可能分区数为
B(n)
称为

迄今为止的守则:

public static <T> Set<Set<T>> powerSet(Set<T> myset) {
        Set<Set<T>> pset = new HashSet<Set<T>>();
        if (myset.isEmpty()) {
            pset.add(new HashSet<T>());
            return pset;
        }
        List<T> list = new ArrayList<T>(myset);
        T head = list.get(0);
        Set<T> rest = new HashSet<T>(list.subList(1, list.size()));
        for (Set<T> set : powerSet(rest)) {
            Set<T> newSet = new HashSet<T>();
            newSet.add(head);
            newSet.addAll(set);
            pset.add(newSet);
            pset.add(set); 
        }

        return pset;
    }

最简单的解决方案是对集合中的元素数使用递归:构建一个函数来构造一个n元素集合的所有分区。对于n+1个元素,您可以将新元素添加到现有分区集中的一个分区集中,或者将其放入自己的分区集中。

我们将把该分区集中的元素转换为数组并使用列表,以便能够保留索引。一旦我们接收到集合的所有数学子集,我们将转换回Java集合

此外,我们将使用T[]模板,因为您已经对集合使用了泛型。我们需要这个数组来定义转换为数组的类型

public <T>Set<Set<T>> subsets(Set<T> nums,T[] template) {
    T[] array = nums.toArray(template);
    List<List<T>> list = new ArrayList<>();
    subsetsHelper(list, new ArrayList<T>(), array, 0);
    return list.stream()
               .map(t->t.stream().collect(Collectors.toSet()))
               .collect(Collectors.toSet());
}

private <T>void subsetsHelper(List<List<T>> list , List<T> resultList, T[]  nums, int start){
    list.add(new ArrayList<>(resultList));

    for(int i = start; i < nums.length; i++){
       // add element
        resultList.add(nums[i]);
       // Explore
        subsetsHelper(list, resultList, nums, i + 1);
       // remove
        resultList.remove(resultList.size() - 1);
    }
}
公共集合子集(集合nums,T[]模板){
T[]数组=nums.toArray(模板);
列表=新的ArrayList();
SubsetHelper(列表,新ArrayList(),数组,0);
return list.stream()
.map(t->t.stream().collect(Collectors.toSet()))
.collect(收集器.toSet());
}
私有void subsetHelper(列表列表、列表结果列表、T[]nums、int start){
添加(新的ArrayList(resultList));
对于(int i=开始;i
源代码是这里介绍的算法的修改版本,以满足泛型要求
搜索算法的解决方案是:

在伪代码中:

Set<T> base; //the base set
Set<Set<T>> pow; //the power set
Set<Set<Set<T>>> parts; //the partitions set

function findAllPartSets():
    pow = power set of base
    if (pow.length > 1) {
        pow.remove(empty set);
    }
    for p in pow:
        findPartSets(p);

function findPartSets(Set<Set<T>> current):
    maxLen = base.length - summed length of all sets in current;
    if (maxLen == 0) {
        parts.add(current);
        return;
    }
    else {
        for i in 1 to maxLen {
            for s in pow {
                if (s.length == i && !(any of s in current)) {
                    Set<Set<T>> s2 = new Set(current, s);
                    findPartSets(s2);
                }
            }
        }
    }
创建的输出与您要求的预期输出类似,只是任何解决方案中都没有空集(输入集为空时除外)。 因此,生成的集合分区和集合分区数现在符合。

首先,powerset:

对于n个不同的元素,您可以创建2n个集合,当考虑“此元素是否包含在此特定集合中?”这一布尔值时,可以很容易地显示这些集合:

对于n=3:

因此,可以通过迭代0到2的整数来实现对所有组合的迭代ⁿ 并使用每个数字的位模式从原始集合中选择元素(为此,我们必须将它们复制到一个有序结构中,如
列表
):


为了利用它来标识分区,我们必须扩展逻辑,使用计数器的位从另一个掩码中选择位,该掩码将标识要包含的实际元素。然后,我们可以使用一个简单的二进制not操作,重新使用相同的操作来获取到目前为止未包含的元素的分区:

public static <T> Set<Set<Set<T>>> allPartitions(Set<T> input) {
    List<T> sequence = new ArrayList<>(input);
    if(sequence.size() > 62) throw new OutOfMemoryError();
    return allPartitions(sequence, (1L << sequence.size()) - 1);
}
private static <T> Set<Set<Set<T>>> allPartitions(List<T> input, long bits) {
    long count = 1L << Long.bitCount(bits);

    if(count == 1) {
        return Collections.singleton(new HashSet<>());
    }

    Set<Set<Set<T>>> result = new HashSet<>();

    for(long l = 1; l >= 0 && l < count; l++) {
        long select = selectBits(l, bits);
        final Set<T> first = get(input, select);
        for(Set<Set<T>> all: allPartitions(input, bits&~select)) {
            all.add(first);
            result.add(all);
        }
    }
    return result;
}
private static long selectBits(long selected, long mask) {
    long result = 0;
    for(long bit; selected != 0; selected >>>= 1, mask -= bit) {
        bit = Long.lowestOneBit(mask);
        if((selected & 1) != 0) result |= bit;
    }
    return result;
}
private static <T> Set<T> get(List<T> elements, long bits) {
    if(bits == 0) return Collections.emptySet();
    else if(Long.lowestOneBit(bits) == bits)
        return Collections.singleton(elements.get(Long.numberOfTrailingZeros(bits)));
    else {
        HashSet<T> next = new HashSet<>();
        for(; bits != 0; bits-=Long.lowestOneBit(bits)) {
            next.add(elements.get(Long.numberOfTrailingZeros(bits)));
        }
        return next;
    }
}
鉴于

Set<String> input = new HashSet<>();
Collections.addAll(input, "1", "2", "3", "4");
for(Set<Set<String>> partition: allPartitions(input))
    System.out.println(partition);

你已经生成的代码是什么?我可以提取set的powerset,也可以提取两个set的分区,例如{{1,2},{3}和{1,3},{2}好的,编辑你的帖子,给我们可能重复的源代码,你能解释一下吗?你建立了一个函数,它以基集合的元素数n为参数。如果n=0,此函数只返回空集。否则,它用参数n-1调用自己,并使用上面解释的结果来计算n的答案。很抱歉,我不能提供代码片段,因为我不是Java程序员。但是这种递归方法是一种通用模式,在Java中也可以使用。它可以查找集合(powerset)的所有子集。答案是要求所有可能的分区。我认为算法不包括
[]
“更正确”,因为分区的定义是“集合X的分区是X的非空子集的集合,因此X中的每个元素X正好位于这些子集中的一个”。在这种情况下,您只需在构建分区之前从电源集中删除空集即可。我会编辑的。谢谢你的建议。我用jUnit测试了你的代码。它起作用了。我只做一个更改:唯一允许使用空集的情况应该是当输入集为空时,因此返回的分区数为1,使其符合。现在,它应该符合钟号。再次感谢您的建议:)如果我希望分区深度为
x
或小于
x
,我可以从电源集中删除大小大于
x
的集合吗?
BaseSet: []
Result:  [[[]]]
Base-Size: 0 Result-Size: 1
BaseSet: [1]
Result:  [[[1]]]
Base-Size: 1 Result-Size: 1
BaseSet: [1, 2]
Result:  [[[1], [2]], [[1, 2]]]
Base-Size: 2 Result-Size: 2
BaseSet: [1, 2, 3]
Result:  [[[1], [2], [3]], [[1], [2, 3]], [[2], [1, 3]], [[1, 2], [3]], [[1, 2, 3]]]
Base-Size: 3 Result-Size: 5
BaseSet: [1, 2, 3, 4]
Result:  [[[1], [2], [3], [4]], [[1], [2], [3, 4]], [[4], [1, 2, 3]], [[1], [3], [2, 4]], [[1, 2, 3, 4]], [[1], [4], [2, 3]], [[1], [2, 3, 4]], [[2], [3], [1, 4]], [[2], [4], [1, 3]], [[2], [1, 3, 4]], [[1, 3], [2, 4]], [[1, 2], [3], [4]], [[1, 2], [3, 4]], [[3], [1, 2, 4]], [[1, 4], [2, 3]]]
Base-Size: 4 Result-Size: 15
0: 0 0 0   none included
1: 0 0 1   first included
2: 0 1 0   second included
3: 0 1 1   first and second one included
4: 1 0 0   third included
5: 1 0 1   first and third included
6: 1 1 0   second and third included
7: 1 1 1   all included
public static <T> Set<Set<T>> allPermutations(Set<T> input) {

    List<T> sequence = new ArrayList<>(input);
    long count = sequence.size() > 62? Long.MAX_VALUE: 1L << sequence.size();

    HashSet<Set<T>> result = new HashSet<>((int)Math.min(Integer.MAX_VALUE, count));

    for(long l = 0; l >= 0 && l < count; l++) {
        if(l == 0) result.add(Collections.emptySet());
        else if(Long.lowestOneBit(l) == l)
            result.add(Collections.singleton(sequence.get(Long.numberOfTrailingZeros(l))));
        else {
            HashSet<T> next = new HashSet<>((int)(Long.bitCount(l)*1.5f));
            for(long tmp = l; tmp != 0; tmp-=Long.lowestOneBit(tmp)) {
                next.add(sequence.get(Long.numberOfTrailingZeros(tmp)));
            }
            result.add(next);
        }
    }
    return result;
}
Set<String> input = new HashSet<>();
Collections.addAll(input, "1", "2", "3");
System.out.println(allPermutations(input));
public static <T> Set<Set<Set<T>>> allPartitions(Set<T> input) {
    List<T> sequence = new ArrayList<>(input);
    if(sequence.size() > 62) throw new OutOfMemoryError();
    return allPartitions(sequence, (1L << sequence.size()) - 1);
}
private static <T> Set<Set<Set<T>>> allPartitions(List<T> input, long bits) {
    long count = 1L << Long.bitCount(bits);

    if(count == 1) {
        return Collections.singleton(new HashSet<>());
    }

    Set<Set<Set<T>>> result = new HashSet<>();

    for(long l = 1; l >= 0 && l < count; l++) {
        long select = selectBits(l, bits);
        final Set<T> first = get(input, select);
        for(Set<Set<T>> all: allPartitions(input, bits&~select)) {
            all.add(first);
            result.add(all);
        }
    }
    return result;
}
private static long selectBits(long selected, long mask) {
    long result = 0;
    for(long bit; selected != 0; selected >>>= 1, mask -= bit) {
        bit = Long.lowestOneBit(mask);
        if((selected & 1) != 0) result |= bit;
    }
    return result;
}
private static <T> Set<T> get(List<T> elements, long bits) {
    if(bits == 0) return Collections.emptySet();
    else if(Long.lowestOneBit(bits) == bits)
        return Collections.singleton(elements.get(Long.numberOfTrailingZeros(bits)));
    else {
        HashSet<T> next = new HashSet<>();
        for(; bits != 0; bits-=Long.lowestOneBit(bits)) {
            next.add(elements.get(Long.numberOfTrailingZeros(bits)));
        }
        return next;
    }
}
    Set<String> input = new HashSet<>();
    Collections.addAll(input, "1", "2", "3");
    System.out.println(allPartitions(input));
Set<String> input = new HashSet<>();
Collections.addAll(input, "1", "2", "3", "4");
for(Set<Set<String>> partition: allPartitions(input))
    System.out.println(partition);