Ottenere un powerset di un set in Java


86

Il set di potenza di {1, 2, 3}è:

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

Diciamo che ho un Setin Java:

Set<Integer> mySet = new HashSet<Integer>();
mySet.add(1);
mySet.add(2);
mySet.add(3);
Set<Set<Integer>> powerSet = getPowerset(mySet);

Come scrivo la funzione getPowerset, con il miglior ordine di complessità possibile? (Penso che potrebbe essere O (2 ^ n).)


7
Supponi di avere una serie di configurazioni - ad esempio "A", "B" e "C" -, che possono essere utilizzate per parametrizzare un modello e di voler vedere quale sottoinsieme produce il miglior risultato, ad esempio solo "A ". Una possibile soluzione sarebbe testare ogni membro del gruppo di potenza.
João Silva

7
È una domanda dell'intervista di Google per gli sviluppatori di software. È un problema artificioso per testare la tua agilità mentale.
Eric Leschinski

Questa è una domanda ragionevole. Ad esempio, per implementare la funzione di punteggio per il cribbage, devi verificare se qualsiasi elemento del gruppo di potenza aggiunge fino a 15.
John Henckel

Risposte:


101

Sì, lo è O(2^n)davvero, poiché devi generare, beh, 2^npossibili combinazioni. Ecco un'implementazione funzionante, utilizzando generici e set:

public static <T> Set<Set<T>> powerSet(Set<T> originalSet) {
    Set<Set<T>> sets = new HashSet<Set<T>>();
    if (originalSet.isEmpty()) {
        sets.add(new HashSet<T>());
        return sets;
    }
    List<T> list = new ArrayList<T>(originalSet);
    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);
        sets.add(newSet);
        sets.add(set);
    }       
    return sets;
}  

E un test, dato il tuo input di esempio:

 Set<Integer> mySet = new HashSet<Integer>();
 mySet.add(1);
 mySet.add(2);
 mySet.add(3);
 for (Set<Integer> s : SetUtils.powerSet(mySet)) {
     System.out.println(s);
 }

1
Sarebbe più veloce usare Iterator invece di usare list? Ad esempio: Set <T> rest = new HashSet <T> (originalSet); Iteratore <T> i = rest.iterator (); T testa = i.next (); rimuovo(); ?
Dimath

1
@CosminVacaroiu ... cos'altro potrebbe fare?
user253751

3
Sei sicuro che lo sia O(2^n)? Questo è il numero di set nel set di potenza, ma ogni set deve essere creato in memoria, il che richiede tempo almeno proporzionale alla dimensione del set. Secondo wolfram alpha, è in O(n * 2^n): wolfram alpha query
fabian

1
Funzionerebbe anche se la dimensione del set è nell'ordine di 10 ^ 5?
bane19

1
@GauravShankar 2 ^ 100 = 2 ^ (10 ^ 2) è già maggiore di 10 ^ 30. Non assisterai al completamento del calcolo su quale macchina turing lo calcolerai.
Karl Richter

31

In realtà, ho scritto codice che fa quello che chiedi in O (1). La domanda è cosa intendi fare con il set dopo. Se hai intenzione di richiamarlo size(), è O (1), ma se lo ripeti è ovviamente O(2^n).

contains()sarebbe O(n), ecc.

Ne hai davvero bisogno?

MODIFICARE:

Questo codice è ora disponibile in Guava , esposto tramite il metodo Sets.powerSet(set).


Ho bisogno di iterare su ogni sottoinsieme
Manuel Araoz

Ma hai bisogno di memorizzare ogni sottoinsieme?
finnw


E se volessi solo i gruppi di potenza con esattamente k elementi? Il tuo codice è efficiente per questo?
Eyal

Nuovo collegamento (Guava spostato su Github)
yiwei

12

Ecco una soluzione in cui utilizzo un generatore, il vantaggio è che l'intero set di alimentazione non viene mai memorizzato in una volta ... Quindi puoi iterare su di esso uno per uno senza che sia necessario salvarlo in memoria. Mi piacerebbe pensare che sia un'opzione migliore ... Nota la complessità è la stessa, O (2 ^ n), ma i requisiti di memoria sono ridotti (supponendo che il garbage collector si comporti!;))

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

}

Per chiamarlo, usa questo modello:

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

È dalla mia libreria Project Euler ... :)


Quello Guava funziona in modo molto simile a questo, ma è limitato a 32 elementi. Non è irragionevole perché 2 ** 32 è probabilmente troppe iterazioni. Utilizza anche meno memoria della tua perché genera l'AbstractSet solo quando necessario. Prova il tuo codice contro quello Guava dove stampi solo 1 su 10.000 elementi e fai un grande esempio. Scommetto che quello Guava sarà più veloce.
Eyal

@ Eyal, ne sono sicuro, non ho mai affermato il contrario. L'ho scritto io stesso, non è inteso per il codice di produzione. Era un esercizio di algoritmi.
st0le

1
Nota minore: il tuo "returnSet" è un TreeSet, che richiede che i suoi elementi siano comparabili. Questo potrebbe non essere il caso. Considera di scambiarlo con un HashSet o LinkedHashSet
Joris Kinable

10

Se n <63, che è un presupposto ragionevole dal momento che avresti esaurito la memoria (a meno che non si utilizzi un'implementazione iteratore) cercando di costruire comunque il power set, questo è un modo più conciso per farlo. Le operazioni binarie sono molto più veloci degli Math.pow()array per le maschere, ma in qualche modo gli utenti Java ne hanno paura ...

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;

La condizione di terminazione in un ciclo for dovrebbe essere i <(2 << n - 1) invece di i <(1 << n - 1).
bazeusz

Grazie @bazeusz, l'ho cambiato in i < (1 << n)equivalente.
Andrew Mao

Poiché vengono utilizzate operazioni bit wise, penso che ((i >> j) &1) == 1invece di (i >> j) % 2 == 1 possa essere utilizzato. Inoltre, longsono firmati, quindi pensi che il controllo per overflow abbia senso?
Ravi Tiwari

9

Ecco un tutorial che descrive esattamente ciò che desideri, incluso il codice. Hai ragione in quanto la complessità è O (2 ^ n).


2
Non è complessità (n * 2 ^ n)? Poiché la stringa binaria è di lunghezza n, e in ogni iterazione del ciclo principale iteriamo l'intera stringa binaria.
Maggie

1
Il tutorial è fantastico MA ho usato questa tecnica per risolvere il problema dell'HackerRank: ha superato solo la metà dei casi di test e l'altra metà non è riuscita a causa del timeout o ha causato un errore di runtime.
Eugenia Ozirna

7

Ho trovato un'altra soluzione basata sulle idee di @Harry He. Probabilmente non il più elegante ma qui va come ho capito:

Prendiamo il classico esempio semplice PowerSet di SP (S) = {{1}, {2}, {3}}. Sappiamo che la formula per ottenere il numero di sottoinsiemi è 2 ^ n (7 + set vuoto). Per questo esempio 2 ^ 3 = 8 sottoinsiemi.

Per trovare ogni sottoinsieme dobbiamo convertire 0-7 decimali in rappresentazione binaria mostrata nella tabella di conversione seguente:

ConversionTable

Se attraversiamo la tabella riga per riga, ogni riga risulterà in un sottoinsieme e i valori di ogni sottoinsieme verranno dai bit abilitati.

Ogni colonna nella sezione Bin Value corrisponde alla posizione di indice nel set di input originale.

Qui il mio codice:

public class PowerSet {

/**
 * @param args
 */
public static void main(String[] args) {
    PowerSet ps = new PowerSet();
    Set<Integer> set = new HashSet<Integer>();
    set.add(1);
    set.add(2);
    set.add(3);
    for (Set<Integer> s : ps.powerSet(set)) {
        System.out.println(s);
    }
}

public Set<Set<Integer>> powerSet(Set<Integer> originalSet) {
    // Original set size e.g. 3
    int size = originalSet.size();
    // Number of subsets 2^n, e.g 2^3 = 8
    int numberOfSubSets = (int) Math.pow(2, size);
    Set<Set<Integer>> sets = new HashSet<Set<Integer>>();
    ArrayList<Integer> originalList = new ArrayList<Integer>(originalSet);
    for (int i = 0; i < numberOfSubSets; i++) {
        // Get binary representation of this index e.g. 010 = 2 for n = 3
        String bin = getPaddedBinString(i, size);
        //Get sub-set
        Set<Integer> set = getSet(bin, originalList));
        sets.add(set);
    }
    return sets;
}

//Gets a sub-set based on the binary representation. E.g. for 010 where n = 3 it will bring a new Set with value 2
private Set<Integer> getSet(String bin, List<Integer> origValues){
    Set<Integer> result = new HashSet<Integer>();
    for(int i = bin.length()-1; i >= 0; i--){
        //Only get sub-sets where bool flag is on
        if(bin.charAt(i) == '1'){
            int val = origValues.get(i);
            result.add(val);
        }
    }
    return result;
}

//Converts an int to Bin and adds left padding to zero's based on size
private String getPaddedBinString(int i, int size) {
    String bin = Integer.toBinaryString(i);
    bin = String.format("%0" + size + "d", Integer.parseInt(bin));
    return bin;
}

}

5

Se stai utilizzando Eclipse Collections (precedentemente GS Collections ), puoi utilizzare il powerSet()metodo su tutti i SetIterables.

MutableSet<Integer> set = UnifiedSet.newSetWith(1, 2, 3);
System.out.println("powerSet = " + set.powerSet());
// prints: powerSet = [[], [1], [2], [1, 2], [3], [1, 3], [2, 3], [1, 2, 3]]

Nota: sono un committer per le collezioni Eclipse.


Puoi condividere e spiegare il codice della tua soluzione?
Konrad Höffner


4

Stavo cercando una soluzione che non fosse così vasta come quelle pubblicate qui. Questo è destinato a Java 7, quindi richiederà una manciata di paste per le versioni 5 e 6.

Set<Set<Object>> powerSetofNodes(Set<Object> orig) {
    Set<Set<Object>> powerSet = new HashSet<>(),
        runSet = new HashSet<>(),
        thisSet = new HashSet<>();

    while (powerSet.size() < (Math.pow(2, orig.size())-1)) {
        if (powerSet.isEmpty()) {
            for (Object o : orig) {
                Set<Object> s = new TreeSet<>();
                s.add(o);
                runSet.add(s);
                powerSet.add(s);
            }
            continue;
        }
        for (Object o : orig) {
            for (Set<Object> s : runSet) {
                Set<Object> s2 = new TreeSet<>();
                s2.addAll(s);
                s2.add(o);
                powerSet.add(s2);
                thisSet.add(s2);
            }
        }
        runSet.clear();
        runSet.addAll(thisSet);
        thisSet.clear();
    }
    powerSet.add(new TreeSet());
    return powerSet;

Ecco alcuni esempi di codice da testare:

Set<Object> hs = new HashSet<>();
hs.add(1);
hs.add(2);
hs.add(3);
hs.add(4);
for(Set<Object> s : powerSetofNodes(hs)) {
    System.out.println(Arrays.toString(s.toArray()));
}

PowerSetofNodes () non manca di una "}" alla fine?
Peter Mortensen,

3

Alcune delle soluzioni di cui sopra soffrono quando la dimensione del set è grande perché creano molti oggetti inutili da raccogliere e richiedono la copia dei dati. Come possiamo evitarlo? Possiamo trarre vantaggio dal fatto che sappiamo quanto sarà grande la dimensione del set di risultati (2 ^ n), preallocare un array così grande e aggiungerlo alla fine, senza mai copiare.

Lo speedup cresce velocemente con n. L'ho confrontato con la soluzione di João Silva sopra. Sulla mia macchina (tutte le misurazioni sono approssimative), n = 13 è 5 volte più veloce, n = 14 è 7x, n = 15 è 12x, n = 16 è 25x, n = 17 è 75x, n = 18 è 140x. In modo che la creazione / raccolta e la copia dei rifiuti dominino in quelle che altrimenti sembrano essere soluzioni simili.

La preallocazione dell'array all'inizio sembra essere una vittoria rispetto al lasciarlo crescere dinamicamente. Con n = 18, la crescita dinamica richiede complessivamente circa il doppio del tempo.

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

3

La seguente soluzione è stata presa in prestito dal mio libro " Coding Interviews: Questions, Analysis & Solutions ":

Vengono selezionati alcuni numeri interi in una matrice che compongono una combinazione. Viene utilizzato un insieme di bit, dove ogni bit rappresenta un numero intero nell'array. Se l' i-esimo carattere è selezionato per una combinazione, l' i-esimo bit è 1; in caso contrario, è 0. Ad esempio, tre bit vengono utilizzati per le combinazioni dell'array [1, 2, 3]. Se i primi due numeri interi 1 e 2 vengono selezionati per comporre una combinazione [1, 2], i bit corrispondenti sono {1, 1, 0}. Allo stesso modo, i bit corrispondenti a un'altra combinazione [1, 3] sono {1, 0, 1}. Siamo in grado di ottenere tutte le combinazioni di un array di lunghezza n se riusciamo a ottenere tutte le possibili combinazioni di n bit.

Un numero è composto da un insieme di bit. Tutte le possibili combinazioni di n bit corrispondono a numeri da 1 a 2 ^ n -1. Pertanto, ogni numero nell'intervallo tra 1 e 2 ^ n -1 corrisponde a una combinazione di un array con lunghezza n . Ad esempio, il numero 6 è composto dai bit {1, 1, 0}, quindi il primo e il secondo carattere vengono selezionati nella matrice [1, 2, 3] per generare la combinazione [1, 2]. Allo stesso modo, il numero 5 con i bit {1, 0, 1} corrisponde alla combinazione [1, 3].

Il codice Java per implementare questa soluzione è simile al seguente:

public static ArrayList<ArrayList<Integer>> powerSet(int[] numbers) {
    ArrayList<ArrayList<Integer>> combinations = new ArrayList<ArrayList<Integer>>(); 
    BitSet bits = new BitSet(numbers.length);
    do{
        combinations.add(getCombination(numbers, bits));
    }while(increment(bits, numbers.length));

    return combinations;
}

private static boolean increment(BitSet bits, int length) {
    int index = length - 1;

    while(index >= 0 && bits.get(index)) {
        bits.clear(index);
        --index;
    }

    if(index < 0)
        return false;

    bits.set(index);
    return true;
}

private static ArrayList<Integer> getCombination(int[] numbers, BitSet bits){
    ArrayList<Integer> combination = new ArrayList<Integer>();
    for(int i = 0; i < numbers.length; ++i) {
        if(bits.get(i))
            combination.add(numbers[i]);
    }

    return combination;
}

L'incremento del metodo aumenta un numero rappresentato in un insieme di bit. L'algoritmo cancella 1 bit dal bit più a destra finché non viene trovato uno 0 bit. Quindi imposta lo 0 bit più a destra su 1. Ad esempio, per aumentare il numero 5 con i bit {1, 0, 1}, cancella 1 bit dal lato destro e imposta lo 0 bit più a destra su 1. I bit diventano {1, 1, 0} per il numero 6, che è il risultato dell'aumento di 5 di 1.


Due cose che ho modificato: il looping in getCombination, invece di numbers.length (o bits.size ()), si può iterare su bits.length (), che accelera leggermente la generazione. Infine, ho ordinato i sottoinsiemi in base alla dimensione per il mio problema lo richiede.
BoLe

3

Ecco una semplice soluzione iterativa O (2 ^ n):

public static Set<Set<Integer>> powerSet(List<Integer> intList){

    Set<Set<Integer>> result = new HashSet();
    result.add(new HashSet());

    for (Integer i : intList){

        Set<Set<Integer>> temp = new HashSet();

        for(Set<Integer> intSet : result){

            intSet = new HashSet(intSet);
            intSet.add(i);                
            temp.add(intSet);
        }
        result.addAll(temp);
    }
    return result;
}

Questa soluzione utilizza anche lo spazio O (2 ^ n) che sarebbe troppo per grandi insiemi di input. È meglio seguire la definizione ricorsiva, utilizzando uno stack o una coda al posto della ricorsione.
rossb83

2
import java.util.Set;
import com.google.common.collect.*;

Set<Set<Integer>> sets = Sets.powerSet(ImmutableSet.of(1, 2, 3));

1

Se S è un insieme finito con N elementi, allora l'insieme di potenze di S contiene 2 ^ N elementi. Il tempo per enumerare semplicemente gli elementi del powerset è 2 ^ N, quindi O(2^N)è un limite inferiore alla complessità temporale della costruzione (avidamente) del powerset.

In parole povere, qualsiasi calcolo che implichi la creazione di gruppi di potenza non scalerà per valori elevati di N. Nessun algoritmo intelligente ti aiuterà ... a parte evitare la necessità di creare i gruppi di potenza!


1

Un modo senza ricorsione è il seguente: utilizzare una maschera binaria e creare tutte le combinazioni possibili.

public HashSet<HashSet> createPowerSet(Object[] array)
{
    HashSet<HashSet> powerSet=new HashSet();
    boolean[] mask= new boolean[array.length];

    for(int i=0;i<Math.pow(2, array.length);i++)
    {
        HashSet set=new HashSet();
        for(int j=0;j<mask.length;j++)
        {
            if(mask[i])
                set.add(array[j]);
        }
        powerSet.add(set);      

        increaseMask(mask);
    }

    return powerSet;
}

public void increaseMask(boolean[] mask)
{
    boolean carry=false;

    if(mask[0])
        {
            mask[0]=false;
            carry=true;
        }
    else
        mask[0]=true;

    for(int i=1;i<mask.length;i++)
    {
        if(mask[i]==true && carry==true)
        mask[i]=false;
        else if (mask[i]==false && carry==true)
        {
            mask[i]=true;
            carry=false;
        }
        else 
            break;

    }

}

1

Algoritmo:

Input: Set [], set_size 1. Ottieni la dimensione del power set powet_set_size = pow (2, set_size) 2 Ciclo per contatore da 0 a pow_set_size (a) Ciclo per i = 0 a set_size (i) Se il bit ith nel contatore è set Stampa iesimi elementi dall'insieme per questo sottoinsieme (b) Stampa separatore per sottoinsiemi, cioè nuova riga

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


1

Questa è la mia soluzione ricorsiva che può ottenere il set di alimentazione di qualsiasi set utilizzando Java Generics. La sua idea principale è combinare la testa dell'array di input con tutte le possibili soluzioni del resto dell'array come segue.

import java.util.LinkedHashSet;
import java.util.Set;

public class SetUtil {
    private static<T>  Set<Set<T>> combine(T head, Set<Set<T>> set) {
        Set<Set<T>> all = new LinkedHashSet<>();

        for (Set<T> currentSet : set) {
            Set<T> outputSet = new LinkedHashSet<>();

            outputSet.add(head);
            outputSet.addAll(currentSet);

            all.add(outputSet);
        }

        all.addAll(set);        

        return all;
    }

    //Assuming that T[] is an array with no repeated elements ...
    public static<T> Set<Set<T>> powerSet(T[] input) {
        if (input.length == 0) {
            Set <Set<T>>emptySet = new LinkedHashSet<>();

            emptySet.add(new LinkedHashSet<T>());

            return emptySet;
        }

        T head = input[0];
        T[] newInputSet = (T[]) new Object[input.length - 1];

        for (int i = 1; i < input.length; ++i) {
            newInputSet[i - 1] = input[i];
        }

        Set<Set<T>> all = combine(head, powerSet(newInputSet));

        return all;
    }

    public static void main(String[] args) {            
        Set<Set<Integer>> set = SetUtil.powerSet(new Integer[] {1, 2, 3, 4, 5, 6});

        System.out.println(set);
    }
}

Questo produrrà:

[[1, 2, 3, 4, 5, 6], [1, 2, 3, 4, 5], [1, 2, 3, 4, 6], [1, 2, 3, 4], [1, 2, 3, 5, 6], [1, 2, 3, 5], [1, 2, 3, 6], [1, 2, 3], [1, 2, 4, 5, 6], [1, 2, 4, 5], [1, 2, 4, 6], [1, 2, 4], [1, 2, 5, 6], [1, 2, 5], [1, 2, 6], [1, 2], [1, 3, 4, 5, 6], [1, 3, 4, 5], [1, 3, 4, 6], [1, 3, 4], [1, 3, 5, 6], [1, 3, 5], [1, 3, 6], [1, 3], [1, 4, 5, 6], [1, 4, 5], [1, 4, 6], [1, 4], [1, 5, 6], [1, 5], [1, 6], [1], [2, 3, 4, 5, 6], [2, 3, 4, 5], [2, 3, 4, 6], [2, 3, 4], [2, 3, 5, 6], [2, 3, 5], [2, 3, 6], [2, 3], [2, 4, 5, 6], [2, 4, 5], [2, 4, 6], [2, 4], [2, 5, 6], [2, 5], [2, 6], [2], [3, 4, 5, 6], [3, 4, 5], [3, 4, 6], [3, 4], [3, 5, 6], [3, 5], [3, 6], [3], [4, 5, 6], [4, 5], [4, 6], [4], [5, 6], [5], [6], []]

1

Un'altra implementazione di esempio:

 public static void main(String args[])
    {
        int[] arr = new int[]{1,2,3,4};
        // Assuming that number of sets are in integer range
        int totalSets = (int)Math.pow(2,arr.length);
        for(int i=0;i<totalSets;i++)
        {
            String binaryRep = Integer.toBinaryString(i);      
            for(int j=0;j<binaryRep.length();j++)
            {
                int index=binaryRep.length()-1-j;
                if(binaryRep.charAt(index)=='1')
                System.out.print(arr[j] +" ");       
            }
            System.out.println();
        }
    }

1

Questo è il mio approccio con le lambda.

public static <T> Set<Set<T>> powerSet(T[] set) {
      return IntStream
            .range(0, (int) Math.pow(2, set.length))
            .parallel() //performance improvement
            .mapToObj(e -> IntStream.range(0, set.length).filter(i -> (e & (0b1 << i)) != 0).mapToObj(i -> set[i]).collect(Collectors.toSet()))
            .map(Function.identity())
            .collect(Collectors.toSet());
        }

O in parallelo (vedi commento parallel ()):

Dimensioni del set di input: 18

Processori logici: da 8 a 3,4 GHz

Miglioramento delle prestazioni: 30%


1

Un sottoinsieme di t è qualsiasi insieme che può essere creato rimuovendo zero o più elementi di t. Il withoutFirst sottoinsieme aggiunge i sottoinsiemi di t a cui manca il primo elemento e il ciclo for si occuperà dell'aggiunta di sottoinsiemi con il primo elemento. Ad esempio, se t conteneva gli elementi ["1", "2", "3"], missingFirst aggiungerà [[""], ["2"], ["3"], ["2", "3 "]] e il ciclo for inserirà" 1 "davanti a questi elementi e lo aggiungerà a newSet. Quindi finiremo con [[""], ["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;
    }

Si prega di considerare l'aggiunta di una breve spiegazione lungo il codice fornito.
Mirza Sisic

Questo non si compila nemmeno, sembra essere scritto in qualche versione fantasy di Java. Setnon ha un getmetodo con un indice, né un subSetmetodo; 1stnon è un identificatore valido (suppongo lstfosse inteso). Cambia tutti i set in elenchi e quasi si compila ...
john16384

0
// input: S
// output: P
// S = [1,2]
// P = [], [1], [2], [1,2]

public static void main(String[] args) {
    String input = args[0];
    String[] S = input.split(",");
    String[] P = getPowerSet(S);
    if (P.length == Math.pow(2, S.length)) {
        for (String s : P) {
            System.out.print("[" + s + "],");
        }
    } else {
        System.out.println("Results are incorrect");
    }
}

private static String[] getPowerSet(String[] s) {
    if (s.length == 1) {
        return new String[] { "", s[0] };
    } else {
        String[] subP1 = getPowerSet(Arrays.copyOfRange(s, 1, s.length));
        String[] subP2 = new String[subP1.length];
        for (int i = 0; i < subP1.length; i++) {
            subP2[i] = s[0] + subP1[i];
        }
        String[] P = new String[subP1.length + subP2.length];
        System.arraycopy(subP1, 0, P, 0, subP1.length);
        System.arraycopy(subP2, 0, P, subP1.length, subP2.length);
        return P;
    }

}

Benvenuto in Stack Overflow. Potresti voler arricchire un po 'questa risposta con un testo che descriva cosa sta facendo e come risolve il problema del richiedente.
Lachlan Goodhew-Cook

0

Recentemente ho dovuto usare qualcosa di simile, ma prima avevo bisogno delle sottoliste più piccole (con 1 elemento, poi 2 elementi, ...). Non volevo includere il vuoto né l'intero elenco. Inoltre, non avevo bisogno di un elenco di tutte le sottoliste restituite, avevo solo bisogno di fare alcune cose con ciascuna.

Volevo farlo senza ricorsione, e ho trovato quanto segue (con le "cose ​​da fare" astratte in un'interfaccia funzionale):

@FunctionalInterface interface ListHandler<T> {
    void handle(List<T> list);
}


public static <T> void forAllSubLists(final List<T> list, ListHandler handler) {
    int     ll = list.size();   // Length of original list
    int     ci[] = new int[ll]; // Array for list indices
    List<T> sub = new ArrayList<>(ll);  // The sublist
    List<T> uml = Collections.unmodifiableList(sub);    // For passing to handler

    for (int gl = 1, gm; gl <= ll; gl++) {  // Subgroup length 1 .. n-1
        gm = 0; ci[0] = -1; sub.add(null);  // Some inits, and ensure sublist is at least gl items long

        do {
                ci[gm]++;                       // Get the next item for this member

                if (ci[gm] > ll - gl + gm) {    // Exhausted all possibilities for this position
                        gm--; continue;         // Continue with the next value for the previous member
                }

                sub.set(gm, list.get(ci[gm]));  // Set the corresponding member in the sublist

                if (gm == gl - 1) {             // Ok, a sublist with length gl
                        handler.handle(uml);    // Handle it
                } else {
                        ci[gm + 1] = ci[gm];    // Starting value for next member is this 
                        gm++;                   // Continue with the next member
                }
        } while (gm >= 0);  // Finished cycling through all possibilities
    }   // Next subgroup length
}

In questo modo, è anche facile limitarlo a sottoliste di lunghezze specifiche.


0
public class PowerSet {
    public static List<HashSet<Integer>> powerset(int[] a) {
        LinkedList<HashSet<Integer>> sets = new LinkedList<HashSet<Integer>>();
        int n = a.length;
        for (int i = 0; i < 1 << n; i++) {
            HashSet<Integer> set = new HashSet<Integer>();
            for (int j = 0; j < n; j++) {
                if ((1 << j & i) > 0)
                    set.add(a[j]);
            }
            sets.add(set);
        }
        return sets;
    }

    public static void main(String[] args) {
        List<HashSet<Integer>> sets = PowerSet.powerset(new int[]{ 1, 2, 3 });
        for (HashSet<Integer> set : sets) {
            for (int i : set)
                System.out.print(i);
            System.out.println();
        } 
    }
}

0

Ancora un'altra soluzione - con java8 + streaming api È pigro e ordinato, quindi restituisce i sottoinsiemi corretti quando viene utilizzato con "limit ()".

 public long bitRangeMin(int size, int bitCount){
    BitSet bs = new BitSet(size);
    bs.set(0, bitCount);
    return bs.toLongArray()[0];
}

public long bitRangeMax(int size, int bitCount){
    BitSet bs = BitSet.valueOf(new long[]{0});
    bs.set(size - bitCount, size);
    return bs.toLongArray()[0];
}

public <T> Stream<List<T>> powerSet(Collection<T> data)
{
    List<T> list = new LinkedHashSet<>(data).stream().collect(Collectors.toList());
    Stream<BitSet> head = LongStream.of(0).mapToObj( i -> BitSet.valueOf(new long[]{i}));
    Stream<BitSet> tail = IntStream.rangeClosed(1, list.size())
            .boxed()
            .flatMap( v1 -> LongStream.rangeClosed( bitRangeMin(list.size(), v1), bitRangeMax(list.size(), v1))
                    .mapToObj(v2 -> BitSet.valueOf(new long[]{v2}))
                    .filter( bs -> bs.cardinality() == v1));

    return Stream.concat(head, tail)
            .map( bs -> bs
                    .stream()
                    .mapToObj(list::get)
                    .collect(Collectors.toList()));
}

E il codice client lo è

@Test
public void testPowerSetOfGivenCollection(){
    List<Character> data = new LinkedList<>();
    for(char i = 'a'; i < 'a'+5; i++ ){
        data.add(i);
    }
    powerSet(data)
            .limit(9)
            .forEach(System.out::print);

}

/ * Stampa: [] [a] [b] [c] [d] [e] [a, b] [a, c] [b, c] * /


0

Potremmo scrivere il power set con o senza usare la ricorsione. Ecco un tentativo senza ricorsione:

public List<List<Integer>> getPowerSet(List<Integer> set) {
    List<List<Integer>> powerSet = new ArrayList<List<Integer>>();
    int max = 1 << set.size();
    for(int i=0; i < max; i++) {
        List<Integer> subSet = getSubSet(i, set);
        powerSet.add(subSet);
    }
    return powerSet;
}

private List<Integer> getSubSet(int p, List<Integer> set) {
    List<Integer> subSet = new ArrayList<Integer>();
    int position = 0;
    for(int i=p; i > 0; i >>= 1) {
        if((i & 1) == 1) {
            subSet.add(set.get(position));
        }
        position++;
    }
    return subSet;
}

0

Qui è per generare un set di alimentazione. L'idea è prima = S[0]e gli insiemi più piccoli sono S[1,...n].

Calcola tutti i sottoinsiemi di più piccoloSet e inseriscili in tutti i sottoinsiemi.

Per ogni sottoinsieme in tutti i sottoinsiemi, clonalo e aggiungilo prima al sottoinsieme.

ArrayList<ArrayList<Integer>> getSubsets(ArrayList<Integer> set, int index){
    ArrayList<ArrayList<Integer>> allsubsets;
    if(set.size() == index){
        allsubsets = new ArrayList<ArrayList<Integer>>();
        allsubsets.add(new ArrayList<Integer>()); // the empty set 
    }else{
        allsubsets = getSubsets(set, index+1);
        int item = set.get(index);

        ArrayList<ArrayList<Integer>> moresubsets = new ArrayList<ArrayList<Integer>>();

        for(ArrayList<Integer> subset: allsubsets){
            ArrayList<Integer> newsubset = new ArrayList<Integer>();

            newsubset.addAll(subset);
            newsubset.add(item);
            moresubsets.add(newsubset);

        }

        moresubsets.addAll(moresubsets);

    }

    return allsubsets;
}

0
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>();
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.