web-dev-qa-db-fra.com

Faire toutes les combinaisons possibles d'une liste

J'ai besoin de pouvoir faire une liste qui contient toutes les combinaisons possibles d'une liste entrée. Par exemple, la liste [1,2,3] devrait renvoyer [1 [1,2] [1,3] 2 [2,3] 3 [1,2,3]] La liste ne doit pas être dans un ordre particulier. Sur ce site, j'ai trouvé beaucoup de fonctions utilisant le itertools mais celles-ci retournent des objets quand j'ai besoin juste d'un list.

43
Deano

Utilisez simplement itertools.combinations . Par exemple:

import itertools

lst = [1, 2, 3]
combs = []

for i in xrange(1, len(lst)+1):
    combs.append(i)
    els = [list(x) for x in itertools.combinations(lst, i)]
    combs.append(els)

Maintenant, combs contient cette valeur:

[1, [[1], [2], [3]], 2, [[1, 2], [1, 3], [2, 3]], 3, [[1, 2, 3]]]

Oui, il est légèrement différent de l'exemple de sortie que vous avez fourni, mais dans cette sortie, vous ne répertoriez pas toutes les combinaisons possibles.

Je liste la taille de la combinaison avant la liste réelle de chaque taille, si ce dont vous avez besoin est simplement les combinaisons (sans la taille, car elle apparaît dans votre exemple de sortie), puis essayez ces autres versions du code:

import itertools

lst = [1, 2, 3]
combs = []

for i in xrange(1, len(lst)+1):
    els = [list(x) for x in itertools.combinations(lst, i)]
    combs.extend(els)

Maintenant, combs contient cette valeur:

[[1], [2], [3], [1, 2], [1, 3], [2, 3], [1, 2, 3]]
52
Óscar López

Le module itertools renvoie en effet générateurs au lieu de listes, mais:

  • Les générateurs sont souvent plus efficaces que les listes (surtout si vous générez un grand nombre de combinaisons)
  • Vous pouvez toujours convertir des générateurs en listes en utilisant list(...) quand vous en avez vraiment besoin.

Les fonctions chain et combinations de itertools fonctionnent bien, mais vous devez utiliser Python 2.6 ou supérieur :

import itertools

def all_combinations(any_list):
    return itertools.chain.from_iterable(
        itertools.combinations(any_list, i + 1)
        for i in xrange(len(any_list)))

Vous pouvez alors appeler cela comme tel:

# as a generator
all_combinations([1,2,3])  # --> <itertools.chain at 0x10ef7ce10>

# as a list
list(all_combinations([1,2,3]))  # --> [(1,), (2,), (3,), (1, 2), (1, 3), (2, 3), (1, 2, 3)]

# as a list of lists
[list(l) for l in all_combinations([1,2,3])]  # --> [[1], [2], [3], [1, 2], [1, 3], [2, 3], [1, 2, 3]]

Si vous n'avez jamais utilisé de générateurs auparavant, notez que vous les parcourez comme s'ils étaient une liste, comme celle-ci:

# a generator returned instead of list
my_combinations = all_combinations([1,2,3])

# this would also work if `my_combinations` were a list
for c in my_combinations:
    print "Combo", c

"""
Prints:
  Combo (1,)
  Combo (2,)
  Combo (3,)
  Combo (1, 2)
  Combo (1, 3)
  Combo (2, 3)
  Combo (1, 2, 3)
"""

La différence de performances peut être dramatique. Si vous comparez les performances, vous verrez que le générateur est beaucoup plus rapide à créer:

# as a generator
all_combinations(range(25))  # timing: 100000 loops, best of 3: 2.53 µs per loop

# as a list
list(all_combinations(range(25)))  # timing: 1 loops, best of 3: 9.37 s per loop

Notez qu'il faudrait encore un certain temps pour parcourir toutes les combinaisons dans les deux cas, mais cela peut être une grande victoire pour vous, surtout si vous trouvez ce que vous recherchez au début.

11
Arel

Les fonctions du module itertools renvoient des itérateurs. Tout ce que vous devez faire pour les convertir en listes est d'appeler list() sur le résultat.

Cependant, puisque vous devrez appeler itertools.combinations trois fois distinctes (une fois pour chaque longueur différente), vous pouvez simplement utiliser list.extend pour ajouter tous les éléments de l'itérateur à votre liste finale.

Essayez ce qui suit:

import itertools
in_list = [1, 2, 3]
out_list = []
for i in range(1, len(in_list)+1):
    out_list.extend(itertools.combinations(in_list, i))

Ou comme compréhension de liste:

out_list = [c for i in range(len(in_list)) for c in itertools.combinations(in_list, i+1)]

Il en résultera la liste suivante:

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

Si vous voulez des listes au lieu de tuples, et pour convertir les tuples de longueur unique en juste la valeur, vous pouvez faire ce qui suit:

out_list = [x[0] if len(x) == 1 else list(x) for x in out_list]
# [1, 2, 3, [1, 2], [1, 3], [2, 3], [1, 2, 3]]

Ou pour laisser les éléments individuels sous forme de listes:

out_list = map(list, out_list)
6
Andrew Clark

Vous pouvez résoudre votre problème en utilisant itertools.combinations À l'intérieur d'une boucle:

>>> l = [1,2,3]
>>> comb = []
>>> for i in range(len(l)):
...   comb += itertools.combinations(l,i+1)
... 
>>> comb
[(1,), (2,), (3,), (1, 2), (1, 3), (2, 3), (1, 2, 3)]

Et si vous les voulez comme liste:

>>> comb_list = [ list(t) for t in comb ]
>>> comb_list
[[1], [2], [3], [1, 2], [1, 3], [2, 3], [1, 2, 3]]

EDIT: Le premier paramètre des combinaisons est l'itérable et le second est la longueur des tuples résultants (dans ce cas, en passant de 1 à len(l)).

En savoir plus sur les combinaisons: http://docs.python.org/library/itertools.html#itertools.combinations

6
juliomalegria
l = [1,2,3]
combs = reduce(lambda x, y: list(itertools.combinations(l, y)) + x, range(len(l)+1), [])

Si vous voulez un oneliner.

3

Je pense que cela vaut la peine de résumer les autres réponses ici dans un simple Python 3 exemple:

from itertools import chain, combinations

def all_combinations(array):
    return chain(*(list(combinations(array, i + 1)) for i in range(len(array))))

Cela renvoie un itérable, pour afficher les valeurs:

>>> print(list(all_combinations((1, 2, 3))))
[(1,), (2,), (3,), (1, 2), (1, 3), (2, 3), (1, 2, 3)]
0
Ninjakannon