Декартово произведение разной величины

Я могу получить декартово произведение списков благодаря функции itertools.product():

lists = [['A', 'B'], ['1', '2'], ['x', 'y']]
combinations = itertools.product(*lists)
# [('A', '1', 'x'), ('A', '2', 'y'), ..., ('B', '2', 'y')]

Я хочу то же самое, но с разными размерами:

all_comb = magicfunction(lists)
# [('A', '1', 'x'), ..., ('B', '2', 'y'), ('A', '1'), ('A', '2'), ... ('2', 'y'), ... ('y')]

Я не вижу единственного очевидного способа сделать это.

Мне нужен метод, который позволил бы мне установить минимальный И максимальный размер кортежей (я имею дело с длинными списками и мне нужны только комбинации размеров от 7 до 3, количество списков и их размер варьируются).

Мои списки больше похожи на:

lists = [['A', 'B', 'C'], ['1', '2'], ['x', 'y', 'z', 'u'], ...] # size may go to a few dozens

person Sildar    schedule 06.03.2014    source источник


Ответы (2)


Просто соедините несколько продуктов вместе, основываясь на комбинациях меньшего размера:

from itertools import chain, product, combinations

def ranged_product(*lists, **start_stop):
    start, stop = start_stop.get('start', len(lists)), start_stop.get('stop', 0)
    return chain.from_iterable(product(*comb)
                               for size in xrange(start, stop - 1, -1)
                               for comb in combinations(lists, r=size))

Демо:

>>> lists = [['A', 'B'], ['1', '2'], ['x', 'y']]
>>> for prod in ranged_product(stop=2, *lists):
...     print prod
... 
('A', '1', 'x')
('A', '1', 'y')
('A', '2', 'x')
('A', '2', 'y')
('B', '1', 'x')
('B', '1', 'y')
('B', '2', 'x')
('B', '2', 'y')
('A', '1')
('A', '2')
('B', '1')
('B', '2')
('A', 'x')
('A', 'y')
('B', 'x')
('B', 'y')
('1', 'x')
('1', 'y')
('2', 'x')
('2', 'y')
person Martijn Pieters    schedule 06.03.2014
comment
Хотя я нахожу то, как вы работаете с запуском / остановкой, несколько странным, ваш ответ, по сути, приводит к тому же выводу: используйте второй параметр комбинаций. Ваш ответ предоставляет функцию, позволяющую легко установить минимальное и максимальное значения. Однако мне не нужно устанавливать разные значения min/max в зависимости от моих данных, и я могу справиться с этим, просто ограничив его min(maxsize, len(lists)) в вызове диапазона. Спасибо за ваш ответ и демонстрацию :) - person Sildar; 06.03.2014
comment
@sildar: обработка start и stop основана на том, как range() обрабатывает это, но в этом случае это может быть немного сложно. Я упрощу. - person Martijn Pieters; 06.03.2014

person    schedule
comment
Я неправильно понял назначение второго аргумента combinaisons() из-за того, что документация использует строки как итерации в своих примерах. Был очевидный способ сделать это. Спасибо. Думаю, я могу установить максимальный размер, просто ограничив диапазон. - person Sildar; 06.03.2014