Obtention d'un ensemble de pouvoirs d'un ensemble en Java


86

La puissance de {1, 2, 3}est:

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

Disons que j'ai un Seten Java:

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

Comment écrire la fonction getPowerset, avec le meilleur ordre de complexité possible? (Je pense que ça pourrait être O (2 ^ n).)


7
Supposons que vous ayez un ensemble de configurations - disons «A», «B» et «C» -, qui peuvent être utilisées pour paramétrer un modèle, et que vous voulez voir quel sous-ensemble donne le meilleur résultat - par exemple simplement «A ". Une solution possible serait de tester chaque membre de l'ensemble de puissance.
João Silva

7
C'est une question d'entretien de Google pour les développeurs de logiciels. C'est un problème artificiel pour tester votre agilité d'esprit.
Eric Leschinski

C'est une question raisonnable. Par exemple, pour implémenter la fonction de notation pour cribbage, vous devez tester si un élément de l'ensemble de pouvoirs ajoute jusqu'à 15.
John Henckel

Réponses:


101

Oui, c'est en O(2^n)effet, puisque vous devez générer, eh bien, 2^ndes combinaisons possibles. Voici une implémentation fonctionnelle, utilisant des génériques et des ensembles:

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

Et un test, étant donné votre exemple d'entrée:

 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
Serait-il plus rapide d'utiliser Iterator au lieu d'utiliser list? Par exemple: Set <T> rest = new HashSet <T> (originalSet); Iterator <T> i = rest.iterator (); Tête T = i.next (); i.remove (); ?
Dimath

1
@CosminVacaroiu ... que pourrait-il faire d'autre?
user253751

3
Etes-vous sûr que c'est O(2^n)? C'est le nombre d'ensembles dans l'ensemble de puissance, mais chaque ensemble doit être créé en mémoire, ce qui prend au moins un temps proportionnel à la taille de l'ensemble. Selon wolfram alpha, c'est dans O(n * 2^n): wolfram alpha query
fabian

1
Cela fonctionnerait-il même si la taille de l'ensemble est de l'ordre de 10 ^ 5?
bane19

1
@GauravShankar 2 ^ 100 = 2 ^ (10 ^ 2) est déjà supérieur à 10 ^ 30. Vous ne serez pas témoin de la fin du calcul, quelle que soit la machine sur laquelle vous allez le calculer.
Karl Richter

31

En fait, j'ai écrit du code qui fait ce que vous demandez dans O (1). La question est de savoir ce que vous comptez faire ensuite avec l'ensemble. Si vous allez simplement l'appeler size(), c'est O (1), mais si vous allez l'itérer, c'est évidemment O(2^n).

contains()serait O(n), etc.

En avez-vous vraiment besoin?

ÉDITER:

Ce code est désormais disponible dans Guava , exposé via la méthode Sets.powerSet(set).


J'ai besoin d'itérer sur chaque sous
Manuel Araoz

Mais avez-vous besoin de stocker chaque sous-ensemble?
finlandais


Et si je veux juste les ensembles de puissance avec exactement k éléments? Votre code est-il efficace pour cela?
Eyal

Nouveau lien (Guava déplacé vers Github)
yiwei

12

Voici une solution où j'utilise un générateur, l'avantage étant que l'ensemble de la puissance n'est jamais stocké à la fois ... Vous pouvez donc le parcourir un par un sans qu'il soit nécessaire de le stocker en mémoire. J'aimerais penser que c'est une meilleure option ... Notez que la complexité est la même, O (2 ^ n), mais les besoins en mémoire sont réduits (en supposant que le ramasse-miettes se comporte!;))

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

}

Pour l'appeler, utilisez ce modèle:

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

C'est de ma bibliothèque Project Euler ... :)


Celui de Guava fonctionne un peu comme celui-ci, mais est limité à 32 éléments. Ce n'est pas déraisonnable car 2 ** 32 est probablement trop d'itérations. Il utilise encore moins de mémoire que la vôtre car il ne génère le AbstractSet qu'en cas de besoin. Essayez votre code contre celui de Guava où vous imprimez seulement 1 élément sur 10 000 et faites un grand exemple. Je parie que celui de Guava sera plus rapide.
Eyal

@Eyal, j'en suis sûr, je n'ai jamais prétendu le contraire. J'ai écrit ceci moi-même, ce n'est pas destiné au code de production. C'était un exercice d'algorithmes.
st0le

1
Remarque mineure: votre 'returnSet' est un TreeSet, ce qui nécessite que ses éléments soient comparables. Cela peut ne pas être le cas. Pensez à l'échanger contre un HashSet ou LinkedHashSet
Joris Kinable

10

Si n <63, ce qui est une hypothèse raisonnable puisque vous manqueriez de mémoire (à moins d'utiliser une implémentation d'itérateur) en essayant quand même de construire l'ensemble de puissance, c'est une manière plus concise de le faire. Les opérations binaires sont bien plus rapides que les Math.pow()tableaux et les masques, mais les utilisateurs de Java en ont peur ...

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 condition de terminaison dans une boucle for doit être i <(2 << n - 1) au lieu de i <(1 << n - 1).
bazeusz le

Merci @bazeusz, je l'ai changé ce i < (1 << n)qui est équivalent.
Andrew Mao

Étant donné que des opérations par bit sont utilisées, je pense ((i >> j) &1) == 1qu'au lieu de (i >> j) % 2 == 1 peuvent être utilisées. De plus, longsont signés, alors pensez-vous que la vérification du dépassement est logique?
Ravi Tiwari

9

Voici un tutoriel décrivant exactement ce que vous voulez, y compris le code. Vous avez raison en ce que la complexité est O (2 ^ n).


2
N'est-ce pas la complexité (n * 2 ^ n)? Parce que la chaîne binaire est de longueur n, et à chaque itération de la boucle principale, nous itérons toute la chaîne binaire.
Maggie

1
Le didacticiel est excellent MAIS j'ai utilisé cette technique pour résoudre le problème du HackerRank: il n'a réussi que la moitié des cas de test, et l'autre moitié a échoué en raison du délai d'expiration ou a causé une erreur d'exécution.
Eugenia Ozirna

7

J'ai trouvé une autre solution basée sur les idées de @Harry He. Probablement pas le plus élégant mais voici ce que je comprends:

Prenons l'exemple classique simple PowerSet de SP (S) = {{1}, {2}, {3}}. Nous savons que la formule pour obtenir le nombre de sous-ensembles est 2 ^ n (7 + ensemble vide). Pour cet exemple 2 ^ 3 = 8 sous-ensembles.

Afin de trouver chaque sous-ensemble, nous devons convertir 0-7 décimal en représentation binaire indiquée dans le tableau de conversion ci-dessous:

ConversionTable

Si nous parcourons la table ligne par ligne, chaque ligne se traduira par un sous-ensemble et les valeurs de chaque sous-ensemble proviendront des bits activés.

Chaque colonne de la section Bin Value correspond à la position d'index dans l'ensemble d'entrée d'origine.

Voici mon code:

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

Si vous utilisez des collections Eclipse (anciennement GS Collections ), vous pouvez utiliser la powerSet()méthode sur tous les 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]]

Remarque: je suis un committer pour les collections Eclipse.


Pouvez-vous partager et expliquer le code de votre solution?
Konrad Höffner


4

Je cherchais une solution qui ne soit pas aussi énorme que celles publiées ici. Cela cible Java 7, il faudra donc une poignée de pâtes pour les versions 5 et 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;

Voici un exemple de code à tester:

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 () ne manque-t-il pas un "}" à la fin?
Peter Mortensen

3

Certaines des solutions ci-dessus souffrent lorsque la taille de l'ensemble est importante car elles créent beaucoup de déchets d'objets à collecter et nécessitent la copie de données. Comment pouvons-nous éviter cela? Nous pouvons profiter du fait que nous savons quelle sera la taille du jeu de résultats (2 ^ n), préallouer un tableau de cette taille et simplement l'ajouter à la fin, sans jamais le copier.

L'accélération augmente rapidement avec n. Je l'ai comparé à la solution de João Silva ci-dessus. Sur ma machine (toutes les mesures sont approximatives), n = 13 est 5x plus rapide, n = 14 est 7x, n = 15 est 12x, n = 16 est 25x, n = 17 est 75x, n = 18 est 140x. Ainsi, la création / collecte de déchets et la copie dominent dans ce qui semble par ailleurs être des solutions big-O similaires.

Préallouer la baie au début semble être une victoire par rapport à la laisser croître dynamiquement. Avec n = 18, la croissance dynamique prend environ deux fois plus de temps dans l'ensemble.

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 solution suivante est empruntée à mon livre " Coding Interviews: Questions, Analysis & Solutions ":

Certains entiers dans un tableau sont sélectionnés qui composent une combinaison. Un ensemble de bits est utilisé, où chaque bit représente un entier dans le tableau. Si le i-ème caractère est sélectionné pour une combinaison, le i-ème bit est 1; sinon, il vaut 0. Par exemple, trois bits sont utilisés pour les combinaisons du tableau [1, 2, 3]. Si les deux premiers entiers 1 et 2 sont sélectionnés pour composer une combinaison [1, 2], les bits correspondants sont {1, 1, 0}. De même, les bits correspondant à une autre combinaison [1, 3] sont {1, 0, 1}. Nous pouvons obtenir toutes les combinaisons d'un tableau de longueur n si nous pouvons obtenir toutes les combinaisons possibles de n bits.

Un nombre est composé d'un ensemble de bits. Toutes les combinaisons possibles de n bits correspondent à des nombres de 1 à 2 ^ n -1. Par conséquent, chaque nombre compris entre 1 et 2 ^ n -1 correspond à une combinaison d'un tableau de longueur n . Par exemple, le nombre 6 est composé de bits {1, 1, 0}, donc les premier et deuxième caractères sont sélectionnés dans le tableau [1, 2, 3] pour générer la combinaison [1, 2]. De même, le nombre 5 avec les bits {1, 0, 1} correspond à la combinaison [1, 3].

Le code Java pour implémenter cette solution ressemble à ci-dessous:

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'incrément de méthode augmente un nombre représenté dans un ensemble de bits. L'algorithme efface 1 bit du bit le plus à droite jusqu'à ce qu'un bit 0 soit trouvé. Il définit ensuite le bit 0 le plus à droite sur 1. Par exemple, pour augmenter le nombre 5 avec les bits {1, 0, 1}, il efface 1 bit du côté droit et définit le bit le plus à droite sur 1. Les bits deviennent {1, 1, 0} pour le nombre 6, qui est le résultat d'une augmentation de 5 par 1.


Deux choses que j'ai modifiées: faire une boucle dans getCombination, au lieu de numbers.length (ou bits.size ()), on peut itérer vers bits.length (), ce qui accélère légèrement la génération. Enfin, j'ai trié les sous-ensembles par taille car mon problème l'exige.
BoLe

3

Voici une solution itérative simple en 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;
}

Cette solution utilise également l'espace O (2 ^ n) qui serait trop pour les grands ensembles d'entrées. Il vaut mieux suivre la définition récursive, en utilisant une pile ou une file d'attente à la place de la récursivité.
rossb83

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

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

1

Si S est un ensemble fini avec N éléments, alors l'ensemble de puissance de S contient 2 ^ N éléments. Le temps pour énumérer simplement les éléments de l'ensemble de puissance est 2 ^ N, doncO(2^N) est de même d'une limite inférieure sur la complexité temporelle de la construction (avec empressement) de l'ensemble de pouvoirs.

En termes simples, tout calcul impliquant la création d'ensembles de puissance ne sera pas mis à l'échelle pour de grandes valeurs de N. Aucun algorithme intelligent ne vous aidera ... à part éviter d'avoir à créer les ensembles de puissance!


1

Une façon sans récursivité est la suivante: Utilisez un masque binaire et faites toutes les combinaisons possibles.

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

Algorithme:

Entrée: Set [], set_size 1. Obtenir la taille du jeu de puissance powet_set_size = pow (2, set_size) 2 Boucle pour le compteur de 0 à pow_set_size (a) Boucle pour i = 0 à set_size (i) Si ith bit du compteur est set Imprimer l'élément ith de l'ensemble pour ce sous-ensemble (b) Séparateur d'impression pour les sous-ensembles, c.-à-d. nouvelle ligne

#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

C'est ma solution récursive qui peut obtenir l'ensemble de puissance de n'importe quel ensemble en utilisant Java Generics. Son idée principale est de combiner la tête du tableau d'entrée avec toutes les solutions possibles du reste du tableau comme suit.

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

Cela produira:

[[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 autre exemple d'implémentation:

 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

C'est mon approche avec les lambdas.

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

Ou en parallèle (voir commentaire parallel ()):

Taille du jeu d'entrées: 18

Processeurs logiques: 8 à 3,4 GHz

Amélioration des performances: 30%


1

Un sous-ensemble de t est tout ensemble qui peut être créé en supprimant zéro ou plusieurs éléments de t. Le sous-ensemble withoutFirst ajoute les sous-ensembles de t qui n'ont pas le premier élément et la boucle for se chargera d'ajouter des sous-ensembles avec le premier élément. Par exemple, si t contenait les éléments ["1", "2", "3"], missingFirst ajoutera [[""], ["2"], ["3"], ["2", "3 "]] et la boucle for collera le" 1 "devant ces éléments et l'ajoutera au newSet. Nous allons donc finir avec [[""], ["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;
    }

Veuillez envisager d'ajouter une courte explication au code que vous avez fourni.
Mirza Sisic

Cela ne compile même pas, semble être écrit dans une version Java fantastique. Setn'a pas de getméthode avec un index, ni de subSetméthode; 1stn'est pas un identifiant valide (je suppose que lstc'était voulu). Changer tous les ensembles en listes et il compile presque ...
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;
    }

}

Bienvenue dans Stack Overflow. Vous voudrez peut-être étoffer un peu cette réponse avec un texte décrivant ce qu'elle fait et comment cela résout le problème du poseur de question.
Lachlan Goodhew-Cook

0

J'ai récemment dû utiliser quelque chose comme ça, mais j'avais d'abord besoin des plus petites sous-listes (avec 1 élément, puis 2 éléments, ...). Je ne voulais pas inclure la liste vide ni la liste entière. De plus, je n'avais pas besoin d'une liste de toutes les sous-listes renvoyées, j'avais juste besoin de faire des choses avec chacune.

Je voulais faire cela sans récursivité, et est venu avec ce qui suit (avec le "faire des choses" abstrait dans une interface fonctionnelle):

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

De cette manière, il est également facile de le limiter à des sous-listes de longueurs spécifiques.


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

Encore une autre solution - avec java8 + streaming api Il est paresseux et ordonné donc il retourne des sous-ensembles corrects quand il est utilisé avec "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()));
}

Et le code client est

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

}

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


0

Nous pourrions écrire l'ensemble de puissance avec ou sans utiliser la récursivité. Voici une tentative sans récursivité:

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

Voici pour générer un ensemble de puissance. L'idée est d'abord = S[0]et les petits ensemblesS[1,...n] .

Calculez tous les sous-ensembles de smallSet et placez-les dans tous les sous-ensembles.

Pour chaque sous-ensemble de tous les sous-ensembles, clonez-le et ajoutez-le d'abord au sous-ensemble.

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>();
}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.