685 votes

Obtenez toutes les combinaisons possibles (2^N) des éléments d'une liste, de n'importe quelle longueur

J'ai une liste de 15 nombres. Comment puis-je produire les 32 768 combinaisons de ces nombres (c'est-à-dire, n'importe quel nombre d'éléments, dans l'ordre original) ?

J'ai pensé à parcourir les entiers décimaux de 1 à 32768 et à utiliser la représentation binaire de chaque nombre comme filtre pour sélectionner les éléments de liste appropriés. Y a-t-il une meilleure façon de le faire ?


Pour les combinaisons <strong>d'une longueur spécifique</strong>, consultez <a href="https://stackoverflow.com/questions/27974126">Obtenir toutes les combinaisons de longueur n-choose-k de longueur n</a>. Veuillez utiliser cette question pour fermer les doublons si nécessaire.

Lorsque vous fermez des questions sur la combinatoire en tant que doublons, il est très important de s'assurer de ce que l'OP <strong>veut réellement, pas</strong> les termes qui ont été utilisés pour décrire le problème. Il est extrêmement courant que les personnes qui veulent, par exemple, un produit cartésien (voir <a href="https://stackoverflow.com/questions/533905">Comment obtenir le produit cartésien de plusieurs listes</a>) posent des questions sur les "combinaisons".

18 votes

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 compteront alors certaines sous-ensembles (par exemple 'abccc' -> ['', 'a', 'b', 'c', 'c', 'c', 'ac', 'ac', 'ac', ...]. Une solution de contournement simple est simplement de mettre tous les éléments dans un ensemble avant d'obtenir leurs permutations.

0 votes

@ninjagecko Utiliser la bibliothèque Set n'est pas efficace car chaque opération est de l'ordre de O(n) au mieux. Ainsi, ajouter n fonctions à un ensemble est en fait de l'ordre de O(n^2)!

3 votes

En lisant attentivement la question, il semble que l'OP demande le PowerSet de sa liste de 15 nombres, et non pas toutes les combinaisons. Je pense que c'est peut-être la raison pour laquelle les réponses sont si variées.

824voto

Dan H Points 1889

Cette réponse a omis un aspect : l'OP a demandé TOUTES les combinaisons ... pas seulement les combinaisons de longueur "r".

Donc, vous devriez soit parcourir toutes les longueurs "L":

import itertools

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

Ou - si vous voulez être élégant (ou faire travailler le cerveau de celui qui lira votre code après vous) - vous pouvez générer la chaîne des générateurs "combinations()", et itérer à travers ceux-ci :

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)

62 votes

Merci pour le soutien! Dans les semaines qui ont suivi la publication de la réponse ci-dessus, j'ai découvert que le NOM du concept que Ben recherche est le "powerset" de l'ensemble original des 15 éléments. En fait, un exemple d'implémentation est donné sur la page de documentation standard python "itertools": docs.python.org/library/itertools.html (recherchez "powerset").

65 votes

Pour ceux qui lisent jusqu'ici: La fonction générateur powerset() dans la section des recettes de la documentation itertools est plus simple, utilise potentiellement moins de mémoire et est probablement plus rapide que l'implémentation montrée ici.

0 votes

Est-il possible de générer toutes les combinaisons dans l'ordre lexicographique ?

680voto

James Brady Points 11646

Jetez un œil à itertools.combinations:

itertools.combinations(iterable, r)

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

Les combinaisons sont émises dans l'ordre de tri lexicographique. Donc, si l'itérable d'entrée est trié, les tuples de combinaison seront produits dans l'ordre trié.

Depuis 2.6, les piles sont incluses !

57 votes

Vous pouvez simplement tout lister. list(itertools.combinations(iterable, r))

26 votes

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.

5 votes

C'est très bien et m'a dirigé vers ce qui a vraiment résolu mon problème, qui était itertools.combination_with_replacement.

65voto

ninjagecko Points 25709

Voici une ligne paresseuse, 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 -- autant que le nombre de chaînes binaires de longueur N. Pour chaque chaîne binaire, vous sélectionnez 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 nécessite que vous puissiez appeler len(...) sur items (contournement: si items est quelque chose comme un itérable comme un générateur, transformez-le d'abord en liste avec items=list(_itemsArg))
  • Cela nécessite que l'ordre d'itération sur items ne soit pas aléatoire (contournement: ne soyez pas insensé)
  • Cela nécessite que les éléments soient uniques, sinon {2,2,1} et {2,1,1} se réduiront tous deux à {2,1} (contournement: utilisez collections.Counter comme un remplacement direct pour set; c'est essentiellement un multi-ensemble... bien que vous deviez peut-être ensuite utiliser tuple(sorted(Counter(...).elements())) si vous avez besoin qu'il soit hashable)

Démonstration

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

45voto

Mathieu Rodic Points 1889

Cette expression en une ligne vous donne toutes les combinaisons (entre 0 et n éléments si la liste/ensemble contient n é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)], [])

Le résultat 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

1 votes

Ceci est une permutation

21 votes

@AdHominem : non, ce n'est pas le cas. Il s'agit d'une liste de toutes les combinaisons. Les permutations incluraient, par exemple, ['b', 'a'].

1 votes

TypeError: ne peut concaténer qu'une liste (pas une "map") à une liste

22voto

Hongbo Zhu Points 1223

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

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

itérable = range(10)
for s in xrange(len(itérable)+1):
  for comb in itertools.combinations(itérable, s):
    yield comb

1 votes

Beau exemple. J'adore les générateurs... et j'adore Python pour les avoir ! Cet exemple a seulement un objet combinations() à la fois, et produit une des combinaisons à la fois. (Peut-être voulez-vous ajouter le bloc def autour de ceci -- comme exemple d'utilisation.) Notez que mon implémentation (avec chain(), donnée ci-dessus) n'est pas beaucoup pire : il est vrai qu'elle crée tous les générateurs len(iterable) à la fois... mais elle ne crée PAS toutes les combinaisons 2 ** len(iterable) à la fois, car -- à ma compréhension -- chain "utilise" le premier générateur avant de tirer des suivants.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X