Как получить все возможные комбинации элементов списка?

У меня есть список из 15 чисел, и мне нужно написать код, который производит все 32 768 комбинаций этих чисел.

Я нашел какой-то код (от Googling), который, по-видимому, делает то, что я ищу, но я нашел код довольно непрозрачным и опасаюсь его использовать. К тому же я чувствую, что должно быть более элегантное решение.

Единственное, что мне приходит в голову, - это просто перебрать десятичные целые числа 1–32768 и преобразовать их в двоичное, а затем использовать двоичное представление в качестве фильтра для выбора подходящих чисел.

Кто-нибудь знает способ лучше? Может быть, с использованием map()?

Читатели должны отметить, что то, являются ли элементы списка уникальный, является чрезвычайно важным соображением, так как многие алгоритмы затем будут пересчитывать некоторое подмножество (например, 'abccc' -> ['', 'a', 'b', 'c', 'c' , 'c', 'ac', 'ac', 'ac', ...]. Простой обходной путь - просто засунуть все элементы в набор перед, получив их перестановки.

ninjagecko 14.09.2015 03:23

@ninjagecko Использование библиотеки Set неэффективно, поскольку каждая из них в лучшем случае O (n). Таким образом, добавление n функций в набор на самом деле составляет O (n ^ 2)!

SMBiggs 11.02.2020 09:02

При внимательном чтении вопроса кажется, что OP запрашивает PowerSet своего списка из 15 номеров, а не всех комбинаций. Думаю, именно поэтому ответы можно найти повсюду.

SMBiggs 11.02.2020 09:53

@ Скотт Биггс: ты уверен, что говоришь здесь о Python? Установка вставок и поисков выполняется в среднем за O (1). Они как словари. Они используют хеширование. У Python нет специальной библиотеки наборов (она находится в стандартной библиотеке). Мы вставляем здесь числа, а не функции. (По-прежнему было бы неэффективно использовать память O (2 ^ n); правильным решением для людей, которым нужны комбинации, а не набор мощности, является простая рекурсивная реализация или product и т. д.)

ninjagecko 12.02.2020 12:36
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
504
4
809 303
28
Перейти к ответу Данный вопрос помечен как решенный

Ответы 28

Ответ принят как подходящий

Взгляните на itertools.combinations:

itertools.combinations(iterable, r)

Return r length subsequences of elements from the input iterable.

Combinations are emitted in lexicographic sort order. So, if the input iterable is sorted, the combination tuples will be produced in sorted order.

Начиная с версии 2.6, батарейки идут в комплекте!

вы можете просто перечислить все это. list(itertools.combinations(iterable, r))

silgon 14.09.2017 15:23

есть ли что-нибудь, что не требует r, то есть комбинации подпоследовательностей элементов любой длины.

mLstudent33 23.05.2020 08:39

это очень хорошо и указывало мне на то, что действительно решило мою проблему, а именно на itertools.combination_with_replacement.

user3348949 16.10.2020 02:39

Этот ответ упустил один аспект: OP запрашивал ВСЕ комбинации ... не только комбинации длины «r».

Таким образом, вам придется либо перебрать все длины "L":

import itertools

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

Или - если вы хотите выглядеть шикарно (или сломать мозг того, кто читает ваш код после вас) - вы можете сгенерировать цепочку генераторов «комбинаций ()» и перебирать ее:

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)

Спасибо за поддержку! За несколько недель, прошедших после того, как я отправил ответ выше, я обнаружил, что НАЗВАНИЕ концепции того, что ищет Бен, является «powerset» исходного набора из 15 предметов. Фактически, пример реализации приведен на стандартной странице документа python "itertools": docs.python.org/library/itertools.html (grep для "powerset").

Dan H 16.11.2011 21:45

Для тех, кто читает это далеко: функция генератора powerset() в разделе рецептов itertools документация проще, потенциально использует меньше памяти и, вероятно, быстрее, чем реализация, показанная здесь.

martineau 25.10.2016 20:16

Можно ли сгенерировать все комбинации в лексикографическом порядке?

guik 04.04.2018 13:24

@guik: Я на 99% уверен, что itertools.combinations сохраняет порядок элементов в списках, которые он выдает. Таким образом, если входные данные лексически отсортированы, то каждый из выходов также будет отсортирован.

Dan H 05.04.2018 15:22

Да, itertools.combinations генерирует комбинации k среди n в лексикографическом порядке, но не все комбинации вплоть до k среди n. powerset генерирует все комбинации вплоть до k, но не в лексикографическом порядке, насколько я понимаю: powerset ([1,2]) -> [(), (1,), (2,), (1, 2)] . Разве это не должно быть: [(), (1,), (1, 2), (2,)]?

guik 05.04.2018 16:28

@guik: Я почти уверен, что некоторые из рекурсивных решений здесь делают это. См., Например, авторство darxtrix. stackoverflow.com/a/23743696/701435

Dan H 07.04.2018 06:03

Есть ли простой способ исключить запятую после отдельных элементов, как в (1,)?

ENIAC-6 18.12.2020 21:25

@ ENIAC-6: именно так Python печатает кортежи с одним элементом. (Запятая отсутствует, пока вы не попытаетесь ее распечатать.) Итак, у вас есть варианты: 1: сначала преобразовать элемент в список: print(list(item)) или 2: использовать ",".join(items), чтобы избежать одноэлементных запятых.

Dan H 26.12.2020 04:47

Вот ленивый однострочник, также использующий 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)) )

Основная идея этого ответа: есть 2 ^ N комбинаций - столько же, сколько двоичных строк длины N. Для каждой двоичной строки вы выбираете все элементы, соответствующие «1».

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

На что следует обратить внимание:

  • Для этого необходимо, чтобы вы могли вызвать len(...) на items (обходной путь: если items является чем-то вроде итерабельного, например генератора, сначала превратите его в список с помощью items=list(_itemsArg))
  • Для этого требуется, чтобы порядок итераций на items не был случайным (обходной путь: не сходите с ума)
  • Для этого необходимо, чтобы элементы были уникальными, иначе {2,2,1} и {2,1,1} свернутся до {2,1} (обходной путь: используйте collections.Counter в качестве замены для set; это в основном мультимножество ... хотя вам может потребоваться позже использовать tuple(sorted(Counter(...).elements())), если вам нужно он должен быть хешируемым)

Демо

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

Использование понимания списка:

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 )

Результат будет:

['A', 'A']
['A', 'B']
['A', 'C']
['B', 'B']
['B', 'C']
['C', 'C']

Это предложение состоит в том, чтобы изменить последовательность строк для создания наборов?!?! Святая ворона .... И: это не возврат powerset, а что-то вроде комбинаций_with_replacement (). (См. docs.python.org/library/…)

Dan H 16.11.2011 22:00

Это действительно то же самое, что и комбинация_with_replacement (), но, по крайней мере, на моем компьютере это работает немного быстрее, чем itertools. Что сказать, мне нравится составление списков.

zmk 25.11.2011 03:16

Спасибо за ответ! А как насчет создания списка в сочетании с перевернутыми списками, такими как ['A', 'A'], ['A', 'B'], ['A', 'C'], ['B', 'A'], [ 'B', 'B'], ['B', 'C'], ['C', 'A'], ['C', 'B'] и ['C', 'C'], которые включают все?

Karyo 19.03.2015 13:55

Очень интересно, но мой питон не совсем разбирается в тонкостях. Есть ли что-то особенное в использовании listCombined в разных областях и в том, что цикл for находится в одной строке? Я безуспешно пытаюсь перенести это на Java.

SMBiggs 11.02.2020 09:47

Я согласен с Дэном Х. в том, что Бен действительно просил комбинации все. itertools.combinations() не дает всех комбинаций.

Другая проблема заключается в том, что если итерируемый ввод большой, возможно, лучше вернуть генератор вместо всего в списке:

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

Хороший пример. Я люблю генераторы ... и люблю Python за то, что они есть! В этом примере одновременно используется только один объект комбинаций (), и в каждый момент времени получается одна из комбинаций. (Возможно, вы захотите добавить вокруг этого блок def - в качестве примера использования.) Обратите внимание, что моя реализация (с цепочкой (), приведенной выше) не намного хуже: это правда, что она создает все len (итерируемые) генераторы в один раз ... но он НЕ создает сразу все 2 ** len (итерируемые) комбинации, поскольку, насколько я понимаю, цепочка "использует" первый генератор перед отрисовкой из последующих.

Dan H 16.11.2011 21:54

Вот пример с использованием рекурсии:

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

Можно ли изменить это так, чтобы он возвращал список списков вместо печати?

James Vickery 09.11.2017 05:14

@JamesVickery да, вы могли бы посмотреть, как создать список вне функции и добавить к нему, или (лучше) сделать функцию генератором, взгляните на ключевое слово yield :)

Dangercrow 12.11.2017 17:01
new_data = copy.copy(data) - эта строка, насколько я понимаю, лишняя, ни на что не влияет
Dmitriy Fialkovskiy 25.10.2018 17:27

Этот однострочный файл дает вам все комбинации (между элементами 0 и n, если исходный список / набор содержит отдельные элементы n) и использует собственный метод 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)], [])

Результатом будет:

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

Попробуйте онлайн:

http://ideone.com/COghfX

Это перестановка

AdHominem 28.10.2016 22:44

@AdHominem: нет, это не так. Это список всех комбинаций. Перестановки могут включать, например, ['b', 'a'].

naught101 06.12.2016 01:44
TypeError: can only concatenate list (not "map") to list
0x48piraj 06.02.2019 03:28

@ 0x48piraj: спасибо, что заметили, поэтому я отредактировал свой ответ!

Mathieu Rodic 07.02.2019 11:14

Вы можете сгенерировать все комбинации списка в Python, используя этот простой код:

import itertools

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

Результат будет:

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

Ошибка в этом коде: не возвращает пустой набор. Может означать xrange (0, ...), но не тестировал. редактировать: Я отредактировал ваш ответ, чтобы исправить это.

ninjagecko 14.09.2015 03:01

Этот код использует простой алгоритм с вложенными списками ...

# 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()

Кажется, этот код возвращает [listOfCombinations, listOfCombinationsGroupedBySize]. К сожалению, при запуске с демонстрационным вводом он дает 63 элемента, а не 64; похоже, отсутствует пустой набор (в данном случае пустая строка "").

ninjagecko 14.09.2015 02:58

Ниже приведен «стандартный рекурсивный ответ», похожий на другой аналогичный ответ https://stackoverflow.com/a/23743696/711085. (На самом деле нам не нужно беспокоиться о нехватке места в стеке, поскольку мы не сможем обработать все N! Перестановок.)

Он посещает каждый элемент по очереди и либо берет его, либо оставляет его (мы можем напрямую увидеть мощность 2 ^ N из этого алгоритма).

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

Демо:

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

Вот еще одно решение (однострочное), включающее использование функции itertools.combinations, но здесь мы используем понимание двойного списка (в отличие от цикла for или суммы):

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

Демо:

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

Как указано в документация

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 = list(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

Если я прав, это точный код, скопированный из документации Python [docs.python.org/3.6/library/itertools.html]. Если да, пожалуйста, сделайте ссылку на источник.

GabrielChu 18.12.2017 12:33

интересный подход

pelos 27.11.2018 01:47

@GabrielChu только что исправил. Формат тоже был неправильным.

Tiago Martins Peres 李大仁 28.10.2020 11:46

В комментариях под отвечать, получившим большое количество голосов от @Dan H, упоминается рецепт powerset() в itertools документация, включая рецепт Сам Дэн. тем не мение, пока никто в ответ не выложил. Поскольку это, вероятно, один из лучших, если не лучший подход к проблеме - и учитывая небольшая поддержка от другого комментатора, он показан ниже. Функция создает уникальные комбинации все элементов списка возможной длины каждый (включая те, которые содержат ноль и все элементы).

Примечание: Если цель несколько отличается, чтобы получить только комбинации уникальных элементов, измените строку s = list(iterable) на s = list(set(iterable)), чтобы исключить любые повторяющиеся элементы. Несмотря на это, тот факт, что iterable в конечном итоге превращается в list, означает, что он будет работать с генераторами (в отличие от некоторых других ответов).

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

Выход:

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)

Для чего в первую очередь нужно преобразование list()?

AMC 07.12.2019 07:47

@Alexander: Чтобы можно было определить длину итерации.

martineau 07.12.2019 11:37

Я подумал, что добавлю эту функцию для тех, кто ищет ответ, без импорта itertools или каких-либо других дополнительных библиотек.

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

Простое использование генератора доходности:

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

Вывод из приведенного выше примера использования:

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

Думаю, это очень изящное решение.

greentec 26.03.2019 10:59

Это моя реализация

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

Что ваша реализация решает лучше, чем предыдущие реализации, размещенные здесь.

user1767754 02.01.2018 01:47

Я знаю, что гораздо практичнее использовать itertools для получения комбинаций все, но вы может достигаете этого частично только с пониманием списка, если вы так захотите, если вы хотите кодировать много

Для комбинаций из двух пар:

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

А для комбинаций из трех пар это так просто:

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

Результат идентичен использованию 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)]

Без использования 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']))

Вот две реализации itertools.combinations

Тот, который возвращает список

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

Один возвращает генератор

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

Обратите внимание, что рекомендуется предоставить им вспомогательную функцию, потому что аргумент prepend является статическим и не изменяется при каждом вызове.

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

Это очень поверхностный случай, но лучше перестраховаться, чем потом сожалеть

Если кто-то ищет перевернутый список, как я:

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)

Как насчет этого ... использовала строку вместо списка, но то же самое ... строку можно рассматривать как список в 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)

Комбинация от 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))

Безitertools в Python 3 вы можете сделать что-то вроде этого:

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

где изначально carry = "".

Это подход, который можно легко перенести на все языки программирования, поддерживающие рекурсию (нет инструментов itertools, нет выхода, нет понимания списка):

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

Ах! Хорошая реализация. Я узнал HEAD = a [0], TAIL = a [1:] из Пролога. Или car = a [0], cdr = a [1:] из Лиспа. Интересно, можно ли здесь использовать мемоизацию ...

Javier Ruiz 14.06.2020 22:17

Правда. Нарезка списка - это O (k), где k - длина фрагмента. Я думаю, можно было бы ускорить это, выполнив поиск на карте, который сделает его O (1) во всех запусках, кроме первого. Однако обратите внимание, что на эту реализацию не следует ссылаться для оценки производительности. Для этого существуют лучшие реализации. Эта реализация предназначена только для простоты и переносимости на большинство других языков.

Jonathan R 15.06.2020 00:11
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')

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

выход

[
 ['A'],
 ['B'],
 ['C'],
 ['A', 'B'],
 ['A', 'C'],
 ['B', 'C'],
 ['A', 'B', 'C']
]

Импорт permutations не используется.

Alex Povel 18.09.2020 14:43

3 функции:

  1. список всех комбинаций из n элементов
  2. список всех комбинаций из n элементов, порядок которых не отличается
  3. все перестановки
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)

Мне это очень нравится!!! Спасибо!!! Однако комбинаторические функции Python немного странные. В математике функция «комбинаций» будет вариациями, а «комбинацияNoOrder» - фактически комбинациями. Думаю, это сбивает с толку людей, пришедших к Python из области математики, как это случилось со мной на этот раз. В любом случае, хорошее решение, большое спасибо за выигрыш!

Đumić Branislav 23.04.2020 12:39

Вы также можете использовать функцию powerset из отличного пакета more_itertools.

from more_itertools import powerset

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

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

Мы также можем проверить, что он соответствует требованиям OP.

from more_itertools import ilen

assert ilen(powerset(range(15))) == 32_768

Я опаздываю на вечеринку, но хотел бы поделиться решением, которое я нашел для той же проблемы: В частности, я искал последовательные комбинации, поэтому для «STAR» я хотел «STAR», «TA», «AR», но не «SR».

lst = [S, T, A, R]
lstCombos = []
for Length in range(0,len(lst)+1):
    for i in lst:
        lstCombos.append(lst[lst.index(i):lst.index(i)+Length])

Дубликаты могут быть отфильтрованы добавлением дополнительной строки if перед последней строкой:

lst = [S, T, A, R]
lstCombos = []
for Length in range(0,len(lst)+1):
    for i in lst:
         if not lst[lst.index(i):lst.index(i)+Length]) in lstCombos:
             lstCombos.append(lst[lst.index(i):lst.index(i)+Length])

Если по какой-то причине это возвращает пустые списки на выходе, что случилось со мной, я добавил:

for subList in lstCombos:
    if subList = '':
         lstCombos.remove(subList)

Другие вопросы по теме