Прежде всего, должны быть созданы все возможные комбинации списка. Это простая проблема - благодаря itertools.combinations. Затем комбинации должны быть упорядочены в соответствии со следующими приоритетами: первый элемент исходного списка имеет наивысший приоритет, второй - второй приоритет и так далее. Обратите внимание, что любая группировка более низких приоритетов не может быть выше любого более высокого приоритета. Тривиальный пример:
input = ['A', 'B']
output = [['A', 'B'], ['A'], ['B']]
Пример из 3 элементов:
input = ['A', 'B', 'C']
output = [['A', 'B', 'C'], ['A', 'B'], ['A', 'C'], ['A'], ['B', 'C'], ['B'], ['C']]
Вопрос. Учитывая список любых элементов, как найти список всех возможных комбинаций, упорядоченных по приоритетам, как описано выше?
Добавление элемента в список всегда увеличивает приоритет. Мы можем вести список списков и рекурсивно добавлять худшие элементы, чтобы получить желаемый порядок.
cs = ["A", "B", "C"]
def subsets(xs):
res = [[]]
for x in xs[::-1]:
res = [[x] + r for r in res] + res
return res[:-1]
# [["A", "B", "C"], ["A", "B"], ["A", "C"], ["A"], ["B", "C"], ["B"], ["C"]]
Во время выполнения res
выглядит следующим образом:
[[]]
[["C"],[]]
[["B","C"],["B"],["C"],[]]
...
В качестве альтернативы вы можете положиться на itertools.product
чтобы получить ленивое решение.
from itertools import product
def lazy_subsets(xs):
for ix in product(*([[True, False]] * len(xs))):
yield [x for x, b in zip(xs, ix) if b]
res = list(lazy_subsets(cs))[:-1]
Здесь каждый ix
представляет собой список bool
который используется в качестве булевой маски для фильтрации xs
. Порядок, в котором product
дает ix
совпадает с порядком на подмножествах xs
который дает приоритеты исходным элементам.
Немного неуклюже, но это работает с использованием itertools.combinations...
import itertools
def iter(string):
raw_result = []
result = []
for i in range(len(string), -1, -1):
for y in itertools.combinations(string, i):
if len(y) > 0:
raw_result.append(list(y))
for s in string:
for bit in raw_result:
if s == bit[0]:
result.append(bit)
return result
print(iter('ABCD'))
Вы можете сортировать комбинации с помощью функции приоритета, которая определяется как отсортированный список исходных индексов элементов комбинации (нижний индекс означает более высокий приоритет), дополненный бесконечностями в конце (так как мы хотим дать комбинациям более высокой длины шанс против более низких длины):
from itertools import combinations
lst = ['A', 'B', 'C']
index = {l: i for i, l in enumerate(lst)}
def priority(c):
return sorted([index[x] for x in c]) + [float('inf')] * (len(lst) - len(c))
result = sorted([c for i in range(1, len(lst) + 1) for c in combinations(lst, i)], key=priority)
print(result)
# [('A', 'B', 'C'), ('A', 'B'), ('A', 'C'), ('A',), ('B', 'C'), ('B',), ('C',)]
Вы можете создать набор полномочий, а затем просто отсортировать каждый элемент в соответствии с вашим приоритетом:
from itertools import combinations, chain
class OrderedList:
indices = {}
def __init__(self, lst):
self.lst = lst
def __lt__(self, other):
lt = all(OrderedList.indices[s] <= OrderedList.indices[o] for s, o in zip(self.lst, other.lst)) and len(self.lst) >= len(other.lst)
return lt
def power_set(iterable):
s = list(iterable)
r = chain.from_iterable(combinations(s, r) for r in range(len(s) + 1))
return map(list, (e for e in r if e))
original = ['A', 'B', 'C']
OrderedList.indices = {e: i for i, e in enumerate(original)}
result = sorted(power_set(original), key=OrderedList)
print(result)
original = ['A', 'B']
result = sorted(power_set(original), key=OrderedList)
print(result)
Выход
[['A', 'B', 'C'], ['A', 'B'], ['A', 'C'], ['A'], ['B', 'C'], ['B'], ['C']]
[['A', 'B'], ['A'], ['B']]