7

In Java I have a set where I want to obtain all possible combinations of subsets which their union make the main set. (partitioning a set) for example, given:

set={1,2,3}

the result should be:

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

the number of possible partition of a set of n elements is B(n) known as Bell number.

The code so far:

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

which outputs the powerset of the the array :

[[], [1], [2], [1, 2], [3], [1, 3], [2, 3], [1, 2, 3]]
Richard Neish
  • 8,414
  • 4
  • 39
  • 69
khodayar J
  • 914
  • 8
  • 16
  • 3
    What is the code you've already produced? – Yassin Hajaj Oct 07 '15 at 18:59
  • I can extract the powerset of set and also the partitions of only two sets for example just {{1,2},{3}} and {{1,3},{2}} – khodayar J Oct 07 '15 at 19:01
  • Ok, edit your post and give us the source code – Yassin Hajaj Oct 07 '15 at 19:02
  • 2
    Possible duplicates [generate all partitions of a set](https://stackoverflow.com/questions/30893292/generate-all-partitions-of-a-set), [How to find all partitions of a set](https://stackoverflow.com/questions/20530128/how-to-find-all-partitions-of-a-set) and [Finding all partitions of a set in Java](https://stackoverflow.com/questions/30769867/finding-all-partitions-of-a-set-in-java) – Naman Jun 03 '19 at 08:59

4 Answers4

4

The easiest solution is using recursion on the number of elements of your set: Build a function that constructs all partitions of an n-element set. For n+1 elements, you either add the new element to of the existing partition sets, or put it in a set of its own.

MightyCurious
  • 833
  • 11
  • 20
  • could you please explain more ? – khodayar J Oct 07 '15 at 19:12
  • 3
    You build a function that takes the number n of elements of the ground set as an argument. If n=0, this function just returnd the empty set. Otherwise, it calls itself with argument n-1, and uses the result as explained above to compute the answer for n. I'm sorry I can't give you code snippets as I'm not a Java programmer. But this recursive approach is a general pattern that also works in Java. – MightyCurious Oct 07 '15 at 19:18
4

A solution for the searched algorithm would be:

In Pseudocode:

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

Or the implementation in java (using a class instead of a static function):

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

The generated output is:

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

The output that is created is similar to the expected output you were asking for except that there is no empty set in any of the solutions (except when the input set is empty). So the generated partitions of the set and the number of partitions of a set are now compliant with the Bell Numbers.

Tobias
  • 2,547
  • 3
  • 14
  • 29
  • 1
    i think it is "more correct" for the algorithm not include `[]` since the definition of partition is "_A partition of a set X is a set of nonempty subsets of X such that every element x in X is in exactly one of these subsets_". – marcopiii Jun 03 '19 at 14:27
  • 1
    In this case you could just remove the empty set from the power-set before building the partitions. I'll edit that. Thanks for the advice. – Tobias Jun 03 '19 at 14:32
  • 1
    I tested your code with jUnit. it works. I would make only one change: the only case where the empty set should be allowed should be when the input set is empty, so the number of returned partition is 1, making it compliant with [Bell number](https://en.wikipedia.org/wiki/Bell_number). – marcopiii Jun 03 '19 at 22:19
  • 1
    Edited this too. Now it should be compliant with the Bell Numbers. Thanks for the advice again :) – Tobias Jun 04 '19 at 04:18
  • can i remove sets with size greater than `x` from power set, if i want the partitions depth be `x` or less than `x`? – subject-q Sep 23 '19 at 11:52
3

First, the powerset:

For n distinct elements, you can create 2n sets which can easily shown when considering the question “is this element included in this particular set?” a boolean value:

For 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

So iterating over all combinations can be implemented by iterating over integer numbers from 0 to 2ⁿ and using the bit pattern of each number to select the elements from the original set (we have to copy them into an ordered structure like a List for that):

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

Then

Set<String> input = new HashSet<>();
Collections.addAll(input, "1", "2", "3");
System.out.println(allPermutations(input));

gives us

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

To utilize this for identifying the partitions, we have to expand the logic to use the counter’s bits to select bits from another mask, which will identify the actual elements to include. Then, we can re-use the same operation to get the partitions of the elements not included so far, using a simple binary not operation:

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

Then,

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

gives us

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

whereas

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

yields

[[1], [3], [2, 4]]
[[1], [2], [3], [4]]
[[1], [2], [3, 4]]
[[1], [4], [2, 3]]
[[4], [1, 2, 3]]
[[1], [2, 3, 4]]
[[2], [3], [1, 4]]
[[2], [4], [1, 3]]
[[1, 2, 3, 4]]
[[1, 3], [2, 4]]
[[1, 4], [2, 3]]
[[2], [1, 3, 4]]
[[3], [1, 2], [4]]
[[1, 2], [3, 4]]
[[3], [1, 2, 4]]
Holger
  • 285,553
  • 42
  • 434
  • 765
1

We will convert the Set to an array and use List in order to be able to keep index. Once we have recieved all mathematical subset of the set we will convert back to the Java Set.

In addition we will use T[] template because you have used generics for the Set. We need this array in order to define the type for the conversion to array.

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

The source code is a modified version of the algorithm presented here, to fit the generics requirement https://java2blog.com/find-subsets-set-power-set/ enter image description herehttps://java2blog.com/wp-content/uploads/2018/08/subsetHelperRecursion.jpg

Alexander Petrov
  • 9,204
  • 31
  • 70
  • 1
    this finds all the subsets of a set (the powerset). The answer is asking for all possible partitions. – marcopiii Jun 03 '19 at 10:15