Obtaining a powerset of a set in Java

后端 未结 26 1700
青春惊慌失措
青春惊慌失措 2020-11-22 11:33

The powerset of {1, 2, 3} is:

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

Let\'s say I have a Set in Java:<

相关标签:
26条回答
  • 2020-11-22 11:44

    Some of the solutions above suffer when the size of the set is large because they are creating a lot of object garbage to be collected and require copying data. How can we avoid that? We can take advantage of the fact that we know how big the result set size will be (2^n), preallocate an array that big, and just append to the end of it, never copying.

    The speedup grows quickly with n. I compared it to João Silva's solution above. On my machine (all measurements approximate), n=13 is 5x faster, n=14 is 7x, n=15 is 12x, n=16 is 25x, n=17 is 75x, n=18 is 140x. So that garbage creation/collection and copying is dominating in what otherwise seem to be similar big-O solutions.

    Preallocating the array at the beginning appears to be a win compared to letting it grow dynamically. With n=18, dynamic growing takes about twice as long overall.

    public static <T> List<List<T>> powerSet(List<T> originalSet) {
        // result size will be 2^n, where n=size(originalset)
        // good to initialize the array size to avoid dynamic growing
        int resultSize = (int) Math.pow(2, originalSet.size());
        // resultPowerSet is what we will return
        List<List<T>> resultPowerSet = new ArrayList<List<T>>(resultSize);
    
        // Initialize result with the empty set, which powersets contain by definition
        resultPowerSet.add(new ArrayList<T>(0)); 
    
        // for every item in the original list
        for (T itemFromOriginalSet : originalSet) {
    
            // iterate through the existing powerset result
            // loop through subset and append to the resultPowerset as we go
            // must remember size at the beginning, before we append new elements
            int startingResultSize = resultPowerSet.size();
            for (int i=0; i<startingResultSize; i++) {
                // start with an existing element of the powerset
                List<T> oldSubset = resultPowerSet.get(i);
    
                // create a new element by adding a new item from the original list
                List<T> newSubset = new ArrayList<T>(oldSubset);
                newSubset.add(itemFromOriginalSet);
    
                // add this element to the result powerset (past startingResultSize)
                resultPowerSet.add(newSubset);
            }
        }
        return resultPowerSet;
    }
    
    0 讨论(0)
  • 2020-11-22 11:45

    Algorithm:

    Input: Set[], set_size 1. Get the size of power set powet_set_size = pow(2, set_size) 2 Loop for counter from 0 to pow_set_size (a) Loop for i = 0 to set_size (i) If ith bit in counter is set Print ith element from set for this subset (b) Print seperator for subsets i.e., newline

    #include <stdio.h>
    #include <math.h>
     
    void printPowerSet(char *set, int set_size)
    {
        /*set_size of power set of a set with set_size
          n is (2**n -1)*/
        unsigned int pow_set_size = pow(2, set_size);
        int counter, j;
     
        /*Run from counter 000..0 to 111..1*/
        for(counter = 0; counter < pow_set_size; counter++)
        {
          for(j = 0; j < set_size; j++)
           {
              /* Check if jth bit in the counter is set
                 If set then pront jth element from set */
              if(counter & (1<<j))
                printf("%c", set[j]);
           }
           printf("\n");
        }
    }
     
    /*Driver program to test printPowerSet*/
    int main()
    {
        char set[] = {'a','b','c'};
        printPowerSet(set, 3);
     
        getchar();
        return 0;
    }

    0 讨论(0)
  • 2020-11-22 11:46

    Here's a solution where I use a generator, the advantage being, the entire power set is never stored at once... So you can iterate over it one-by-one without needing it to be stored in memory. I'd like to think it's a better option... Note the complexity is the same, O(2^n), but the memory requirements are reduced (assuming the garbage collector behaves! ;) )

    /**
     *
     */
    package org.mechaevil.util.Algorithms;
    
    import java.util.BitSet;
    import java.util.Iterator;
    import java.util.Set;
    import java.util.TreeSet;
    
    /**
     * @author st0le
     *
     */
    public class PowerSet<E> implements Iterator<Set<E>>,Iterable<Set<E>>{
        private E[] arr = null;
        private BitSet bset = null;
    
        @SuppressWarnings("unchecked")
        public PowerSet(Set<E> set)
        {
            arr = (E[])set.toArray();
            bset = new BitSet(arr.length + 1);
        }
    
        @Override
        public boolean hasNext() {
            return !bset.get(arr.length);
        }
    
        @Override
        public Set<E> next() {
            Set<E> returnSet = new TreeSet<E>();
            for(int i = 0; i < arr.length; i++)
            {
                if(bset.get(i))
                    returnSet.add(arr[i]);
            }
            //increment bset
            for(int i = 0; i < bset.size(); i++)
            {
                if(!bset.get(i))
                {
                    bset.set(i);
                    break;
                }else
                    bset.clear(i);
            }
    
            return returnSet;
        }
    
        @Override
        public void remove() {
            throw new UnsupportedOperationException("Not Supported!");
        }
    
        @Override
        public Iterator<Set<E>> iterator() {
            return this;
        }
    
    }
    

    To call it, use this pattern:

            Set<Character> set = new TreeSet<Character> ();
            for(int i = 0; i < 5; i++)
                set.add((char) (i + 'A'));
    
            PowerSet<Character> pset = new PowerSet<Character>(set);
            for(Set<Character> s:pset)
            {
                System.out.println(s);
            }
    

    It's from my Project Euler Library... :)

    0 讨论(0)
  • 2020-11-22 11:46

    If n < 63, which is a reasonable assumption since you'd run out of memory (unless using an iterator implementation) trying to construct the power set anyway, this is a more concise way to do it. Binary operations are way faster than Math.pow() and arrays for masks, but somehow Java users are afraid of them...

    List<T> list = new ArrayList<T>(originalSet);
    int n = list.size();
    
    Set<Set<T>> powerSet = new HashSet<Set<T>>();
    
    for( long i = 0; i < (1 << n); i++) {
        Set<T> element = new HashSet<T>();
        for( int j = 0; j < n; j++ )
            if( (i >> j) % 2 == 1 ) element.add(list.get(j));
        powerSet.add(element); 
    }
    
    return powerSet;
    
    0 讨论(0)
  • 2020-11-22 11:47

    A sub-set of t is any set that can be made by removing zero or more elements of t. The withoutFirst subset adds the subsets of t that are missing the first element and the for loop will deal with adding subsets with the first element. For example, if t contained the elements ["1", "2", "3"], missingFirst will add [[""], ["2"], ["3"], ["2","3"]] and the for loop will stick the "1" in front of these element and add it to the newSet. So we'll end up with [[""], ["1"], ["2"], ["3"], ["1", "2"], ["1", "3"], ["2","3"], ["1", "2", "3"]].

    public static Set<Set<String>> allSubsets(Set<String> t) {
            Set<Set<String>> powerSet = new TreeSet<>();
            if(t.isEmpty()) {
                powerSet.add(new TreeSet<>());
                return powerSet;
            }
            String first = t.get(0);
            Set<Set<String>> withoutFirst = allSubsets(t.subSet(1, t.size()));
            for (List<String> 1st : withoutFirst) {
                Set<String> newSet = new TreeSet<>();
                newSet.add(first);
                newSet.addAll(lst);
                powerSet.add(newSet);
            }
            powerSet.addAll(withoutFirst);
            return powerSet;
        }
    
    0 讨论(0)
  • 2020-11-22 11:48
    package problems;
    
    import java.util.ArrayList;
    import java.util.List;
    
    public class SubsetFinderRecursive {
        public static void main(String[] args) {
            //input
            int[] input = new int[3];
            for(int i=0; i<input.length; i++) {
                input[i] = i+1;
            }
            // root node of the tree
            Node root = new Node();
    
            // insert values into tree
            for(int i=0; i<input.length; i++) {
                insertIntoTree(root, input[i]);
            }
    
            // print leaf nodes for subsets
            printLeafNodes(root);
        }
    
        static void printLeafNodes(Node root) {
    
            if(root == null) {
                return;
            }
    
            // Its a leaf node
            if(root.left == null && root.right == null) {
                System.out.println(root.values);
                return;
            }
    
            // if we are not at a leaf node, then explore left and right
    
            if(root.left !=null) {
                printLeafNodes(root.left);
            }
    
            if(root.right != null) {
                printLeafNodes(root.right);
            }
        }
    
        static void insertIntoTree(Node root, int value) {
    
            // Error handling
            if(root == null) {
                return;
            }
    
            // if there is a sub tree then go down
            if(root.left !=null && root.right != null) {
                insertIntoTree(root.left, value);
                insertIntoTree(root.right, value);
            }
    
            // if we are at the leaf node, then we have 2 choices
            // Either exclude or include
            if(root.left == null && root.right == null) {
                // exclude
                root.left = new Node();
                root.left.values.addAll(root.values);
                // include
                root.right = new Node();
                root.right.values.addAll(root.values);
                root.right.values.add(value);
                return;
            }
        }
    
    }
    
    class Node {
        Node left;
        Node right;
        List<Integer> values = new ArrayList<Integer>();
    }
    
    0 讨论(0)
提交回复
热议问题