Étant donné un ensemble
{0, 1, 2, 3}
Comment puis-je 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}]
Réponses:
La itertools
page Python a exactement une powerset
recette pour cela:
from itertools import chain, combinations
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))
Production:
>>> 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' range
instruction range(1, len(s)+1)
pour éviter une combinaison de longueur 0.
s = list(iterable)
nécessaire?
__len__
implémentés essayez powerset((n for n in range(3)))
sans l'encapsulation de la liste.
powerset(range(3))
fonctionnerait bien même sanss = list(iterable)
.
Voici plus de code pour un PowerSet. 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 s'applique 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 dans mon cas, vous changez for i in range(1 << x)
en for i in range(1, 1 << x)
.
En revenant à cela des années 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 puis le code de test ressemblerait à ceci, disons:
print(list(powerset([4, 5, 6])))
L'utilisation yield
signifie que vous n'avez pas besoin de calculer tous les résultats dans une seule mémoire. Le précalcul des 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 je suis venu avec 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 peut être triée et imprimée:
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]]
[[][]]
, pour corriger cela, séparer simplement les cas de vérification de la longueurif len(seq) == 0: yield [] elif len(seq) == 1: yield seq yield []
Il y a un raffinement de Powerset:
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
Je sais que j'ai déjà ajouté une réponse, mais j'aime vraiment ma nouvelle implémentation. Je prends un ensemble comme entrée, mais il pourrait en fait être n'importe quel itérable, et je renvoie un ensemble d'ensembles qui est l'ensemble de puissance de l'entrée. J'aime cette approche car elle est plus alignée sur la définition mathématique de l' ensemble de puissance ( 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 publié 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)
, de sorte que cela pourrait être clairement vu dans la for
boucle.
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 uniques non ordonnés, et l'ordre sera crucial pour générer les sous-ensembles.
selector
est la clé de cet algorithme. Notez qu'il selector
a la même longueur que l'ensemble d'entrée, et pour rendre cela possible, il utilise une f-string avec un remplissage. Fondamentalement, cela me permet de sélectionner les éléments qui seront ajoutés à chaque sous-ensemble à chaque itération. Disons que le jeu d'entrées a 3 éléments {0, 1, 2}
, donc le sélecteur prendra des valeurs comprises entre 0 et 7 (inclus), qui en binaire sont:
000 # 0
001 # 1
010 # 2
011 # 3
100 # 4
101 # 5
110 # 6
111 # 7
Ainsi, chaque bit pourrait servir d'indicateur si un élément de l'ensemble d'origine doit être ajouté ou non. Regardez les nombres binaires, et pensez simplement à chaque nombre comme un élément du super ensemble dans lequel 1
signifie qu'un élément à l'index j
doit être ajouté et 0
que cet élément ne doit pas être ajouté.
J'utilise une compréhension d'ensemble 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 de puissance). Sinon, je ne pourrai pas l'ajouter car un ensemble en Python se compose uniquement d'objets immuables.
Vous pouvez simplifier le code en utilisant certaines compréhensions python, afin de pouvoir vous débarrasser de ces boucles for. Vous pouvez également utiliser zip
pour éviter d'utiliser l' j
index et le code se terminera 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 ça. Ce que j'aime de cet algorithme, c'est qu'il est plus clair et plus intuitif que d'autres car il semble assez magique de s'appuyer sur itertools
même s'il fonctionne comme prévu.
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]]
power_set
) dans la boucle qu'elle régit est une pratique très discutable. Par exemple, supposons que vous avez écrit ceci à la place du code de variable modification proposée: power_set += [list(sub_set)+[elem]]
. Ensuite, la boucle ne se termine pas.
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_powerset(remaining_list):
subsets.append(partial_subset)
subsets.append(partial_subset[:] + [first_element])
return subsets
Une autre façon de générer l'ensemble de puissance est de générer tous les nombres binaires qui ont des n
bits. En tant que puissance définie, la quantité de nombre 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 car un chiffre binaire peut ê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
J'ai trouvé les deux algorithmes lorsque je prenais MITx: 6.00.2x Introduction à la pensée computationnelle et à la science des données, et je considère que c'est l'un des algorithmes les plus faciles à comprendre que j'ai vus.
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 resultats
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 d'informations, consultez la documentation itertools , ainsi que l'entrée wikipedia sur les ensembles d'alimentation
Juste un rafraîchissement rapide de l'ensemble de puissance!
Ensemble de puissance d'un ensemble X, est simplement l'ensemble de tous les sous-ensembles de X, y compris l'ensemble vide
Exemple d'ensemble X = (a, b, c)
Ensemble de puissance = {{a, b, c}, {a, b}, {a, c}, {b, c}, {a}, {b}, {c}, {}}
Voici une autre façon de trouver l'ensemble de puissance:
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 total à la source
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)
Cela peut être fait très naturellement avec itertools.product
:
import itertools
def powerset(l):
for sl in itertools.product(*[[[], [i]] for i in l]):
yield {j for i in sl for j in i}
Un moyen simple serait d'exploiter la représentation interne des nombres entiers sous l'arithmétique du complément à 2.
La représentation binaire des nombres entiers est {000, 001, 010, 011, 100, 101, 110, 111} pour les nombres allant de 0 à 7. Pour une valeur de compteur entière, en considérant 1 comme inclusion de l'élément correspondant dans la collection et «0» comme 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 du tableau, c'est-à-dire le nombre de bits en représentation binaire.
Une fonction de générateur de sous-ensemble simple basée sur elle 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
Essai
Ajout de suivi dans un fichier local
if __name__ == '__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']]
Presque toutes ces réponses utilisent list
plutôt que set
, ce qui me semblait un peu une triche. Donc, par curiosité, j'ai essayé de faire une version simple set
et de résumer pour les autres "nouveaux à Python".
J'ai trouvé qu'il y avait quelques bizarreries dans le traitement de l' implémentation d'ensemble de Python . La principale surprise pour moi a été de manipuler des décors vides. Ceci est en contraste avec l' implémentation de Ruby's Set , où je peux simplement faire Set[Set[]]
et obtenir un Set
contenant un vide Set
, donc je l'ai trouvé au départ un peu déroutant.
Pour revoir, en faisant powerset
avec set
s, j'ai rencontré deux problèmes:
set()
prend un itérable, donc set(set())
reviendra set()
car l'ensemble vide itérable est vide (duh je suppose :))set({set()})
et set.add(set)
ne fonctionnera pas car set()
n'est pas hachablePour résoudre les deux problèmes, j'ai utilisé frozenset()
, ce qui signifie que je n'obtiens pas tout à fait ce que je veux (le type est littéralement set
), mais utilise l' set
interface globale .
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 2² (16) frozenset
s correctement en sortie:
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'y a aucun moyen d'avoir un set
de set
s en Python, si vous voulez transformer ces frozenset
s en set
s, vous devrez les mapper à nouveau dans un list
( list(map(set, powerset(set([1,2,3,4]))))
) ou modifier ce qui précède.
Peut-être que la question vieillit, mais j'espère que mon code aidera quelqu'un.
def powSet(set):
if len(set) == 0:
return [[]]
return addtoAll(set[0],powSet(set[1:])) + powSet(set[1:])
def addtoAll(e, set):
for c in set:
c.append(e)
return set
Utilisez la fonction powerset()
du package more_itertools
.
Rend tous les sous-ensembles possibles de l'itérable
>>> list(powerset([1, 2, 3]))
[(), (1,), (2,), (3,), (1, 2), (1, 3), (2, 3), (1, 2, 3)]
Si vous voulez des ensembles, utilisez:
list(map(set, powerset(iterable)))
Obtenir tous les sous-ensembles avec récursivité. Une doublure folle
from typing import List
def subsets(xs: list) -> List[list]:
return subsets(xs[1:]) + [x + [xs[0]] for x in subsets(xs[1:])] if xs else [[]]
Basé sur une solution Haskell
subsets :: [a] -> [[a]]
subsets [] = [[]]
subsets (x:xs) = map (x:) (subsets xs) ++ subsets xs
NameError: name 'List' is not defined
List
importation
def findsubsets(s, n):
return list(itertools.combinations(s, n))
def allsubsets(s) :
a = []
for x in range(1,len(s)+1):
a.append(map(set,findsubsets(s,x)))
return a
Vous pouvez le faire comme ceci:
def powerset(x):
m=[]
if not x:
m.append(x)
else:
A = x[0]
B = x[1:]
for z in powerset(B):
m.append(z)
r = [A] + z
m.append(r)
return m
print(powerset([1, 2, 3, 4]))
Production:
[[], [1], [2], [1, 2], [3], [1, 3], [2, 3], [1, 2, 3], [4], [1, 4], [2, 4], [1, 2, 4], [3, 4], [1, 3, 4], [2, 3, 4], [1, 2, 3, 4]]
Je sais que c'est trop tard
Il existe déjà beaucoup d'autres solutions mais quand même ...
def power_set(lst):
pw_set = [[]]
for i in range(0,len(lst)):
for j in range(0,len(pw_set)):
ele = pw_set[j].copy()
ele = ele + [lst[i]]
pw_set = pw_set + [ele]
return pw_set
C'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 ensemble de pouvoirs 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'adorerais voir une meilleure implémentation, cependant.
[*map(set, chain.from_iterable(combinations(s, r) for r in range(len(s)+1)))]
; la fonction arg de map
peut être frozenset
si vous préférez.
Voici ma mise en œuvre rapide en utilisant des combinaisons mais en utilisant uniquement des éléments 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
Tous les sous-ensembles de la plage n tels que définis:
n = int(input())
l = [i for i in range (1, n + 1)]
for number in range(2 ** n) :
binary = bin(number)[: 1 : -1]
subset = [l[i] for i in range(len(binary)) if binary[i] == "1"]
print(set(sorted(subset)) if number > 0 else "{}")
import math
def printPowerSet(set,set_size):
pow_set_size =int(math.pow(2, set_size))
for counter in range(pow_set_size):
for j in range(set_size):
if((counter & (1 << j)) > 0):
print(set[j], end = "")
print("")
set = ['a', 'b', 'c']
printPowerSet(set,3)
Une variante de la question, est un exercice que je vois sur le livre "Discovering Computer Science: Interdisciplinary Problems, Principles, and Python Programming. 2015 edition". Dans cet exercice 10.2.11, l'entrée est juste un nombre entier et la sortie doit être les ensembles de puissance. Voici ma solution récursive (n'utilisant rien d'autre que python3 de base)
def powerSetR(n):
assert n >= 0
if n == 0:
return [[]]
else:
input_set = list(range(1, n+1)) # [1,2,...n]
main_subset = [ ]
for small_subset in powerSetR(n-1):
main_subset += [small_subset]
main_subset += [ [input_set[-1]] + small_subset]
return main_subset
superset = powerSetR(4)
print(superset)
print("Number of sublists:", len(superset))
Et la sortie est
[[], [4], [3], [4, 3], [2], [4, 2], [3, 2], [4, 3, 2], [1], [4, 1 ], [3, 1], [4, 3, 1], [2, 1], [4, 2, 1], [3, 2, 1], [4, 3, 2, 1]] Nombre de sous-listes: 16
Je n'avais pas rencontré la more_itertools.powerset
fonction et je recommanderais de l'utiliser. Je recommande également de ne pas utiliser l'ordre par défaut de la sortie de itertools.combinations
, souvent au lieu de cela, vous voulez minimiser la distance entre les positions et trier les sous-ensembles d'éléments avec une distance plus courte entre eux au-dessus / avant les éléments avec une plus grande distance entre eux.
La itertools
page des recettes montre qu'il utilisechain.from_iterable
r
ici correspond à la notation standard pour la partie inférieure d'un coefficient binomial , le s
est généralement appelé n
dans les textes de mathématiques et sur les calculatrices («n Choisissez r»)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))
Les autres exemples ici donnent la puissance de [1,2,3,4]
de telle manière que les 2-tuples sont listés dans l'ordre "lexicographique" (lorsque nous imprimons les nombres sous forme d'entiers). Si j'écris la distance entre les nombres à côté (c'est-à-dire la différence), cela montre mon point:
12 ⇒ 1
13 ⇒ 2
14 ⇒ 3
23 ⇒ 1
24 ⇒ 2
34 ⇒ 1
L'ordre correct pour les sous-ensembles doit être l'ordre qui `` épuise '' d'abord la distance minimale, comme ceci:
12 ⇒ 1
23 ⇒ 1
34 ⇒ 1
13 ⇒ 2
24 ⇒ 2
14 ⇒ 3
L'utilisation de nombres ici rend cet ordre `` faux '', mais considérez par exemple les lettres, ["a","b","c","d"]
il est plus clair pourquoi cela pourrait être utile pour obtenir l'ensemble de puissance dans cet ordre:
ab ⇒ 1
bc ⇒ 1
cd ⇒ 1
ac ⇒ 2
bd ⇒ 2
ad ⇒ 3
Cet effet est plus prononcé avec plus d'éléments, et pour mes besoins, cela fait la différence entre être capable de décrire de manière significative les plages des index de l'ensemble de puissance.
(Il y a beaucoup d'écrit sur les codes Gray, etc. pour l'ordre de sortie des algorithmes en combinatoire, je ne le vois pas comme un problème secondaire).
En fait, je viens d'écrire un programme assez complexe qui utilisait ce code de partition d'entier rapide pour afficher les valeurs dans le bon ordre, mais j'ai ensuite découvert more_itertools.powerset
et pour la plupart des utilisations, il est probablement bien d'utiliser cette fonction comme suit:
from more_itertools import powerset
from numpy import ediff1d
def ps_sorter(tup):
l = len(tup)
d = ediff1d(tup).tolist()
return l, d
ps = powerset([1,2,3,4])
ps = sorted(ps, key=ps_sorter)
for x in ps:
print(x)
⇣
()
(1,)
(2,)
(3,)
(4,)
(1, 2)
(2, 3)
(3, 4)
(1, 3)
(2, 4)
(1, 4)
(1, 2, 3)
(2, 3, 4)
(1, 2, 4)
(1, 3, 4)
(1, 2, 3, 4)
J'ai écrit un code plus impliqué qui imprimera le powerset bien (voir la prise en pension pour à peu les fonctions d' impression , je ne l' ai pas inclus ici: print_partitions
, print_partitions_by_length
et pprint_tuple
).
pset_partitions.py
Tout cela est assez simple, mais cela peut quand même être utile si vous voulez un code qui vous permettra d'accéder directement aux différents niveaux de l'ensemble de puissance:
from itertools import permutations as permute
from numpy import cumsum
# http://jeromekelleher.net/generating-integer-partitions.html
# via
# /programming/10035752/elegant-python-code-for-integer-partitioning#comment25080713_10036764
def asc_int_partitions(n):
a = [0 for i in range(n + 1)]
k = 1
y = n - 1
while k != 0:
x = a[k - 1] + 1
k -= 1
while 2 * x <= y:
a[k] = x
y -= x
k += 1
l = k + 1
while x <= y:
a[k] = x
a[l] = y
yield tuple(a[:k + 2])
x += 1
y -= 1
a[k] = x + y
y = x + y - 1
yield tuple(a[:k + 1])
# https://stackoverflow.com/a/6285330/2668831
def uniquely_permute(iterable, enforce_sort=False, r=None):
previous = tuple()
if enforce_sort: # potential waste of effort (default: False)
iterable = sorted(iterable)
for p in permute(iterable, r):
if p > previous:
previous = p
yield p
def sum_min(p):
return sum(p), min(p)
def partitions_by_length(max_n, sorting=True, permuting=False):
partition_dict = {0: ()}
for n in range(1,max_n+1):
partition_dict.setdefault(n, [])
partitions = list(asc_int_partitions(n))
for p in partitions:
if permuting:
perms = uniquely_permute(p)
for perm in perms:
partition_dict.get(len(p)).append(perm)
else:
partition_dict.get(len(p)).append(p)
if not sorting:
return partition_dict
for k in partition_dict:
partition_dict.update({k: sorted(partition_dict.get(k), key=sum_min)})
return partition_dict
def print_partitions_by_length(max_n, sorting=True, permuting=True):
partition_dict = partitions_by_length(max_n, sorting=sorting, permuting=permuting)
for k in partition_dict:
if k == 0:
print(tuple(partition_dict.get(k)), end="")
for p in partition_dict.get(k):
print(pprint_tuple(p), end=" ")
print()
return
def generate_powerset(items, subset_handler=tuple, verbose=False):
"""
Generate the powerset of an iterable `items`.
Handling of the elements of the iterable is by whichever function is passed as
`subset_handler`, which must be able to handle the `None` value for the
empty set. The function `string_handler` will join the elements of the subset
with the empty string (useful when `items` is an iterable of `str` variables).
"""
ps = {0: [subset_handler()]}
n = len(items)
p_dict = partitions_by_length(n-1, sorting=True, permuting=True)
for p_len, parts in p_dict.items():
ps.setdefault(p_len, [])
if p_len == 0:
# singletons
for offset in range(n):
subset = subset_handler([items[offset]])
if verbose:
if offset > 0:
print(end=" ")
if offset == n - 1:
print(subset, end="\n")
else:
print(subset, end=",")
ps.get(p_len).append(subset)
for pcount, partition in enumerate(parts):
distance = sum(partition)
indices = (cumsum(partition)).tolist()
for offset in range(n - distance):
subset = subset_handler([items[offset]] + [items[offset:][i] for i in indices])
if verbose:
if offset > 0:
print(end=" ")
if offset == n - distance - 1:
print(subset, end="\n")
else:
print(subset, end=",")
ps.get(p_len).append(subset)
if verbose and p_len < n-1:
print()
return ps
À titre d'exemple, j'ai écrit un programme de démonstration CLI qui prend une chaîne comme argument de ligne de commande:
python string_powerset.py abcdef
⇣
a, b, c, d, e, f
ab, bc, cd, de, ef
ac, bd, ce, df
ad, be, cf
ae, bf
af
abc, bcd, cde, def
abd, bce, cdf
acd, bde, cef
abe, bcf
ade, bef
ace, bdf
abf
aef
acf
adf
abcd, bcde, cdef
abce, bcdf
abde, bcef
acde, bdef
abcf
abef
adef
abdf
acdf
acef
abcde, bcdef
abcdf
abcef
abdef
acdef
abcdef
Si vous voulez une longueur spécifique de sous-ensembles, vous pouvez le faire comme ceci:
from itertools import combinations
someSet = {0, 1, 2, 3}
([x for i in range(len(someSet)+1) for x in combinations(someSet,i)])
Plus généralement, pour les sous-ensembles de longueur arbitraire, vous pouvez modifier l'arugment de plage. La sortie est
[(), (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 )]
Dans Python 3.5 ou supérieur, vous pouvez utiliser l' yield from
instruction avec itertools.combinations :
def subsets(iterable):
for n in range(len(iterable)):
yield from combinations(iterable, n + 1)
def powerset(some_set):
res = [(a,b) for a in some_set for b in some_set]
return res