Obtener un conjunto de potencias de un conjunto en Java


86

El poder de {1, 2, 3}es:

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

Digamos que tengo un Seten Java:

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

¿Cómo escribo la función getPowerset, con el mejor orden de complejidad posible? (Creo que podría ser O (2 ^ n).)


7
Suponga que tiene un conjunto de configuraciones, digamos "A", "B" y "C", que se pueden usar para parametrizar un modelo y desea ver qué subconjunto produce el mejor resultado, por ejemplo, solo "A" ". Una posible solución sería probar cada miembro del conjunto de potencias.
João Silva

7
Es una pregunta de entrevista de Google para desarrolladores de software. Es un problema inventado para poner a prueba tu agilidad mental.
Eric Leschinski

Ésta es una pregunta razonable. Por ejemplo, para implementar la función de puntuación para cribbage, debe probar si algún elemento del conjunto de potencia suma 15.
John Henckel

Respuestas:


101

Sí, de hecho lo es O(2^n), ya que necesitas generar, bueno, 2^ncombinaciones posibles. Aquí hay una implementación funcional, usando genéricos y conjuntos:

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

Y una prueba, dada su entrada de ejemplo:

 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
¿Sería más rápido usar Iterator en lugar de usar list? Por ejemplo: Set <T> rest = new HashSet <T> (originalSet); Iterador <T> i = rest.iterator (); T cabeza = i.siguiente (); yo remuevo(); ?
Dimath

1
@CosminVacaroiu ... ¿qué más podría hacer?
user253751

3
¿Estás seguro de que lo es O(2^n)? Ese es el número de conjuntos en el conjunto de energía, pero cada conjunto debe crearse en la memoria, lo que lleva un tiempo proporcional al tamaño del conjunto al menos. De acuerdo con wolfram alpha, está en O(n * 2^n): consulta wolfram alpha
fabian

1
¿Funcionaría esto incluso si el tamaño del conjunto es del orden de 10 ^ 5?
bane19

1
@GauravShankar 2 ^ 100 = 2 ^ (10 ^ 2) ya es mayor que 10 ^ 30. No será testigo del final del cálculo sin importar en qué máquina de turing lo vaya a calcular.
Karl Richter

31

De hecho, he escrito un código que hace lo que pide en O (1). La pregunta es qué planea hacer con el Set a continuación. Si solo lo va a llamar size(), eso es O (1), pero si lo va a iterar, obviamente O(2^n).

contains()sería O(n), etc.

¿Realmente necesitas esto?

EDITAR:

Este código ahora está disponible en Guava , expuesto a través del método Sets.powerSet(set).


Necesito iterar sobre cada subconjunto
Manuel Araoz

¿Pero necesitas almacenar todos los subconjuntos?
finnw


¿Qué pasa si solo quiero los conjuntos de potencia con exactamente k elementos? ¿Es tu código eficiente para eso?
Eyal

Nuevo enlace (Guava se mudó a Github)
yiwei

12

Aquí hay una solución en la que uso un generador, con la ventaja de que el conjunto de energía completo nunca se almacena a la vez ... Por lo tanto, puede iterar uno por uno sin necesidad de almacenarlo en la memoria. Me gustaría pensar que es una mejor opción ... Tenga en cuenta que la complejidad es la misma, O (2 ^ n), pero los requisitos de memoria se reducen (¡asumiendo que el recolector de basura se comporta!;))

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

}

Para llamarlo, use este patrón:

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

Es de mi biblioteca Project Euler ... :)


El guayaba funciona de manera muy similar a este, pero está restringido a 32 elementos. Eso no es irrazonable porque 2 ** 32 es probablemente demasiadas iteraciones. Utiliza incluso menos memoria que la suya porque genera el AbstractSet solo cuando es necesario. Pruebe su código con el de Guava en el que imprime solo 1 de cada 10,000 elementos y dé un gran ejemplo. Apuesto a que el de Guayaba será más rápido.
Eyal

@Eyal, estoy seguro de que sí, nunca dije lo contrario. Escribí esto yo mismo, no está destinado a código de producción. Fue un ejercicio de algoritmos.
st0le

1
Comentario menor: su 'returnSet' es un TreeSet, que requiere que sus elementos sean comparables. Puede que este no sea el caso. Considere cambiarlo por un HashSet o LinkedHashSet
Joris Kinable

10

Si n <63, que es una suposición razonable ya que se quedaría sin memoria (a menos que use una implementación de iterador) tratando de construir el conjunto de potencia de todos modos, esta es una forma más concisa de hacerlo. Las operaciones binarias son mucho más rápidas que las Math.pow()matrices para máscaras, pero de alguna manera los usuarios de Java les tienen miedo ...

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 condición de terminación en un bucle for debería ser i <(2 << n - 1) en lugar de i <(1 << n - 1).
bazeusz

Gracias @bazeusz, lo cambié a lo i < (1 << n)que es equivalente.
Andrew Mao

Dado que se utilizan operaciones de bits, creo que en ((i >> j) &1) == 1lugar de (i >> j) % 2 == 1 se pueden utilizar. Además, longestán firmados, así que, ¿cree que tiene sentido verificar el desbordamiento?
Ravi Tiwari

9

Aquí hay un tutorial que describe exactamente lo que desea, incluido el código. Tiene razón en que la complejidad es O (2 ^ n).


2
¿No es complejidad (n * 2 ^ n)? Debido a que la cadena binaria tiene una longitud n, y en cada iteración del bucle principal iteramos toda la cadena binaria.
Maggie

1
El tutorial es genial, PERO he usado esta técnica para resolver el problema de HackerRank: pasó solo la mitad de los casos de prueba y la otra mitad falló debido al tiempo de espera o provocó un error de tiempo de ejecución.
Eugenia Ozirna

7

Se me ocurrió otra solución basada en las ideas de @Harry He. Probablemente no sea el más elegante, pero aquí va como lo entiendo:

Tomemos el clásico ejemplo simple PowerSet de SP (S) = {{1}, {2}, {3}}. Sabemos que la fórmula para obtener el número de subconjuntos es 2 ^ n (7 + conjunto vacío). Para este ejemplo 2 ^ 3 = 8 subconjuntos.

Para encontrar cada subconjunto, necesitamos convertir 0-7 decimal a la representación binaria que se muestra en la tabla de conversión a continuación:

Tabla de conversión

Si recorremos la tabla fila por fila, cada fila resultará en un subconjunto y los valores de cada subconjunto provendrán de los bits habilitados.

Cada columna de la sección Valor de ubicación corresponde a la posición del índice en el conjunto de entrada original.

Aquí mi código:

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 está usando Eclipse Collections (anteriormente GS Collections ), puede usar el powerSet()método en todos los 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: Soy un comprometido con Eclipse Collections.


¿Puedes compartir y explicar el código de tu solución?
Konrad Höffner


4

Estaba buscando una solución que no fuera tan grande como las publicadas aquí. Esto apunta a Java 7, por lo que requerirá un puñado de pastas para las versiones 5 y 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;

Aquí hay un código de ejemplo para probar:

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

¿No falta un "}" en powerSetofNodes () al final?
Peter Mortensen

3

Algunas de las soluciones anteriores sufren cuando el tamaño del conjunto es grande porque están creando una gran cantidad de basura de objetos para recolectar y requieren copiar datos. ¿Cómo podemos evitar eso? Podemos aprovechar el hecho de que sabemos qué tan grande será el tamaño del conjunto de resultados (2 ^ n), preasignar una matriz tan grande y simplemente agregarla al final, sin copiar nunca.

La aceleración crece rápidamente con n. Lo comparé con la solución de João Silva anterior. En mi máquina (todas las medidas son aproximadas), n = 13 es 5 veces más rápido, n = 14 es 7x, n = 15 es 12x, n = 16 es 25x, n = 17 es 75x, n = 18 es 140x. De modo que la creación / recolección y copia de basura está dominando lo que de otra manera parecen ser soluciones similares de big-O.

Preasignar la matriz al principio parece ser una ventaja en comparación con dejarla crecer dinámicamente. Con n = 18, el crecimiento dinámico toma aproximadamente el doble de tiempo en general.

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 siguiente solución está tomada de mi libro " Codificación de entrevistas: preguntas, análisis y soluciones ":

Se seleccionan algunos números enteros en una matriz que componen una combinación. Se utiliza un conjunto de bits, donde cada bit representa un número entero en la matriz. Si se selecciona el carácter i-ésimo para una combinación, el bit i-ésimo es 1; de lo contrario, es 0. Por ejemplo, se utilizan tres bits para combinaciones de la matriz [1, 2, 3]. Si los dos primeros enteros 1 y 2 se seleccionan para componer una combinación [1, 2], los bits correspondientes son {1, 1, 0}. De manera similar, los bits correspondientes a otra combinación [1, 3] son ​​{1, 0, 1}. Podemos obtener todas las combinaciones de una matriz con longitud n si podemos obtener todas las combinaciones posibles de n bits.

Un número está compuesto por un conjunto de bits. Todas las combinaciones posibles de n bits corresponden a números del 1 al 2 ^ n -1. Por lo tanto, cada número en el rango entre 1 y 2 ^ n -1 corresponde a una combinación de una matriz con longitud n . Por ejemplo, el número 6 está compuesto por bits {1, 1, 0}, por lo que el primer y segundo caracteres se seleccionan en la matriz [1, 2, 3] para generar la combinación [1, 2]. De manera similar, el número 5 con bits {1, 0, 1} corresponde a la combinación [1, 3].

El código Java para implementar esta solución se ve a continuación:

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

El incremento del método aumenta un número representado en un conjunto de bits. El algoritmo borra 1 bit del bit más a la derecha hasta que se encuentra un bit 0. Luego establece el bit 0 más a la derecha en 1. Por ejemplo, para aumentar el número 5 con los bits {1, 0, 1}, borra 1 bits del lado derecho y establece el bit 0 más a la derecha en 1. Los bits se vuelven {1, 1, 0} para el número 6, que es el resultado de aumentar 5 en 1.


Modifiqué dos cosas: bucle en getCombination, en lugar de numbers.length (o bits.size ()), se puede iterar a bits.length (), lo que acelera ligeramente la generación. Finalmente, clasifiqué los subconjuntos por tamaño porque mi problema lo requiere.
BoLe

3

Aquí hay una solución iterativa O (2 ^ n) fácil:

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

Esta solución también usa espacio O (2 ^ n), que sería demasiado para conjuntos de entrada grandes. Es mejor seguir la definición recursiva, usando una pila o una cola en lugar de la recursividad.
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 es un conjunto finito con N elementos, entonces el conjunto de potencias de S contiene 2 ^ N elementos. El tiempo para simplemente enumerar los elementos del conjunto de poderes es 2 ^ N, por lo que O(2^N)es un límite inferior en la complejidad temporal de (con entusiasmo) construir el conjunto de poderes.

En pocas palabras, cualquier cálculo que implique la creación de conjuntos de potencias no se escalará para valores grandes de N. Ningún algoritmo inteligente lo ayudará ... ¡aparte de evitar la necesidad de crear los conjuntos de potencias!


1

Una forma sin recursividad es la siguiente: Utilice una máscara binaria y realice todas las combinaciones posibles.

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:

Entrada: Set [], set_size 1. Obtenga el tamaño de potencia set powet_set_size = pow (2, set_size) 2 Bucle para contador de 0 a pow_set_size (a) Bucle para i = 0 a set_size (i) Si el bit en el contador es establecer Imprimir el elemento del conjunto para este subconjunto (b) Separador de impresión para subconjuntos, es decir, nueva línea

#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

Esta es mi solución recursiva que puede obtener el conjunto de potencia de cualquier conjunto usando Java Generics. Su idea principal es combinar el encabezado de la matriz de entrada con todas las posibles soluciones del resto de la matriz de la siguiente manera.

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

Esto dará como resultado:

[[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

Otra implementación de muestra:

 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

Este es mi enfoque con 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());
        }

O en paralelo (ver comentario paralelo ()):

Tamaño del conjunto de entrada: 18

Procesadores lógicos: 8 a 3,4 GHz

Mejora del rendimiento: 30%


1

Un subconjunto de t es cualquier conjunto que se puede hacer eliminando cero o más elementos de t. El subconjunto withoutFirst agrega los subconjuntos de t a los que les falta el primer elemento y el bucle for se ocupará de agregar subconjuntos con el primer elemento. Por ejemplo, si t contenía los elementos ["1", "2", "3"], missingFirst agregará [[""], ["2"], ["3"], ["2", "3 "]] y el bucle for pegará el" 1 "delante de estos elementos y lo agregará al newSet. Así que terminaremos 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;
    }

Considere agregar una breve explicación junto con el código que proporcionó.
Mirza Sisic

Esto ni siquiera se compila, parece estar escrito en alguna versión de fantasía de Java. Setno tiene un getmétodo con un índice, ni un subSetmétodo; 1stno es un identificador válido (supongo que lstse refería). Cambie todos los conjuntos a listas y casi se 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;
    }

}

Bienvenido a Stack Overflow. Es posible que desee desarrollar un poco esta respuesta con un texto que describa lo que está haciendo y cómo resuelve el problema del autor de la pregunta.
Lachlan Goodhew-Cook

0

Recientemente tuve que usar algo como esto, pero primero necesitaba las sublistas más pequeñas (con 1 elemento, luego 2 elementos, ...). No quería incluir la lista vacía ni completa. Además, no necesitaba una lista de todas las sublistas devueltas, solo necesitaba hacer algunas cosas con cada una.

Quería hacer esto sin recursividad y se me ocurrió lo siguiente (con el "hacer cosas" resumido en una interfaz funcional):

@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 esta forma, también es fácil limitarlo a sublistas de longitudes específicas.


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

Otra solución más, con java8 + streaming api. Es perezoso y ordenado, por lo que devuelve los subconjuntos correctos cuando se usa 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()));
}

Y el código de cliente es

@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

Podríamos escribir el conjunto de potencia con o sin recursividad. Aquí hay un intento sin recursividad:

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

Aquí está para generar un conjunto de energía. La idea es primero = S[0]y conjuntos más pequeños S[1,...n].

Calcule todos los subconjuntos de SmallSet y colóquelos en todos los subconjuntos.

Para cada subconjunto en todos los subconjuntos, clónelo y agréguelo primero al subconjunto.

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>();
}
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.