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