获取集合的所有可能分区

时间:2015-10-07 18:59:27

标签: java set partitioning

在java中我有一个集合,我希望获得所有可能的子集组合,它们的联合作为主集合。 (分区一组) 例如 : 集合= {1,2,3}

结果应为:

{ {{1,2,3},{}} , {{1},{2,3}} , {{1,2},{3}} , {{1,3},{2}}, {{1},{2},{3}}}

到目前为止的代码:

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;
    }

输出数组的powerset:

[[], [1], [2], [1, 2], [3], [1, 3], [2, 3], [1, 2, 3]]

4 个答案:

答案 0 :(得分:2)

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

答案 1 :(得分:2)

搜索算法的解决方案是:

使用伪代码:

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);
                }
            }
        }
    }

或者用Java实现(使用类而不是静态函数):

package partitionSetCreator;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class PartitionSetCreator<T> {

    private Set<Set<Set<T>>> parts;//the partitions that are created
    private Set<Set<T>> pow;//the power set of the input set
    private Set<T> base;//the base set

    /**
     * The main method is just for testing and can be deleted.
     */
    public static void main(String[] args) {
        //test using an empty set = []
        Set<Integer> baseSet = new HashSet<Integer>();
        PartitionSetCreator<Integer> partSetCreatorEmpty = new PartitionSetCreator<Integer>(baseSet);
        Set<Set<Set<Integer>>> partitionSetsEmpty = partSetCreatorEmpty.findAllPartitions();
        System.out.println("BaseSet: " + baseSet);
        System.out.println("Result:  " + partitionSetsEmpty);
        System.out.println("Base-Size: " + baseSet.size() + " Result-Size: " + partitionSetsEmpty.size());

        //test using base set = [1]
        baseSet.add(1);
        PartitionSetCreator<Integer> partSetCreator = new PartitionSetCreator<Integer>(baseSet);
        Set<Set<Set<Integer>>> partitionSets = partSetCreator.findAllPartitions();
        System.out.println("BaseSet: " + baseSet);
        System.out.println("Result:  " + partitionSets);
        System.out.println("Base-Size: " + baseSet.size() + " Result-Size: " + partitionSets.size());

        //test using base set = [1, 2]
        baseSet.add(2);
        PartitionSetCreator<Integer> partSetCreator2 = new PartitionSetCreator<Integer>(baseSet);
        Set<Set<Set<Integer>>> partitionSets2 = partSetCreator2.findAllPartitions();
        System.out.println("BaseSet: " + baseSet);
        System.out.println("Result:  " + partitionSets2);
        System.out.println("Base-Size: " + baseSet.size() + " Result-Size: " + partitionSets2.size());

        //another test using base set = [1, 2, 3]
        baseSet.add(3);
        PartitionSetCreator<Integer> partSetCreator3 = new PartitionSetCreator<Integer>(baseSet);
        Set<Set<Set<Integer>>> partitionSets3 = partSetCreator3.findAllPartitions();
        System.out.println("BaseSet: " + baseSet);
        System.out.println("Result:  " + partitionSets3);
        System.out.println("Base-Size: " + baseSet.size() + " Result-Size: " + partitionSets3.size());

        //another test using base set = [1, 2, 3, 4]
        baseSet.add(4);
        PartitionSetCreator<Integer> partSetCreator4 = new PartitionSetCreator<Integer>(baseSet);
        Set<Set<Set<Integer>>> partitionSets4 = partSetCreator4.findAllPartitions();

        System.out.println("BaseSet: " + baseSet);
        System.out.println("Result:  " + partitionSets4);
        System.out.println("Base-Size: " + baseSet.size() + " Result-Size: " + partitionSets4.size());
    }

    public PartitionSetCreator(Set<T> base) {
        this.base = base;
        this.pow = powerSet(base);
        if (pow.size() > 1) {
            //remove the empty set if it's not the only entry in the power set
            pow.remove(new HashSet<T>());           
        }
        this.parts = new HashSet<Set<Set<T>>>();
    }

    /**
     * Calculation is in this method.
     */
    public Set<Set<Set<T>>> findAllPartitions() {
        //find part sets for every entry in the power set
        for (Set<T> set : pow) {
            Set<Set<T>> current = new HashSet<Set<T>>();
            current.add(set);
            findPartSets(current);
        }

        //return all partitions that were found
        return parts;
    }

    /**
     * Finds all partition sets for the given input and adds them to parts (global variable).
     */
    private void findPartSets(Set<Set<T>> current) {
        int maxLen = base.size() - deepSize(current);
        if (maxLen == 0) {
            //the current partition is full -> add it to parts
            parts.add(current);
            //no more can be added to current -> stop the recursion
            return;
        }
        else {
            //for all possible lengths
            for (int i = 1; i <= maxLen; i++) {
                //for every entry in the power set
                for (Set<T> set : pow) {
                    if (set.size() == i) {
                        //the set from the power set has the searched length
                        if (!anyInDeepSet(set, current)) {
                            //none of set is in current
                            Set<Set<T>> next = new HashSet<Set<T>>();
                            next.addAll(current);
                            next.add(set);
                            //next = current + set
                            findPartSets(next);
                        }
                    }
                }
            }
        }
    }

    /**
     * Creates a power set from the base set.
     */
    private Set<Set<T>> powerSet(Set<T> base) {
        Set<Set<T>> pset = new HashSet<Set<T>>();
        if (base.isEmpty()) {
            pset.add(new HashSet<T>());
            return pset;
        }
        List<T> list = new ArrayList<T>(base);
        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;
    }

    /**
     * The summed up size of all sub-sets
     */
    private int deepSize(Set<Set<T>> set) {
        int deepSize = 0;
        for (Set<T> s : set) {
            deepSize += s.size();
        }
        return deepSize;
    }

    /**
     * Checks whether any of set is in any of the sub-sets of current
     */
    private boolean anyInDeepSet(Set<T> set, Set<Set<T>> current) {
        boolean containing = false;

        for (Set<T> s : current) {
            for (T item : set) {
                containing |= s.contains(item);
            }
        }

        return containing;
    }
}

生成的输出是:

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

创建的输出与您要求的期望输出相似,除了在任何解决方案中都没有空集(当输入集为空时除外)。 因此,集合的生成分区和集合的分区数量现在符合Bell Numbers

答案 2 :(得分:1)

我们将Set转换为数组并使用List以便能够保留索引。收到集合的所有数学子集后,我们将转换回Java集合。

此外,我们将使用T []模板,因为您对Set使用了泛型。我们需要此数组才能定义转换为数组的类型。

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);
    }
}

源代码是此处介绍的算法的修改版本,以适应泛型要求https://java2blog.com/find-subsets-set-power-set/ enter image description here https://java2blog.com/wp-content/uploads/2018/08/subsetHelperRecursion.jpg

答案 3 :(得分:1)

首先,功率集:

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

对于 n = 3:

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

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

List

然后

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));

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

[[], [1], [2], [1, 2], [3], [1, 3], [2, 3], [1, 2, 3]]

然后

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));

[[[1], [2], [3]], [[1], [2, 3]], [[2], [1, 3]], [[3], [1, 2]], [[1, 2, 3]]]

收益

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