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

103

É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}]
patrick
la source

Réponses:

145

La itertoolspage Python a exactement une powersetrecette 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' rangeinstruction range(1, len(s)+1)pour éviter une combinaison de longueur 0.

Mark Rushakoff
la source
1
C'est la réponse la plus rapide que j'ai pu trouver, en comparant d'autres solutions de cette page à celle-ci en utilisant le module timeit de Python. Cependant, dans certains cas, si vous avez besoin de modifier la sortie résultante (par exemple en joignant les lettres pour former des chaînes), écrire une recette personnalisée en utilisant des générateurs et construire la sortie que vous voulez (par exemple, ajouter deux chaînes) peut être beaucoup plus rapide.
Ceasar Bautista
pourquoi est s = list(iterable)nécessaire?
Jack Stevens
@JackStevens car les itérables ne sont pas rembobinables et ne doivent pas être __len__implémentés essayez powerset((n for n in range(3)))sans l'encapsulation de la liste.
hoefling
1
pour les grosses cordes, cela mangerait beaucoup de mémoire!
NoobEditor
1
@AlexandreHuat: Les plages sont des séquences paresseuses, pas des itérateurs. powerset(range(3))fonctionnerait bien même sanss = list(iterable) .
user2357112 prend en charge Monica
50

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 yieldsignifie 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.

Hughdbrown
la source
6
C'est une réponse créative. Cependant, je l'ai mesuré en utilisant timeit pour le comparer à Mark Rushakoff et j'ai remarqué qu'il était beaucoup plus lent. Pour générer 100 fois l'ensemble de puissance de 16 éléments, mes mesures étaient de 0,55 contre 15,6.
Ceasar Bautista
19

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]]
Edan Maor
la source
1
Dans le cas d'un tableau vide en entrée, le code ci-dessus reviendrait [[][]], 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 []
Ayush
3
Pour référence, j'ai mesuré cela (avec la modification d'Ayush) en utilisant timeit et je l'ai comparé à la recette Poweret dans la réponse de Mark Rushakoff. Sur ma machine, pour générer 100 fois l'ensemble de puissance de 16 éléments, cet algorithme a pris 1,36 seconde tandis que celui de Rushakoff en a pris 0,55.
Ceasar Bautista
Quelle sera la complexité du temps pour cela?
CodeQuestor
13
def powerset(lst):
    return reduce(lambda result, x: result + [subset + [x] for subset in result],
                  lst, [[]])
newacct
la source
8

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
Jingguo Yao
la source
8

TL; DR (aller directement à Simplification)

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

Explication

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 forboucle.

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.

selectorest la clé de cet algorithme. Notez qu'il selectora 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 1signifie qu'un élément à l'index jdoit être ajouté et 0que 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 frozensetafin 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.

Simplification

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 zippour éviter d'utiliser l' jindex 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 itertoolsmême s'il fonctionne comme prévu.

lmiguelvargasf
la source
5
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]]
jmkg
la source
1
Modifier une variable de boucle ( 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.
hughdbrown
5

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 nbits. En tant que puissance définie, la quantité de nombre avec nchiffres 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.

lmiguelvargasf
la source
3

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

Gourneau
la source
2

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

grepit
la source
2

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)
Sous-ensemble
la source
2

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}
Paul Crowley
la source
1
réponse la plus élégante donnée à cette question
Arthur B.
1

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) -1où 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']]
ViFI
la source
Ce n'est peut-être pas pratique en ce qui concerne la maintenabilité ou la lisibilité, mais cela m'a époustouflé. Merci pour le partage, solution intelligente!
lorey
1

Presque toutes ces réponses utilisent listplutôt que set, ce qui me semblait un peu une triche. Donc, par curiosité, j'ai essayé de faire une version simple setet 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 Setcontenant un vide Set, donc je l'ai trouvé au départ un peu déroutant.

Pour revoir, en faisant powersetavec sets, j'ai rencontré deux problèmes:

  1. set()prend un itérable, donc set(set())reviendra set() car l'ensemble vide itérable est vide (duh je suppose :))
  2. pour obtenir un ensemble d'ensembles, set({set()})et set.add(set)ne fonctionnera pas car set() n'est pas hachable

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

Alex Moore-Niemi
la source
1

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
Lisandro Di Meo
la source
ew, récursion! =)
étale-cohomology
Probablement pas le moyen le plus efficace, mais il est toujours intéressant de voir le chemin récursif!
Lisandro Di Meo
1

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)))
Alexandre Huat
la source
1
Tant de gens réinventent la roue ici, à mon humble avis, c'est la meilleure réponse car elle peut déjà l'être dans vos dépendances car elle est requise par de nombreuses bibliothèques courantes, par exemple pytest. libraries.io/pypi/more-itertools/dependents
lorey
1

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
Paweł Rubin
la source
NameError: name 'List' is not defined
4LegsDrivenCat
@ 4LegsDrivenCat J'ai ajouté une Listimportation
Paweł Rubin
1
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
Mohamed_Abdelmadjid Boudis
la source
Les réponses basées uniquement sur le code sont considérées comme de mauvaise qualité: assurez-vous de fournir une explication de ce que fait votre code et de la manière dont il résout le problème. Cela aidera le demandeur et les futurs lecteurs si vous pouvez ajouter plus d'informations dans votre message. Voir Expliquer les réponses entièrement basées sur le code
Calos
1

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]]
Blake
la source
1
Puis-je suggérer que lorsque vous publiez une solution de code, soyez assez aimable pour donner une explication détaillée de ce que fait le code et pourquoi vous utilisez telle ou telle méthode pour résoudre un problème. Les nouveaux codeurs ne doivent pas simplement regarder un bloc de code et le copier / coller sans savoir exactement ce que fait le code et pourquoi. Merci et bienvenue sur Stackoverflow.
Yokai le
Une réponse vraiment impressionnante et récursive.
John
1

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
Nilesh Das
la source
0

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.

Matthew Turner
la source
Bon point, mais l'OP veut une liste d'ensembles comme sortie, donc (en Python 3) vous pouvez le faire [*map(set, chain.from_iterable(combinations(s, r) for r in range(len(s)+1)))]; la fonction arg de mappeut être frozensetsi vous préférez.
PM 2 Ring
0

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

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 "{}")
Cestmoimahdi
la source
0
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)
Subbhashit Mukherjee
la source
0

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

GeorgiosDoumas
la source
0

Je n'avais pas rencontré la more_itertools.powersetfonction 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 itertoolspage des recettes montre qu'il utilisechain.from_iterable

  • Notez que rici correspond à la notation standard pour la partie inférieure d'un coefficient binomial , le sest généralement appelé ndans 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:

121
132
143
231
242
341

L'ordre correct pour les sous-ensembles doit être l'ordre qui `` épuise '' d'abord la distance minimale, comme ceci:

121
231
341
132
242
143

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.powersetet 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_lengthet pprint_tuple).

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
Louis Maddox
la source
0

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

Adel
la source
-1

Dans Python 3.5 ou supérieur, vous pouvez utiliser l' yield frominstruction avec itertools.combinations :

def subsets(iterable):
    for n in range(len(iterable)):
        yield from combinations(iterable, n + 1)
Juan C. Roldán
la source
-1
def powerset(some_set):
    res = [(a,b) for a in some_set for b in some_set]
    return res
Boz
la source
6
Bien que ce code puisse répondre à la question, fournir un contexte supplémentaire concernant la raison et / ou la manière dont ce code répond à la question améliore sa valeur à long terme. Pensez à lire Comment répondre et modifiez votre réponse pour l'améliorer.
blurfus
2
Ce que @blurfus est toujours une bonne pratique, mais est particulièrement important lorsque vous répondez à une question vieille de dix ans avec 28 autres réponses. Pourquoi est-ce une amélioration par rapport à la réponse acceptée? Qu'est-ce que cette réponse apporte que les autres réponses n'offrent pas?
Jeremy Caney