Comment obtenir toutes les combinaisons possibles des éléments d'une liste?

423

J'ai une liste avec 15 chiffres et j'ai besoin d'écrire du code qui produit les 32 768 combinaisons de ces nombres.

J'ai trouvé du code (par Google) qui fait apparemment ce que je recherche, mais j'ai trouvé le code assez opaque et je me méfie de l'utiliser. De plus, j'ai le sentiment qu'il doit y avoir une solution plus élégante.

La seule chose qui me vient à l'esprit serait de parcourir simplement les entiers décimaux 1–32768 et de les convertir en binaire, et d'utiliser la représentation binaire comme filtre pour sélectionner les nombres appropriés.

Quelqu'un connaît-il un meilleur moyen? À l'aide map(), peut-être?

Ben
la source
9
Les lecteurs doivent noter que la question de savoir si les éléments de la liste sont uniques est une considération extrêmement importante, car de nombreux algorithmes surévalueront alors certains sous-ensembles (par exemple, 'abccc' -> ['', 'a', 'b', 'c', 'c' , 'c', 'ac', 'ac', 'ac', ...]. Une solution de contournement simple consiste à simplement pousser tous les éléments d'un ensemble avant d' obtenir leurs permutations.
ninjagecko
@ninjagecko L'utilisation de la bibliothèque Set n'est pas efficace car chacune est au mieux O (n). Ainsi, ajouter n fonctions à un ensemble est en fait O (n ^ 2)!
Scott Biggs
D'après une lecture attentive de la question, il semble que l'OP demande le PowerSet de sa liste de 15 numéros, pas toutes les combinaisons. Je pense que cela peut être la raison pour laquelle les réponses sont partout.
Scott Biggs
@Scott Biggs: êtes-vous sûr de parler de Python ici? Les insertions et les recherches d'ensembles sont des cas moyens O (1). Ce sont comme des dictionnaires. Ils utilisent le hachage. Python n'a pas de bibliothèque d'ensemble spéciale (elle se trouve dans la bibliothèque standard). Nous insérons ici des nombres et non des fonctions. (Il serait toujours inefficace d'utiliser la mémoire O (2 ^ n); la solution appropriée pour les personnes qui souhaitent des combinaisons plutôt que le jeu de puissance est une implémentation récursive simple, ou product, etc.)
ninjagecko

Réponses:

467

Jetez un œil à itertools.combinations :

itertools.combinations(iterable, r)

Renvoie les sous-séquences de longueur r des éléments de l'itérable d'entrée.

Les combinaisons sont émises dans l'ordre de tri lexicographique. Ainsi, si l'itérable en entrée est trié, les tuples combinés seront produits dans un ordre trié.

Depuis 2.6, les piles sont incluses!

James Brady
la source
31
vous pouvez simplement tout lister. list(itertools.combinations(iterable, r))
silgon
1
y a-t-il quelque chose qui ne nécessite pas r, c'est-à-dire des combinaisons de sous-séquences de n'importe quelle longueur d'éléments.
mLstudent33
630

Cette réponse a manqué un aspect: le PO a demandé TOUTES les combinaisons ... pas seulement les combinaisons de longueur "r".

Il vous faudrait donc soit parcourir toutes les longueurs "L":

import itertools

stuff = [1, 2, 3]
for L in range(0, len(stuff)+1):
    for subset in itertools.combinations(stuff, L):
        print(subset)

Ou - si vous voulez devenir fou (ou fléchir le cerveau de celui qui lit votre code après vous) - vous pouvez générer la chaîne de générateurs "combinaisons ()", et itérer à travers cela:

from itertools import chain, combinations
def all_subsets(ss):
    return chain(*map(lambda x: combinations(ss, x), range(0, len(ss)+1)))

for subset in all_subsets(stuff):
    print(subset)
Dan H
la source
42
Merci pour le soutien! Dans les semaines qui ont suivi la publication de la réponse ci-dessus, j'ai constaté que le NOM du concept pour ce que Ben recherchait est le "jeu de puissance" de l'ensemble original de 15 articles. En fait, un exemple d'implémentation est donné sur la page doc standard de python "itertools": docs.python.org/library/itertools.html (grep pour "powerset").
Dan H
38
Pour tous ceux qui lisent jusqu'ici: La powerset()fonction de générateur dans la section des recettes de la itertoolsdocumentation est plus simple, utilise potentiellement moins de mémoire et est probablement plus rapide que l'implémentation présentée ici.
martineau
Est-il possible de générer toutes les combinaisons dans l'ordre lexicographique?
guik
@guik: Je suis sûr à 99% que itertools.combinationsl'ordre des articles est préservé dans les listes qu'il génère. Ainsi, si l'entrée est triée lexicalement, alors chacune des sorties le sera également.
Dan H
Oui, itertools.combinationsgénère les combinaisons de k parmi n dans l'ordre lexicographique, mais pas toutes les combinaisons jusqu'à k parmi n. powersetgénère toutes les combinaisons jusqu'à k, mais pas dans l'ordre lexicographique pour autant que je le comprends: powerset ([1,2]) -> [(), (1,), (2,), (1, 2)] . Cela ne devrait-il pas être: [(), (1,), (1, 2), (2,)]?
guik
52

Voici un one-liner paresseux, utilisant également itertools:

from itertools import compress, product

def combinations(items):
    return ( set(compress(items,mask)) for mask in product(*[[0,1]]*len(items)) )
    # alternative:                      ...in product([0,1], repeat=len(items)) )

Idée principale derrière cette réponse: il y a 2 ^ N combinaisons - identiques au nombre de chaînes binaires de longueur N. Pour chaque chaîne binaire, vous choisissez tous les éléments correspondant à un "1".

items=abc * mask=###
 |
 V
000 -> 
001 ->   c
010 ->  b
011 ->  bc
100 -> a
101 -> a c
110 -> ab
111 -> abc

Choses à considérer:

  • Cela exige que vous pouvez appeler len(...)sur items(solution de contournement: si itemsest quelque chose comme un itérables comme un générateur, la transformer en une première liste avec items=list(_itemsArg))
  • Cela nécessite que l'ordre d'itération sur itemsne soit pas aléatoire (solution de contournement: ne soyez pas fou)
  • Cela exige que les articles sont uniques, ou bien {2,2,1}et {2,1,1}sera à la fois à l' effondrement {2,1}(solution de contournement: l' utilisation collections.Counteren remplacement sans rendez- vous pour set, il est fondamentalement un multiset ... si vous devrez peut - être utiliser plus tard tuple(sorted(Counter(...).elements()))si vous avez besoin d'être hashable)

Démo

>>> list(combinations(range(4)))
[set(), {3}, {2}, {2, 3}, {1}, {1, 3}, {1, 2}, {1, 2, 3}, {0}, {0, 3}, {0, 2}, {0, 2, 3}, {0, 1}, {0, 1, 3}, {0, 1, 2}, {0, 1, 2, 3}]

>>> list(combinations('abcd'))
[set(), {'d'}, {'c'}, {'c', 'd'}, {'b'}, {'b', 'd'}, {'c', 'b'}, {'c', 'b', 'd'}, {'a'}, {'a', 'd'}, {'a', 'c'}, {'a', 'c', 'd'}, {'a', 'b'}, {'a', 'b', 'd'}, {'a', 'c', 'b'}, {'a', 'c', 'b', 'd'}]
ninjagecko
la source
46

Dans les commentaires sous la réponse très appréciée de @Dan H, il est fait mention de la powerset()recette dans la itertoolsdocumentation, y compris celle de Dan lui-même . Cependant , jusqu'à présent, personne ne l'a posté comme réponse. Étant donné que c'est probablement l'une des meilleures, sinon la meilleure approche du problème - et étant donné un peu d'encouragement d'un autre intervenant, c'est indiqué ci-dessous. La fonction produit toutes les combinaisons uniques des éléments de liste de toutes les longueurs possibles (y compris celles contenant zéro et tous les éléments).

Remarque : Si le, subtilement différent, le but est d'obtenir que des combinaisons d'éléments uniques, changer la ligne s = list(iterable)à s = list(set(iterable))éliminer tous les éléments en double. Quoi qu'il en soit, le fait que le iterablesoit finalement transformé en un listmoyen de fonctionner avec des générateurs (contrairement à plusieurs des autres réponses).

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)  # allows duplicate elements
    return chain.from_iterable(combinations(s, r) for r in range(len(s)+1))

stuff = [1, 2, 3]
for i, combo in enumerate(powerset(stuff), 1):
    print('combo #{}: {}'.format(i, combo))

Production:

combo #1: ()
combo #2: (1,)
combo #3: (2,)
combo #4: (3,)
combo #5: (1, 2)
combo #6: (1, 3)
combo #7: (2, 3)
combo #8: (1, 2, 3)
martineau
la source
À quoi sert la list()conversion en premier lieu?
AMC
@Alexander: pour permettre de déterminer la longueur de l'itérable.
martineau
36

En voici un qui utilise la récursivité:

>>> import copy
>>> def combinations(target,data):
...     for i in range(len(data)):
...         new_target = copy.copy(target)
...         new_data = copy.copy(data)
...         new_target.append(data[i])
...         new_data = data[i+1:]
...         print new_target
...         combinations(new_target,
...                      new_data)
...                      
... 
>>> target = []
>>> data = ['a','b','c','d']
>>> 
>>> combinations(target,data)
['a']
['a', 'b']
['a', 'b', 'c']
['a', 'b', 'c', 'd']
['a', 'b', 'd']
['a', 'c']
['a', 'c', 'd']
['a', 'd']
['b']
['b', 'c']
['b', 'c', 'd']
['b', 'd']
['c']
['c', 'd']
['d']
darxtrix
la source
Peut-il être modifié pour renvoyer une liste de listes au lieu d'imprimer?
James Vickery
@JamesVickery oui, vous pouvez envisager de créer une liste en dehors de la fonction et de l'ajouter, ou (mieux) faire de la fonction un générateur, jetez un œil au mot-clé 'yield' :)
Dangercrow
new_data = copy.copy(data)- cette ligne est redondante pour autant que je vois, elle n'influence rien
Dmitriy Fialkovskiy
31

Ce one-liner vous donne toutes les combinaisons (entre les éléments 0et nsi la liste / l'ensemble d'origine contient ndes éléments distincts) et utilise la méthode native itertools.combinations:

Python 2

from itertools import combinations

input = ['a', 'b', 'c', 'd']

output = sum([map(list, combinations(input, i)) for i in range(len(input) + 1)], [])

Python 3

from itertools import combinations

input = ['a', 'b', 'c', 'd']

output = sum([list(map(list, combinations(input, i))) for i in range(len(input) + 1)], [])

La sortie sera:

[[],
 ['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']]

Essayez-le en ligne:

http://ideone.com/COghfX

Mathieu Rodic
la source
Ceci est une permutation
AdHominem
15
@AdHominem: non, ce n'est pas le cas. C'est une liste de toutes les combinaisons. Les permutations incluraient, par exemple ['b', 'a'].
naught101
TypeError: can only concatenate list (not "map") to list
0x48piraj
@ 0x48piraj: merci de l'avoir remarqué, j'ai modifié ma réponse en conséquence!
Mathieu Rodic
21

Je suis d'accord avec Dan H que Ben a en effet demandé toutes les combinaisons. itertools.combinations()ne donne pas toutes les combinaisons.

Un autre problème est que si l'entrée itérable est grande, il est peut-être préférable de renvoyer un générateur au lieu de tout dans une liste:

iterable = range(10)
for s in xrange(len(iterable)+1):
  for comb in itertools.combinations(iterable, s):
    yield comb
HongboZhu
la source
1
Bel exemple. J'adore les générateurs ... et j'adore Python pour les avoir! Cet exemple n'a qu'un seul objet combinaisons () à la fois et donne une des combinaisons à la fois. (Peut-être que vous voulez ajouter le bloc def autour de cela - comme exemple d'utilisation.) Notez que mon implémentation (avec chain (), donnée ci-dessus) n'est pas trop pire: il est vrai que cela crée tous les générateurs len (itérables) à une fois ... mais il ne crée PAS toutes les combinaisons de 2 ** len (itérables) à la fois, car - à ma connaissance - la chaîne "utilise" le premier générateur avant de puiser dans les suivantes.
Dan H
18

Il s'agit d'une approche qui peut être facilement transférée à tous les langages de programmation prenant en charge la récursivité (pas d'outils itert, pas de rendement, pas de compréhension de liste) :

def combs(a):
    if len(a) == 0:
        return [[]]
    cs = []
    for c in combs(a[1:]):
        cs += [c, c+[a[0]]]
    return cs

>>> combs([1,2,3,4,5])
[[], [1], [2], [2, 1], [3], [3, 1], [3, 2], ..., [5, 4, 3, 2, 1]]
Jonathan R
la source
Ah! Belle implémentation. Je reconnais HEAD = a [0], TAIL = a [1:] de Prolog. Ou car = a [0], cdr = a [1:] de Lisp. Je me demande si nous pourrions utiliser la mémorisation ici ...
Javier Ruiz Il y a
Vrai. Le découpage de liste est O (k) où k est la longueur du découpage. Je suppose que l'on pourrait accélérer cela en faisant une recherche dans une carte, ce qui en ferait O (1) dans tous les passages, sauf le premier. Notez cependant que cette implémentation ne doit pas être référencée pour les performances. Pour cela, de meilleures implémentations existent. Cette implémentation est uniquement pour la simplicité et la portabilité vers la plupart des autres langues.
Jonathan R Il y a
14

Vous pouvez générer toutes les combinaisons d'une liste en python en utilisant ce code simple

import itertools

a = [1,2,3,4]
for i in xrange(0,len(a)+1):
   print list(itertools.combinations(a,i))

Le résultat serait:

[()]
[(1,), (2,), (3,), (4,)]
[(1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)]
[(1, 2, 3), (1, 2, 4), (1, 3, 4), (2, 3, 4)]
[(1, 2, 3, 4)]
saimadhu.polamuri
la source
Bug dans ce code: ne renvoie pas l'ensemble vide. Peut signifier xrange (0, ...) mais n'a pas été testé. modifier : j'ai continué et j'ai modifié votre réponse pour le corriger.
ninjagecko
13

Je pensais que j'ajouterais cette fonction pour ceux qui recherchent une réponse sans importer d'itertools ou d'autres bibliothèques supplémentaires.

def powerSet(items):
    """
    Power set generator: get all possible combinations of a list’s elements

    Input:
        items is a list
    Output:
        returns 2**n combination lists one at a time using a generator 

    Reference: edx.org 6.00.2x Lecture 2 - Decision Trees and dynamic programming
    """

    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

Utilisation du générateur de rendement simple:

for i in powerSet([1,2,3,4]):
    print (i, ", ",  end="")

Sortie de l'exemple d'utilisation ci-dessus:

[], [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],


la source
Je pense que c'est une solution très soignée.
greentec
8

Voici encore une autre solution (one-liner), impliquant l'utilisation de la itertools.combinationsfonction, mais ici nous utilisons une compréhension à double liste (par opposition à une boucle for ou à une somme):

def combs(x):
    return [c for i in range(len(x)+1) for c in combinations(x,i)]

Démo:

>>> combs([1,2,3,4])
[(), 
 (1,), (2,), (3,), (4,), 
 (1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4), 
 (1, 2, 3), (1, 2, 4), (1, 3, 4), (2, 3, 4), 
 (1, 2, 3, 4)]
ninjagecko
la source
5
from itertools import permutations, combinations


features = ['A', 'B', 'C']
tmp = []
for i in range(len(features)):
    oc = combinations(features, i + 1)
    for c in oc:
        tmp.append(list(c))

production

[
 ['A'],
 ['B'],
 ['C'],
 ['A', 'B'],
 ['A', 'C'],
 ['B', 'C'],
 ['A', 'B', 'C']
]
Andrew Li
la source
4

Vous trouverez ci-dessous une "réponse récursive standard", similaire à l'autre réponse similaire https://stackoverflow.com/a/23743696/711085 . (En réalité, nous n'avons pas à nous soucier de manquer d'espace de pile car il n'y a aucun moyen de traiter toutes les N! Permutations.)

Il visite chaque élément tour à tour et le prend ou le quitte (nous pouvons voir directement la cardinalité 2 ^ N de cet algorithme).

def combs(xs, i=0):
    if i==len(xs):
        yield ()
        return
    for c in combs(xs,i+1):
        yield c
        yield c+(xs[i],)

Démo:

>>> list( combs(range(5)) )
[(), (0,), (1,), (1, 0), (2,), (2, 0), (2, 1), (2, 1, 0), (3,), (3, 0), (3, 1), (3, 1, 0), (3, 2), (3, 2, 0), (3, 2, 1), (3, 2, 1, 0), (4,), (4, 0), (4, 1), (4, 1, 0), (4, 2), (4, 2, 0), (4, 2, 1), (4, 2, 1, 0), (4, 3), (4, 3, 0), (4, 3, 1), (4, 3, 1, 0), (4, 3, 2), (4, 3, 2, 0), (4, 3, 2, 1), (4, 3, 2, 1, 0)]

>>> list(sorted( combs(range(5)), key=len))
[(), 
 (0,), (1,), (2,), (3,), (4,), 
 (1, 0), (2, 0), (2, 1), (3, 0), (3, 1), (3, 2), (4, 0), (4, 1), (4, 2), (4, 3), 
 (2, 1, 0), (3, 1, 0), (3, 2, 0), (3, 2, 1), (4, 1, 0), (4, 2, 0), (4, 2, 1), (4, 3, 0), (4, 3, 1), (4, 3, 2), 
 (3, 2, 1, 0), (4, 2, 1, 0), (4, 3, 1, 0), (4, 3, 2, 0), (4, 3, 2, 1), 
 (4, 3, 2, 1, 0)]

>>> len(set(combs(range(5))))
32
ninjagecko
la source
2

Utilisation de la compréhension de liste:

def selfCombine( list2Combine, length ):
    listCombined = str( ['list2Combine[i' + str( i ) + ']' for i in range( length )] ).replace( "'", '' ) \
                     + 'for i0 in range(len( list2Combine ) )'
    if length > 1:
        listCombined += str( [' for i' + str( i ) + ' in range( i' + str( i - 1 ) + ', len( list2Combine ) )' for i in range( 1, length )] )\
            .replace( "', '", ' ' )\
            .replace( "['", '' )\
            .replace( "']", '' )

    listCombined = '[' + listCombined + ']'
    listCombined = eval( listCombined )

    return listCombined

list2Combine = ['A', 'B', 'C']
listCombined = selfCombine( list2Combine, 2 )

La sortie serait:

['A', 'A']
['A', 'B']
['A', 'C']
['B', 'B']
['B', 'C']
['C', 'C']
zmk
la source
4
Cette proposition consiste à faire des changements de chaîne pour créer des ensembles?!?! Holy crow .... Et: il ne s'agit pas de renvoyer le jeu de puissance, mais plutôt quelque chose comme combinaisons_with_replacement (). (Voir docs.python.org/library/… )
Dan H
Cela fait en effet la même chose que combination_with_replacement () , mais au moins sur ma boîte, cela fonctionne légèrement plus vite que itertools . Que puis-je dire, j'aime les listes de compréhension.
zmk
1
Merci pour la réponse! Qu'en est-il de la création d'une liste combinée à des listes inversées telles que ['A', 'A'], ['A', 'B'], ['A', 'C'], ['B', 'A'], [ «B», «B»], [«B», «C»], [«C», «A»], [«C», «B»] et [«C», «C»] qui comprennent tout?
Karyo
Très intéressant, mais mon python n'est pas tout à fait capable de comprendre les subtilités ici. Y a-t-il quelque chose de spécial à propos de l'utilisation de listCombined dans différentes étendues et du fait que la boucle for est sur une seule ligne? J'essaie de porter cela sur Java avec peu de chance.
Scott Biggs
2

Ce code utilise un algorithme simple avec des listes imbriquées ...

# FUNCTION getCombos: To generate all combos of an input list, consider the following sets of nested lists...
#
#           [ [ [] ] ]
#           [ [ [] ], [ [A] ] ]
#           [ [ [] ], [ [A],[B] ],         [ [A,B] ] ]
#           [ [ [] ], [ [A],[B],[C] ],     [ [A,B],[A,C],[B,C] ],                   [ [A,B,C] ] ]
#           [ [ [] ], [ [A],[B],[C],[D] ], [ [A,B],[A,C],[B,C],[A,D],[B,D],[C,D] ], [ [A,B,C],[A,B,D],[A,C,D],[B,C,D] ], [ [A,B,C,D] ] ]
#
#  There is a set of lists for each number of items that will occur in a combo (including an empty set).
#  For each additional item, begin at the back of the list by adding an empty list, then taking the set of
#  lists in the previous column (e.g., in the last list, for sets of 3 items you take the existing set of
#  3-item lists and append to it additional lists created by appending the item (4) to the lists in the
#  next smallest item count set. In this case, for the three sets of 2-items in the previous list. Repeat
#  for each set of lists back to the initial list containing just the empty list.
#

def getCombos(listIn = ['A','B','C','D','E','F'] ):
    listCombos = [ [ [] ] ]     # list of lists of combos, seeded with a list containing only the empty list
    listSimple = []             # list to contain the final returned list of items (e.g., characters)

    for item in listIn:
        listCombos.append([])   # append an emtpy list to the end for each new item added
        for index in xrange(len(listCombos)-1, 0, -1):  # set the index range to work through the list
            for listPrev in listCombos[index-1]:        # retrieve the lists from the previous column
                listCur = listPrev[:]                   # create a new temporary list object to update
                listCur.append(item)                    # add the item to the previous list to make it current
                listCombos[index].append(listCur)       # list length and append it to the current list

                itemCombo = ''                          # Create a str to concatenate list items into a str
                for item in listCur:                    # concatenate the members of the lists to create
                    itemCombo += item                   # create a string of items
                listSimple.append(itemCombo)            # add to the final output list

    return [listSimple, listCombos]
# END getCombos()
Conseils
la source
Donc, ce que ce code semble faire, c'est retourner [listOfCombinations, listOfCombinationsGroupedBySize]. Malheureusement, lorsqu'il est exécuté avec l'entrée de démonstration, il donne 63 éléments plutôt que 64; il semble manquer l'ensemble vide (dans ce cas, la chaîne vide "").
ninjagecko
2

Je sais qu'il est beaucoup plus pratique d'utiliser itertools pour obtenir toutes les combinaisons, mais vous pouvez y parvenir en partie avec seulement une compréhension de la liste si vous le souhaitez, étant donné que vous voulez coder beaucoup

Pour les combinaisons de deux paires:

    lambda l: [(a, b) for i, a in enumerate(l) for b in l[i+1:]]


Et, pour les combinaisons de trois paires, c'est aussi simple que cela:

    lambda l: [(a, b, c) for i, a in enumerate(l) for ii, b in enumerate(l[i+1:]) for c in l[i+ii+2:]]


Le résultat est identique à l'utilisation d'itertools.combinations:

import itertools
combs_3 = lambda l: [
    (a, b, c) for i, a in enumerate(l) 
    for ii, b in enumerate(l[i+1:]) 
    for c in l[i+ii+2:]
]
data = ((1, 2), 5, "a", None)
print("A:", list(itertools.combinations(data, 3)))
print("B:", combs_3(data))
# A: [((1, 2), 5, 'a'), ((1, 2), 5, None), ((1, 2), 'a', None), (5, 'a', None)]
# B: [((1, 2), 5, 'a'), ((1, 2), 5, None), ((1, 2), 'a', None), (5, 'a', None)]
Cynadyde
la source
2

Sans utiliser itertools:

def combine(inp):
    return combine_helper(inp, [], [])


def combine_helper(inp, temp, ans):
    for i in range(len(inp)):
        current = inp[i]
        remaining = inp[i + 1:]
        temp.append(current)
        ans.append(tuple(temp))
        combine_helper(remaining, temp, ans)
        temp.pop()
    return ans


print(combine(['a', 'b', 'c', 'd']))
Pradeep Vairamani
la source
2

Voici deux implémentations de itertools.combinations

Celui qui renvoie une liste

def combinations(lst, depth, start=0, items=[]):
    if depth <= 0:
        return [items]
    out = []
    for i in range(start, len(lst)):
        out += combinations(lst, depth - 1, i + 1, items + [lst[i]])
    return out

On retourne un générateur

def combinations(lst, depth, start=0, prepend=[]):
    if depth <= 0:
        yield prepend
    else:
        for i in range(start, len(lst)):
            for c in combinations(lst, depth - 1, i + 1, prepend + [lst[i]]):
                yield c

Veuillez noter qu'il est conseillé de fournir une fonction d'assistance à ceux-ci car l'argument de préfixe est statique et ne change pas à chaque appel.

print([c for c in combinations([1, 2, 3, 4], 3)])
# [[1, 2, 3], [1, 2, 4], [1, 3, 4], [2, 3, 4]]

# get a hold of prepend
prepend = [c for c in combinations([], -1)][0]
prepend.append(None)

print([c for c in combinations([1, 2, 3, 4], 3)])
# [[None, 1, 2, 3], [None, 1, 2, 4], [None, 1, 3, 4], [None, 2, 3, 4]]

Ceci est un cas très superficiel mais mieux vaut prévenir que guérir

Modar
la source
2

Que diriez-vous de cela ... utilisé une chaîne au lieu de liste, mais la même chose .. chaîne peut être traitée comme une liste en Python:

def comb(s, res):
    if not s: return
    res.add(s)
    for i in range(0, len(s)):
        t = s[0:i] + s[i + 1:]
        comb(t, res)

res = set()
comb('game', res) 

print(res)
Apurva Singh
la source
2

Combinaison d'itertools

import itertools
col_names = ["aa","bb", "cc", "dd"]
all_combinations = itertools.chain(*[itertools.combinations(col_names,i+1) for i,_ in enumerate(col_names)])
print(list(all_combinations))

Merci

Abhishek
la source
2

Sans itertools Python 3, vous pourriez faire quelque chose comme ceci:

def combinations(arr, carry):
    for i in range(len(arr)):
        yield carry + arr[i]
        yield from combinations(arr[i + 1:], carry + arr[i])

où au départ carry = "".

Laurynas Tamulevičius
la source
2

3 fonctions:

  1. toutes les combinaisons de n éléments liste
  2. toutes les combinaisons de n éléments listent où l'ordre n'est pas distinct
  3. toutes permutations
import sys

def permutations(a):
    return combinations(a, len(a))

def combinations(a, n):
    if n == 1:
        for x in a:
            yield [x]
    else:
        for i in range(len(a)):
            for x in combinations(a[:i] + a[i+1:], n-1):
                yield [a[i]] + x

def combinationsNoOrder(a, n):
    if n == 1:
        for x in a:
            yield [x]
    else:
        for i in range(len(a)):
            for x in combinationsNoOrder(a[:i], n-1):
                yield [a[i]] + x

if __name__ == "__main__":
    for s in combinations(list(map(int, sys.argv[2:])), int(sys.argv[1])):
        print(s)
Alan Swindells
la source
J'aime beaucoup !!! Je vous remercie!!! Les fonctions combinatoires de Python sont cependant un peu étranges. En mathématiques, la fonction "combinaisons" serait Variations, et "combinaisonsNonOrdre" sont en fait des combinaisons. Je suppose que cela confond les gens qui viennent au python de la branche des mathématiques, comme cela m'a été le cas cette fois. Quoi qu'il en soit, une belle solution, merci beaucoup un gain!
Đumić Branislav
1

Ceci est ma mise en œuvre

    def get_combinations(list_of_things):
    """gets every combination of things in a list returned as a list of lists

    Should be read : add all combinations of a certain size to the end of a list for every possible size in the
    the list_of_things.

    """
    list_of_combinations = [list(combinations_of_a_certain_size)
                            for possible_size_of_combinations in range(1,  len(list_of_things))
                            for combinations_of_a_certain_size in itertools.combinations(list_of_things,
                                                                                         possible_size_of_combinations)]
    return list_of_combinations
Andres Ulloa
la source
1
Quelle est votre implémentation qui résout mieux que les implémentations précédentes publiées ici.
user1767754
0

Vous pouvez également utiliser la fonction PowerSet de l'excellent more_itertoolspackage.

from more_itertools import powerset

l = [1,2,3]
list(powerset(l))

# [(), (1,), (2,), (3,), (1, 2), (1, 3), (2, 3), (1, 2, 3)]

Nous pouvons également vérifier qu'il répond aux exigences de l'OP

from more_itertools import ilen

assert ilen(powerset(range(15))) == 32_768
Jarno
la source
-1
def combinations(iterable, r):
# combinations('ABCD', 2) --> AB AC AD BC BD CD
# combinations(range(4), 3) --> 012 013 023 123
pool = tuple(iterable)
n = len(pool)
if r > n:
    return
indices = range(r)
yield tuple(pool[i] for i in indices)
while True:
    for i in reversed(range(r)):
        if indices[i] != i + n - r:
            break
    else:
        return
    indices[i] += 1
    for j in range(i+1, r):
        indices[j] = indices[j-1] + 1
    yield tuple(pool[i] for i in indices)


x = [2, 3, 4, 5, 1, 6, 4, 7, 8, 3, 9]
for i in combinations(x, 2):
    print i
Anoop
la source
1
Si j'ai raison, c'est le code exact copié de la documentation python [ docs.python.org/3.6/library/itertools.html ]. Si oui, veuillez faire référence à la source.
GabrielChu
approche intéressante
pelos
-1

Si quelqu'un cherche une liste inversée, comme je l'étais:

stuff = [1, 2, 3, 4]

def reverse(bla, y):
    for subset in itertools.combinations(bla, len(bla)-y):
        print list(subset)
    if y != len(bla):
        y += 1
        reverse(bla, y)

reverse(stuff, 1)
Expat C
la source
-1
flag = 0
requiredCals =12
from itertools import chain, combinations

def powerset(iterable):
    s = list(iterable)  # allows duplicate elements
    return chain.from_iterable(combinations(s, r) for r in range(len(s)+1))

stuff = [2,9,5,1,6]
for i, combo in enumerate(powerset(stuff), 1):
    if(len(combo)>0):
        #print(combo , sum(combo))
        if(sum(combo)== requiredCals):
            flag = 1
            break
if(flag==1):
    print('True')
else:
    print('else')
Priyansh gupta
la source