Obtenção de um conjunto de poderes de um conjunto em Java


86

O conjunto de poderes {1, 2, 3}é:

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

Digamos que eu tenha um Setem Java:

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

Como escrevo a função getPowerset, com a melhor ordem de complexidade possível? (Eu acho que pode ser O (2 ^ n).)


7
Suponha que você tenha um conjunto de configurações - digamos "A", "B" e "C" - que pode ser usado para parametrizar um modelo e deseja ver qual subconjunto produz o melhor resultado - por exemplo, apenas "A " Uma possível solução seria testar cada membro do conjunto de poderes.
João Silva

7
É uma pergunta da entrevista do Google para desenvolvedores de software. É um problema inventado para testar sua agilidade mental.
Eric Leschinski

Esta é uma pergunta razoável. Por exemplo, para implementar a função de pontuação para cribbage, você tem que testar se algum elemento do conjunto de poderes soma 15.
John Henckel

Respostas:


101

Sim, é O(2^n)verdade, já que você precisa gerar, bem, 2^ncombinações possíveis. Aqui está uma implementação funcional, usando genéricos e 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;
}  

E um teste, dado seu exemplo de entrada:

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

Seria mais rápido usar o Iterator em vez de usar a lista? Ex: Definir <T> rest = new HashSet <T> (originalSet); Iterator <T> i = rest.iterator (); Cabeça T = i.next (); eu removo(); ?
Dia

1
@CosminVacaroiu ... o que mais poderia fazer?
user253751

3
Tem certeza que sim O(2^n)? Esse é o número de conjuntos no conjunto de potência, mas cada conjunto deve ser criado na memória, o que leva pelo menos um tempo proporcional ao tamanho do conjunto. De acordo com o wolfram alpha, está em O(n * 2^n): consulta do wolfram alpha
fabian

1
Isso funcionaria mesmo se o tamanho do conjunto fosse da ordem de 10 ^ 5?
bane19

1
@GauravShankar 2 ^ 100 = 2 ^ (10 ^ 2) já é maior que 10 ^ 30. Você não testemunhará a conclusão do cálculo em qualquer máquina de rotação que irá calculá-lo.
Karl Richter

31

Na verdade, escrevi um código que faz o que você está pedindo em O (1). A questão é o que você planeja fazer com o Conjunto a seguir. Se você for apenas chamá size()-lo, será O (1), mas se for iterar, é óbvio O(2^n).

contains()seria O(n), etc.

Você realmente precisa disso?

EDITAR:

Este código agora está disponível no Guava , exposto por meio do método Sets.powerSet(set).


Preciso iterar sobre cada subconjunto
Manuel Araoz

Mas você precisa armazenar cada subconjunto?
finnw


E se eu quiser apenas os conjuntos de poderes com exatamente k elementos? Seu código é eficiente para isso?
Eyal

Novo link (Guava mudou para Github)
yiwei,

12

Aqui está uma solução onde eu uso um gerador, com a vantagem de que o conjunto de energia inteiro nunca é armazenado de uma vez ... Assim, você pode iterar um por um sem precisar que seja armazenado na memória. Eu gostaria de pensar que é uma opção melhor ... Observe que a complexidade é a mesma, O (2 ^ n), mas os requisitos de memória são reduzidos (assumindo que o coletor de lixo 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;
    }

}

Para chamá-lo, use este padrão:

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

É da minha Biblioteca do Projeto Euler ... :)


A Goiaba funciona de forma muito parecida com esta, mas está restrita a 32 elementos. Isso não é irracional porque 2 ** 32 é provavelmente muitas iterações. Ele usa ainda menos memória do que o seu porque gera o AbstractSet apenas quando necessário. Tente seu código contra o Guava, onde você imprime apenas 1 em 10.000 elementos, e dá um grande exemplo. Aposto que a Goiaba vai ser mais rápida.
Eyal

@Eyal, tenho certeza que sim, nunca afirmei o contrário. Eu mesmo escrevi, não se destina a código de produção. Foi um exercício de algoritmos.
st0le

1
Observação secundária: seu 'returnSet' é um TreeSet, o que requer que seus itens sejam comparáveis. Pode não ser o caso. Considere trocá-lo por um HashSet ou LinkedHashSet
Joris Kinable

10

Se n <63, o que é uma suposição razoável, já que você ficaria sem memória (a menos que esteja usando uma implementação de iterador) tentando construir o conjunto de potência de qualquer maneira, esta é uma maneira mais concisa de fazer isso. As operações binárias são muito mais rápidas do que Math.pow()arrays para máscaras, mas de alguma forma os usuários Java têm medo delas ...

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;

A condição de término em um loop for deve ser i <(2 << n - 1) em vez de i <(1 << n - 1).
bazeusz

Obrigado @bazeusz, mudei para i < (1 << n)que é equivalente.
Andrew Mao,

9

Aqui está um tutorial que descreve exatamente o que você deseja, incluindo o código. Você está correto em que a complexidade é O (2 ^ n).


2
Não é complexidade (n * 2 ^ n)? Como a string binária tem comprimento n, e em cada iteração do loop principal, iteramos toda a string binária.
Maggie

1
O tutorial é ótimo, MAS eu usei essa técnica para resolver o problema do HackerRank: ele passou apenas na metade dos casos de teste e a outra metade falhou devido ao tempo limite ou causou um erro de tempo de execução.
Eugenia Ozirna

7

Eu encontrei outra solução baseada nas ideias de @Harry He. Provavelmente não é o mais elegante, mas aqui vai como eu o entendo:

Vamos pegar o exemplo clássico simples PowerSet de SP (S) = {{1}, {2}, {3}}. Sabemos que a fórmula para obter o número de subconjuntos é 2 ^ n (7 + conjunto vazio). Para este exemplo, 2 ^ 3 = 8 subconjuntos.

Para encontrar cada subconjunto, precisamos converter 0-7 decimal em representação binária mostrada na tabela de conversão abaixo:

Tabela de conversão

Se percorrermos a tabela linha por linha, cada linha resultará em um subconjunto e os valores de cada subconjunto virão dos bits habilitados.

Cada coluna na seção Bin Value corresponde à posição do índice no conjunto de entrada original.

Aqui está meu 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

Se você estiver usando Eclipse Collections (anteriormente GS Collections ), você pode usar o powerSet()método em todos os 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: Eu sou um committer para Eclipse Collections.


Você pode compartilhar e explicar o código da sua solução?
Konrad Höffner


4

Eu estava procurando uma solução que não fosse tão grande quanto as postadas aqui. Isso é voltado para o Java 7, portanto, exigirá um punhado de pastas para as versões 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;

Aqui está um exemplo de código para testar:

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

Não está faltando em powerSetofNodes () um "}" no final?
Peter Mortensen

3

Algumas das soluções acima sofrem quando o tamanho do conjunto é grande, porque elas estão criando uma grande quantidade de lixo de objetos a ser coletado e exigem a cópia de dados. Como podemos evitar isso? Podemos tirar vantagem do fato de que sabemos quão grande será o tamanho do conjunto de resultados (2 ^ n), pré-alocar um array desse tamanho e apenas anexar ao final dele, nunca copiando.

O aumento de velocidade cresce rapidamente com n. Eu comparei com a solução do João Silva acima. Na minha máquina (todas as medições são aproximadas), n = 13 é 5x mais rápido, n = 14 é 7x, n = 15 é 12x, n = 16 é 25x, n = 17 é 75x, n = 18 é 140x. Assim, a criação / coleta e cópia de lixo está dominando o que de outra forma parecem ser soluções big-O semelhantes.

Pré-alocar a matriz no início parece ser uma vitória em comparação com deixá-la crescer dinamicamente. Com n = 18, o crescimento dinâmico leva cerca de duas vezes mais tempo no geral.

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

A solução a seguir foi emprestada do meu livro " Entrevistas de codificação: perguntas, análises e soluções ":

Alguns inteiros em uma matriz são selecionados para compor uma combinação. Um conjunto de bits é utilizado, onde cada bit representa um inteiro na matriz. Se o i-ésimo caractere for selecionado para uma combinação, o i-ésimo bit será 1; caso contrário, é 0. Por exemplo, três bits são usados ​​para combinações da matriz [1, 2, 3]. Se os dois primeiros inteiros 1 e 2 são selecionados para compor uma combinação [1, 2], os bits correspondentes são {1, 1, 0}. Da mesma forma, os bits correspondentes a outra combinação [1, 3] são {1, 0, 1}. Somos capazes de obter todas as combinações de uma matriz com comprimento n se pudermos obter todas as combinações possíveis de n bits.

Um número é composto de um conjunto de bits. Todas as combinações possíveis de n bits correspondem a números de 1 a 2 ^ n -1. Portanto, cada número no intervalo entre 1 e 2 ^ n -1 corresponde a uma combinação de uma matriz com comprimento n . Por exemplo, o número 6 é composto de bits {1, 1, 0}, então o primeiro e o segundo caracteres são selecionados na matriz [1, 2, 3] para gerar a combinação [1, 2]. Da mesma forma, o número 5 com bits {1, 0, 1} corresponde à combinação [1, 3].

O código Java para implementar esta solução é o seguinte:

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

O incremento do método aumenta um número representado em um conjunto de bits. O algoritmo limpa 1 bit do bit mais à direita até que um bit 0 seja encontrado. Em seguida, ele define o bit 0 mais à direita como 1. Por exemplo, para aumentar o número 5 com os bits {1, 0, 1}, ele limpa 1 bit do lado direito e define o bit 0 mais à direita como 1. Os bits se tornam {1, 1, 0} para o número 6, que é o resultado do aumento de 5 em 1.


Duas coisas eu modifiquei: loop em getCombination, em vez de numbers.length (ou bits.size ()), pode-se iterar para bits.length (), o que acelera um pouco a geração. Finalmente, classifiquei os subconjuntos por tamanho, pois meu problema exige isso.
BoLe

3

Aqui está uma solução O (2 ^ n) iterativa 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 solução também usa espaço O (2 ^ n), o que seria muito para grandes conjuntos de entrada. É melhor seguir a definição recursiva, usando uma pilha ou uma fila no lugar da recursão.
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 é um conjunto finito com N elementos, então o conjunto de potência de S contém 2 ^ N elementos. O tempo para simplesmente enumerar os elementos do conjunto de potência é 2 ^ N, então O(2^N)é um limite inferior na complexidade de tempo de (ansiosamente) construir o conjunto de potência.

Simplificando, qualquer cálculo que envolva a criação de conjuntos de potência não vai escalar para grandes valores de N. Nenhum algoritmo inteligente irá ajudá-lo ... além de evitar a necessidade de criar conjuntos de potência!


1

Uma maneira sem recursão é a seguinte: Use uma máscara binária e faça todas as combinações possíveis.

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. Obtenha o tamanho do conjunto de potência powet_set_size = pow (2, set_size) 2 Loop para o contador de 0 a pow_set_size (a) Loop para i = 0 para set_size (i) Se o iº bit no contador é set Imprime o iésimo elemento do conjunto para este subconjunto (b) Imprime separador para subconjuntos, ou seja, nova linha

#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 é a minha solução recursiva que pode obter o conjunto de potência de qualquer conjunto usando Java Generics. Sua ideia principal é combinar o cabeçalho do array de entrada com todas as soluções possíveis do resto do array da seguinte maneira.

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

Isso resultará em:

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

Outro exemplo de implementação:

 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

Esta é minha abordagem com 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 em paralelo (ver comentário paralelo ()):

Tamanho do conjunto de entrada: 18

Processadores lógicos: 8 à 3,4 GHz

Melhoria de desempenho: 30%


1

Um subconjunto de t é qualquer conjunto que pode ser feito removendo zero ou mais elementos de t. O subconjunto withoutFirst adiciona os subconjuntos de t que estão sem o primeiro elemento e o loop for lidará com a adição de subconjuntos com o primeiro elemento. Por exemplo, se t contiver os elementos ["1", "2", "3"], omitirFirst adicionará [[""], ["2"], ["3"], ["2", "3 "]] e o loop for irá colar o" 1 "na frente desses elementos e adicioná-lo ao newSet. Então, vamos acabar com [[""], ["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 adicionar uma breve explicação junto com o código fornecido.
Mirza Sisic

Isso nem mesmo compila, parece ter sido escrito em alguma versão fantástica de Java. Setnão tem getmétodo com índice, nem subSetmétodo; 1stnão é um identificador válido (suponho que lstfoi dito). Mude todos os conjuntos para listas e quase compilará ...
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;
    }

}

Bem-vindo ao Stack Overflow. Você pode querer aprofundar esta resposta um pouco com algum texto que descreve o que está fazendo e como resolve o problema do questionador.
Lachlan Goodhew-Cook

0

Recentemente, tive que usar algo assim, mas precisava das menores sublistas (com 1 elemento, depois 2 elementos, ...) primeiro. Não quis incluir a lista vazia nem toda. Além disso, não precisei de uma lista de todas as sublistas devolvidas, só precisava fazer algumas coisas com cada uma.

Queria fazer isso sem recursão e veio com o seguinte (com o "fazer coisas" abstraído em uma interface 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
}

Dessa forma, também é fácil limitá-lo a sublistas de comprimentos específicos.


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

Ainda outra solução - com java8 + streaming api É preguiçoso e ordenado, portanto, retorna subconjuntos corretos quando é usado com "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 o código do cliente é

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

}

/ * Impressões: [] [a] [b] [c] [d] [e] [a, b] [a, c] [b, c] * /


0

Poderíamos escrever o conjunto de energia com ou sem o uso de recursão. Aqui está uma tentativa sem recursão:

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

Aqui está para gerar um conjunto de energia. A ideia é primeiro = S[0]e conjuntos menores sejam S[1,...n].

Calcule todos os subconjuntos de menorSet e coloque-os em todos os subconjuntos.

Para cada subconjunto em todos os subconjuntos, clone-o e adicione primeiro ao 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>();
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.