Как получить все возможные комбинации элементов списка?
У меня есть список с 15 номерами, и мне нужно написать некоторый код, который производит все 32 768 комбинаций этих чисел.
Я нашел код (по Googling) это, по-видимому, делает то, что я ищу, но я нашел код довольно непрозрачным и опасаюсь его использовать. Кроме того, у меня есть ощущение, что должно быть более элегантное решение.
единственное, что приходит мне в голову, это просто перебрать десятичные целые числа 1-32768 и преобразовать их в двоичный, и использовать двоичное представление в качестве фильтра, чтобы выбрать соответствующие числа.
кто-нибудь знает лучший способ? Используя map(), может быть?
22 ответов:
посмотреть itertools.комбинации:
itertools.combinations(iterable, r)возвращает подпоследовательности длины R элементов из вход итерационный.
комбинации выделяются в лексикографическом порядке сортировки. Итак, если входной итератор занимает, в комбинированные Кортежи будут производиться в порядок сортировки.
С 2.6, батареи включены!
ответ пропустил один аспект: ОП попросил все комбинации... не только комбинации длины "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)
вот ленивый однострочный, также используя 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(...)onitems(еслиitemsэто что-то вроде итерационного типа генератора, сначала превратите его в список сitems=list(_itemsArg))- это требует, чтобы порядок итерации на
itemsне случайно (обходной путь: не будьте безумны)- это требует, чтобы элементы были уникальными, иначе
{2,2,1}и{2,1,1}оба рухнут в{2,1}(используйтеcollections.Counterв качестве замены дляset, это мультимножество... хотя вам может понадобиться позже использоватьtuple(sorted(Counter(...).elements()))если вам это нужно, чтобы быть hashable)
демо
>>> 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'}]
в комментариях под высоко поднятой ответ by @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)
вот один с помощью рекурсии:
>>> 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']
Я согласен с Дэном х, что Бен действительно просил все комбинаций.
itertools.combinations()не дает всех комбинаций.еще одна проблема заключается в том, что если входной iterable большой, возможно, лучше вернуть генератор вместо всего в списке:
iterable = range(10) for s in xrange(len(iterable)+1): for comb in itertools.combinations(iterable, s): yield comb
этот ОДН-вкладыш дает вам все комбинации (между
0иnэлементы, если исходный список/набор содержитnотдельные элементы) и использует собственный методitertools.combinations:from itertools import combinations input = ['a', 'b', 'c', 'd'] output = sum([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']]
попробовать его в интернете:
вы можете генерировать все комбинации списка в 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)]
Я думал, что добавлю эту функцию для тех, кто ищет ответ, не импортируя 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] ,
вот еще одно решение (однострочное), включающее использование
itertools.combinationsфункция, но здесь мы используем понимание двойного списка (в отличие от цикла for или sum):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)]
Ниже приведен "стандартный рекурсивный ответ", аналогичный другому аналогичному ответу 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, чтобы получить все комбинации, а вы можете достигните этого частично только с пониманием списка, если вы так хотите, предоставьте вам код большое
для комбинаций из двух пар:
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.сочетания: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
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))спасибо
используя список осмысления:
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']
этот код использует простой алгоритм с вложенными списками...
# 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()
Это моя реализация
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
вот две реализации
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)
Это может быть сделано с помощью itertools
для перестановки
этот метод принимает список в качестве входных данных и возвращает список объектов кортежей, которые содержат перестановку длины L в виде списка.
# A Python program to print all # permutations of given length from itertools import permutations # Get all permutations of length 2 # and length 2 perm = permutations([1, 2, 3], 2) # Print the obtained permutations for i in list(perm): print (i)В Сочетании
этот метод принимает список и вход r в качестве входных данных и возвращает список объектов кортежей, которые содержат все возможные комбинации длины r в форме списка.
# A Python program to print all # combinations of given length from itertools import combinations # Get all combinations of [1, 2, 3] # and length 2 comb = combinations([1, 2, 3], 2) # Print the obtained combinations for i in list(comb): print (i)посмотреть этой
Как насчет этого.. использовал строку вместо списка, но то же самое.. строка может рассматриваться как список в 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)
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
Comments