O conjunto de poderes {1, 2, 3}
é:
{{}, {2}, {3}, {2, 3}, {1, 2}, {1, 3}, {1, 2, 3}, {1}}
Digamos que eu tenha um Set
em 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).)
Respostas:
Sim, é
O(2^n)
verdade, já que você precisa gerar, bem,2^n
combinações possíveis. Aqui está uma implementação funcional, usando genéricos e conjuntos:E um teste, dado seu exemplo de entrada:
fonte
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á emO(n * 2^n)
: consulta do wolfram alphaNa 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, é óbvioO(2^n)
.contains()
seriaO(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)
.fonte
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!;))
Para chamá-lo, use este padrão:
É da minha Biblioteca do Projeto Euler ... :)
fonte
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 ...fonte
i < (1 << n)
que é equivalente.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).
fonte
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:
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:
fonte
Se você estiver usando Eclipse Collections (anteriormente GS Collections ), você pode usar o
powerSet()
método em todos os SetIterables.Nota: Eu sou um committer para Eclipse Collections.
fonte
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.
Aqui está um exemplo de código para testar:
fonte
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.
fonte
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:
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.
fonte
Aqui está uma solução O (2 ^ n) iterativa fácil:
fonte
fonte
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!
fonte
Uma maneira sem recursão é a seguinte: Use uma máscara binária e faça todas as combinações possíveis.
fonte
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
fonte
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.
Isso resultará em:
fonte
Outro exemplo de implementação:
fonte
Esta é minha abordagem com lambdas.
Ou em paralelo (ver comentário paralelo ()):
Tamanho do conjunto de entrada: 18
Processadores lógicos: 8 à 3,4 GHz
Melhoria de desempenho: 30%
fonte
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"]].
fonte
Set
não temget
método com índice, nemsubSet
método;1st
não é um identificador válido (suponho quelst
foi dito). Mude todos os conjuntos para listas e quase compilará ...fonte
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):
Dessa forma, também é fácil limitá-lo a sublistas de comprimentos específicos.
fonte
fonte
Ainda outra solução - com java8 + streaming api É preguiçoso e ordenado, portanto, retorna subconjuntos corretos quando é usado com "limit ()".
E o código do cliente é
/ * Impressões: [] [a] [b] [c] [d] [e] [a, b] [a, c] [b, c] * /
fonte
Poderíamos escrever o conjunto de energia com ou sem o uso de recursão. Aqui está uma tentativa sem recursão:
fonte
Aqui está para gerar um conjunto de energia. A ideia é primeiro =
S[0]
e conjuntos menores sejamS[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.
fonte
fonte