Étant donné un ensemble
{0, 1, 2, 3}
Quel est un bon moyen de produire les sous-ensembles:
[set(),
{0},
{1},
{2},
{3},
{0, 1},
{0, 2},
{0, 3},
{1, 2},
{1, 3},
{2, 3},
{0, 1, 2},
{0, 1, 3},
{0, 2, 3},
{1, 2, 3},
{0, 1, 2, 3}]
La page Python itertools
a exactement une recette powerset
pour cela:
def powerset(iterable):
"powerset([1,2,3]) --> () (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)"
s = list(iterable)
return chain.from_iterable(combinations(s, r) for r in range(len(s)+1))
Sortie:
>>> list(powerset("abcd"))
[(), ('a',), ('b',), ('c',), ('d',), ('a', 'b'), ('a', 'c'), ('a', 'd'), ('b', 'c'), ('b', 'd'), ('c', 'd'), ('a', 'b', 'c'), ('a', 'b', 'd'), ('a', 'c', 'd'), ('b', 'c', 'd'), ('a', 'b', 'c', 'd')]
Si vous n'aimez pas ce tuple vide au début, vous pouvez simplement changer l'instruction range
en range(1, len(s)+1)
pour éviter une combinaison de longueur nulle.
Voici plus de code pour un poweret. Ceci est écrit à partir de zéro:
>>> def powerset(s):
... x = len(s)
... for i in range(1 << x):
... print [s[j] for j in range(x) if (i & (1 << j))]
...
>>> powerset([4,5,6])
[]
[4]
[5]
[4, 5]
[6]
[4, 6]
[5, 6]
[4, 5, 6]
Le commentaire de Mark Rushakoff est applicable ici: "Si vous n'aimez pas ce tuple vide au début, on", vous pouvez simplement changer l'instruction range en range (1, len (s) +1) pour éviter une combinaison de longueur 0 ", sauf que dans mon cas, vous changez for i in range(1 << x)
en for i in range(1, 1 << x)
.
Revenant à cette année plus tard, je l'écrirais maintenant comme ceci:
def powerset(s):
x = len(s)
masks = [1 << i for i in range(x)]
for i in range(1 << x):
yield [ss for mask, ss in Zip(masks, s) if i & mask]
Et ensuite, le code de test ressemblerait à ceci:
print(list(powerset([4, 5, 6])))
Utiliser yield
signifie que vous n'avez pas besoin de calculer tous les résultats dans un seul morceau de mémoire. Précalculer les masques en dehors de la boucle principale est supposé être une optimisation intéressante.
Si vous cherchez une réponse rapide, je viens de chercher "python power set" sur google et de proposer ceci: Python Power Set Generator
Voici un copier-coller du code de cette page:
def powerset(seq):
"""
Returns all the subsets of this set. This is a generator.
"""
if len(seq) <= 1:
yield seq
yield []
else:
for item in powerset(seq[1:]):
yield [seq[0]]+item
yield item
Cela peut être utilisé comme ceci:
l = [1, 2, 3, 4]
r = [x for x in powerset(l)]
Maintenant, r est une liste de tous les éléments que vous vouliez, et qui peuvent être triés et imprimés:
r.sort()
print r
[[], [1], [1, 2], [1, 2, 3], [1, 2, 3, 4], [1, 2, 4], [1, 3], [1, 3, 4], [1, 4], [2], [2, 3], [2, 3, 4], [2, 4], [3], [3, 4], [4]]
def powerset(lst):
return reduce(lambda result, x: result + [subset + [x] for subset in result],
lst, [[]])
Il y a un raffinement de la puissance:
def powerset(seq):
"""
Returns all the subsets of this set. This is a generator.
"""
if len(seq) <= 0:
yield []
else:
for item in powerset(seq[1:]):
yield [seq[0]]+item
yield item
def get_power_set(s):
power_set=[[]]
for elem in s:
# iterate over the sub sets so far
for sub_set in power_set:
# add a new subset consisting of the subset at hand added elem
power_set=power_set+[list(sub_set)+[elem]]
return power_set
Par exemple:
get_power_set([1,2,3])
rendement
[[], [1], [2], [1, 2], [3], [1, 3], [2, 3], [1, 2, 3]]
Je sais que j'ai déjà ajouté une réponse, mais j'aime beaucoup ma nouvelle implémentation. Je prends un ensemble en entrée, mais il peut en réalité s'agir de n'importe quelle valeur, et je renvoie un ensemble d'ensembles qui correspond à la puissance de l'entrée. J'aime cette approche car elle est plus conforme à la définition mathématique de power set ( ensemble de tous les sous-ensembles ).
def power_set(A):
"""A is an iterable (list, Tuple, set, str, etc)
returns a set which is the power set of A."""
length = len(A)
l = [a for a in A]
ps = set()
for i in range(2 ** length):
selector = f'{i:0{length}b}'
subset = {l[j] for j, bit in enumerate(selector) if bit == '1'}
ps.add(frozenset(subset))
return ps
Si vous voulez exactement le résultat que vous avez posté dans votre réponse, utilisez ceci:
>>> [set(s) for s in power_set({1, 2, 3, 4})]
[{3, 4},
{2},
{1, 4},
{2, 3, 4},
{2, 3},
{1, 2, 4},
{1, 2},
{1, 2, 3},
{3},
{2, 4},
{1},
{1, 2, 3, 4},
set(),
{1, 3},
{1, 3, 4},
{4}]
On sait que le nombre d'éléments de l'ensemble de puissance est 2 ** len(A)
, ce qui est clairement visible dans la boucle for
.
J'ai besoin de convertir l'entrée (idéalement un ensemble) en une liste, car un ensemble est une structure de données d'éléments non ordonnés uniques, et l'ordre sera crucial pour générer les sous-ensembles.
selector
est la clé de cet algorithme. Notez que selector
a la même longueur que le jeu d’entrée, et pour rendre cela possible, il utilise une chaîne f avec padding. En gros, cela me permet de sélectionner les éléments qui seront ajoutés à chaque sous-ensemble à chaque itération. Supposons que le jeu d'entrées comporte 3 éléments {0, 1, 2}
, le sélecteur prendra donc des valeurs comprises entre 0 et 7 (inclus), qui sont en binaire:
000 # 0
001 # 1
010 # 2
011 # 3
100 # 4
101 # 5
110 # 6
111 # 7
Ainsi, chaque bit peut servir d'indicateur si un élément de l'ensemble d'origine doit être ajouté ou non. Regardez les nombres binaires, et considérez chaque nombre comme un élément du super ensemble dans lequel 1
signifie qu’un élément à l’index j
devrait être ajouté, et 0
que cet élément ne devrait pas être ajouté.
J'utilise un ensemble de compréhension pour générer un sous-ensemble à chaque itération et je convertis ce sous-ensemble en un frozenset
afin que je puisse l'ajouter à ps
(ensemble d'alimentation). Sinon, je ne pourrai pas l'ajouter car un ensemble en Python est constitué uniquement d'objets immuables.
Vous pouvez simplifier le code en utilisant certaines compréhensions en python, de sorte que vous puissiez vous en débarrasser. Vous pouvez également utiliser Zip
pour éviter d'utiliser j
index et le code se présentera comme suit:
def power_set(A):
length = len(A)
return {
frozenset({e for e, b in Zip(A, f'{i:{length}b}') if b == '1'})
for i in range(2 ** length)
}
C'est tout. Ce que j’aime dans cet algorithme, c’est qu’il est plus clair et plus intuitif que d’autres, car il semble assez magique de pouvoir compter sur itertools
même s’il fonctionne comme prévu.
Presque toutes ces réponses utilisent list
plutôt que set
, ce qui me paraissait un peu trompeur. Donc, par curiosité, j'ai essayé de faire une version simple vraiment sur set
et de la résumer pour d'autres personnes "novices en Python".
J'ai trouvé quelques bizarreries dans l'utilisation de Python set implementation . La principale surprise pour moi était de manipuler des ensembles vides. Ceci est en contraste avec Ruby's Set implementation , où je peux simplement faire Set[Set[]]
et obtenir un Set
contenant un vide Set
, alors je l'ai trouvé initialement un peu déroutant.
Pour réviser, en faisant powerset
avec set
s, j'ai rencontré deux problèmes:
set()
prend un itérable, donc set(set())
retournera set()
parce que l'ensemble vide iterable est vide (duh je suppose :))set({set()})
et set.add(set)
ne fonctionneront pas car set()
n'est pas hashablePour résoudre ces deux problèmes, j'ai utilisé frozenset()
, ce qui signifie que je ne comprends pas tout à fait ce que je veux (le type est littéralement set
), mais utilise l'interface globale set
.
def powerset(original_set):
# below gives us a set with one empty set in it
ps = set({frozenset()})
for member in original_set:
subset = set()
for m in ps:
# to be added into subset, needs to be
# frozenset.union(set) so it's hashable
subset.add(m.union(set([member]))
ps = ps.union(subset)
return ps
Ci-dessous, nous obtenons correctement 2² (16) frozenset
s:
In [1]: powerset(set([1,2,3,4]))
Out[2]:
{frozenset(),
frozenset({3, 4}),
frozenset({2}),
frozenset({1, 4}),
frozenset({3}),
frozenset({2, 3}),
frozenset({2, 3, 4}),
frozenset({1, 2}),
frozenset({2, 4}),
frozenset({1}),
frozenset({1, 2, 4}),
frozenset({1, 3}),
frozenset({1, 2, 3}),
frozenset({4}),
frozenset({1, 3, 4}),
frozenset({1, 2, 3, 4})}
Comme il n’ya aucun moyen d’avoir un set
de set
s en Python, si vous voulez transformer ces frozenset
s en set
s, vous devrez les reconvertir en un list
(list(map(set, powerset(set([1,2,3,4]))))
) ou modifier ce qui précède.
J'ai trouvé l'algorithme suivant très clair et simple:
def get_powerset(some_list):
"""Returns all subsets of size 0 - len(some_list) for some_list"""
if len(some_list) == 0:
return [[]]
subsets = []
first_element = some_list[0]
remaining_list = some_list[1:]
# Strategy: get all the subsets of remaining_list. For each
# of those subsets, a full subset list will contain both
# the original subset as well as a version of the subset
# that contains first_element
for partial_subset in get_all_subsets(remaining_list):
subsets.append(partial_subset)
subsets.append(partial_subset[:] + [first_element])
return subsets
Une autre façon de générer le pouvoir consiste à générer tous les nombres binaires contenant des bits n
. En tant que puissance, la quantité de chiffres avec n
chiffres est 2 ^ n
. Le principe de cet algorithme est qu'un élément peut être présent ou non dans un sous-ensemble, un chiffre binaire pouvant être un ou zéro mais pas les deux.
def power_set(items):
N = len(items)
# enumerate the 2 ** N possible combinations
for i in range(2 ** N):
combo = []
for j in range(N):
# test bit jth of integer i
if (i >> j) % 2 == 1:
combo.append(items[j])
yield combo
Je trouvais les deux algorithmes lorsque je prenais MITx: 6.00.2x Introduction à la pensée informatique et à la science des données, et j’estime qu’il s’agit de l’un des algorithmes les plus faciles à comprendre que j’ai jamais vu.
Juste un rafraîchissement rapide de la puissance!
L'ensemble de puissance d'un ensemble X est simplement l'ensemble de tous les sous-ensembles de X, y compris le jeu vide
Exemple set X = (a, b, c)
Jeu de puissance = {{a, b, c}, {a, b}, {a, c}, {b, c}, {a}, {b}, {c}, {}}
Voici un autre moyen de trouver le pouvoir:
def power_set(input):
# returns a list of all subsets of the list a
if (len(input) == 0):
return [[]]
else:
main_subset = [ ]
for small_subset in power_set(input[1:]):
main_subset += [small_subset]
main_subset += [[input[0]] + small_subset]
return main_subset
print(power_set([0,1,2,3]))
crédit complet à source
Je voulais juste fournir la solution la plus compréhensible, la version anti-code-golf.
from itertools import combinations
l = ["x", "y", "z", ]
def powerset(items):
combo = []
for r in range(len(items) + 1):
#use a list to coerce a actual list from the combinations generator
combo.append(list(combinations(items,r)))
return combo
l_powerset = powerset(l)
for i, item in enumerate(l_powerset):
print "All sets of length ", i
print item
Les résultats
Tous les ensembles de longueur 0
[()]
Tous les ensembles de longueur 1
[('x',), ('y',), ('z',)]
Tous les ensembles de longueur 2
[('x', 'y'), ('x', 'z'), ('y', 'z')]
Tous les ensembles de longueur 3
[('x', 'y', 'z')]
Pour plus voir la documentation itertools , également l'entrée wikipedia sur ensembles de puissance
Voici ma rapide implémentation utilisant des combinaisons mais utilisant uniquement des composants intégrés.
def powerSet(array):
length = str(len(array))
formatter = '{:0' + length + 'b}'
combinations = []
for i in xrange(2**int(length)):
combinations.append(formatter.format(i))
sets = set()
currentSet = []
for combo in combinations:
for i,val in enumerate(combo):
if val=='1':
currentSet.append(array[i])
sets.add(Tuple(sorted(currentSet)))
currentSet = []
return sets
Dans Python 3.5 ou supérieur, vous pouvez utiliser l'instruction yield from
avec itertools.combinations :
def subsets(iterable):
for n in range(len(iterable)):
yield from combinations(iterable, n + 1)
Avec un ensemble vide, qui fait partie de tous les sous-ensembles, vous pouvez utiliser:
def subsets(iterable):
for n in range(len(iterable) + 1):
yield from combinations(iterable, n)
Ceci est sauvage car aucune de ces réponses ne fournit réellement le retour d'un ensemble Python réel. Voici une implémentation désordonnée qui donnera un Powerset qui est en fait un Python set
.
test_set = set(['yo', 'whatup', 'money'])
def powerset( base_set ):
""" modified from pydoc's itertools recipe shown above"""
from itertools import chain, combinations
base_list = list( base_set )
combo_list = [ combinations(base_list, r) for r in range(len(base_set)+1) ]
powerset = set([])
for ll in combo_list:
list_of_frozensets = list( map( frozenset, map( list, ll ) ) )
set_of_frozensets = set( list_of_frozensets )
powerset = powerset.union( set_of_frozensets )
return powerset
print powerset( test_set )
# >>> set([ frozenset(['money','whatup']), frozenset(['money','whatup','yo']),
# frozenset(['whatup']), frozenset(['whatup','yo']), frozenset(['yo']),
# frozenset(['money','yo']), frozenset(['money']), frozenset([]) ])
J'aimerais voir une meilleure mise en œuvre, cependant.
Un moyen simple serait d'exploiter la représentation interne des nombres entiers de l'arithmétique du complément à 2.
La représentation binaire des nombres entiers est égale à {000, 001, 010, 011, 100, 101, 110, 111} pour les nombres compris entre 0 et 7. Pour une valeur de compteur entier, considérer 1 comme l'inclusion de l'élément correspondant dans la collection et '0' en tant qu'exclusion, nous pouvons générer des sous-ensembles basés sur la séquence de comptage. Les nombres doivent être générés de 0
à pow(2,n) -1
où n est la longueur de la matrice, c'est-à-dire le nombre de bits dans la représentation binaire.
Une simple Fonction de générateur de sous-ensemble basée sur celle-ci peut être écrite comme ci-dessous. Il repose essentiellement
def subsets(array):
if not array:
return
else:
length = len(array)
for max_int in range(0x1 << length):
subset = []
for i in range(length):
if max_int & (0x1 << i):
subset.append(array[i])
yield subset
et puis il peut être utilisé comme
def get_subsets(array):
powerset = []
for i in subsets(array):
powerser.append(i)
return powerset
Essais
Ajout de la suite dans le fichier local
if __== '__main__':
sample = ['b', 'd', 'f']
for i in range(len(sample)):
print "Subsets for " , sample[i:], " are ", get_subsets(sample[i:])
donne la sortie suivante
Subsets for ['b', 'd', 'f'] are [[], ['b'], ['d'], ['b', 'd'], ['f'], ['b', 'f'], ['d', 'f'], ['b', 'd', 'f']]
Subsets for ['d', 'f'] are [[], ['d'], ['f'], ['d', 'f']]
Subsets for ['f'] are [[], ['f']]