web-dev-qa-db-fra.com

Comment obtenir tous les sous-ensembles d'un ensemble? (poweret)

É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}]
48
datatoad

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.

72
Mark Rushakoff

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.

29
hughdbrown

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]]
13
Edan Maor
def powerset(lst):
    return reduce(lambda result, x: result + [subset + [x] for subset in result],
                  lst, [[]])
10
newacct

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
6
Jingguo Yao
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]]
4
jmkg

TL; DR (aller directement à la simplification)

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

Explication

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.

Simplification

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.

2
lmiguelvargasf

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 sets, j'ai rencontré deux problèmes:

  1. set() prend un itérable, donc set(set()) retournera set()parce que l'ensemble vide iterable est vide (duh je suppose :))
  2. pour obtenir un ensemble d'ensembles, set({set()}) et set.add(set) ne fonctionneront pas car set()n'est pas hashable

Pour 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) frozensets:

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 sets en Python, si vous voulez transformer ces frozensets en sets, vous devrez les reconvertir en un list (list(map(set, powerset(set([1,2,3,4]))))) ou modifier ce qui précède.

1
Alex Moore-Niemi

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.

1
lmiguelvargasf

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

1
grepit

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

1
Gourneau

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
0
Daniel

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)
0
Juan Carlos

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)
0
SubSet

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.

0

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']]
0
ViFI