Это дает вам комбинации элементов, общая сумма которых меньше или равна 66, отсортированных по количеству элементов (по возрастанию) и по общей сумме (по убыванию).
Работает путем создания комбинаций с использованием простого счетчик, учитывая биты его двоичного представления. Бит, установленный в единицу, означает, что элемент группы присутствует в комбинации.
Примечание: он по-прежнему проходит все возможные комбинации, но с небольшим объемом памяти. Математически говоря, это ни в коем случае не лучшее из возможных решений.
from operator import itemgetter
def combins(group, max_len=66):
for n in range(1, 2**len(group)):
n_bin = bin(n)[:1:-1]
comb = tuple(l for l, c in zip(group, n_bin) if c == '1')
s = sum(comb)
if s <= max_len:
yield comb, len(comb), -s
group = (59, 5, 20, 20, 1, 7, 33, 22, 20)
sorted_subs = sorted(combins(group), key=itemgetter(1, 2))
clean_subs = [c for c, _, _ in sorted_subs]
print(clean_subs)
производит
[(59,), (33,), (22,), (20,), (20,), (20,), (7,), (5,), (1,), (59, 7), (59, 5), (59, 1), (33, 22), (20, 33), (20, 33), (33, 20), (20, 22), (20, 22), (22, 20), (20, 20), (7, 33), (20, 20), (20, 20), (5, 33), (1, 33), (7, 22), (20, 7), (20, 7), (5, 22), (7, 20), (5, 20), (5, 20), (5, 20), (1, 22), (20, 1), (20, 1), (1, 20), (5, 7), (1, 7), (5, 1), (59, 5, 1), (20, 20, 22), (7, 33, 22), (20, 22, 20), (20, 22, 20), (20, 7, 33), (20, 7, 33), (5, 33, 22), (20, 20, 20), (7, 33, 20), (5, 20, 33), (5, 20, 33), (5, 33, 20), (1, 33, 22), (20, 1, 33), (20, 1, 33), (1, 33, 20), (20, 7, 22), (20, 7, 22), (7, 22, 20), (20, 20, 7), (5, 20, 22), (5, 20, 22), (20, 7, 20), (20, 7, 20), (5, 22, 20), (5, 20, 20), (5, 7, 33), (5, 20, 20), (5, 20, 20), (20, 1, 22), (20, 1, 22), (1, 22, 20), (20, 20, 1), (1, 7, 33), (20, 1, 20), (20, 1, 20), (5, 1, 33), (5, 7, 22), (5, 20, 7), (5, 20, 7), (5, 7, 20), (1, 7, 22), (20, 1, 7), (20, 1, 7), (5, 1, 22), (1, 7, 20), (5, 20, 1), (5, 20, 1), (5, 1, 20), (5, 1, 7), (5, 20, 7, 33), (5, 20, 7, 33), (5, 20, 20, 20), (5, 7, 33, 20), (20, 20, 1, 22), (1, 7, 33, 22), (20, 1, 22, 20), (20, 1, 22, 20), (20, 1, 7, 33), (20, 1, 7, 33), (5, 1, 33, 22), (20, 20, 1, 20), (1, 7, 33, 20), (5, 20, 1, 33), (5, 20, 1, 33), (5, 1, 33, 20), (5, 20, 7, 22), (5, 20, 7, 22), (5, 7, 22, 20), (5, 20, 20, 7), (5, 20, 7, 20), (5, 20, 7, 20), (20, 1, 7, 22), (20, 1, 7, 22), (1, 7, 22, 20), (20, 20, 1, 7), (5, 20, 1, 22), (5, 20, 1, 22), (20, 1, 7, 20), (20, 1, 7, 20), (5, 1, 22, 20), (5, 20, 20, 1), (5, 1, 7, 33), (5, 20, 1, 20), (5, 20, 1, 20), (5, 1, 7, 22), (5, 20, 1, 7), (5, 20, 1, 7), (5, 1, 7, 20), (5, 20, 1, 7, 33), (5, 20, 1, 7, 33), (5, 20, 20, 1, 20), (5, 1, 7, 33, 20), (5, 20, 1, 7, 22), (5, 20, 1, 7, 22), (5, 1, 7, 22, 20), (5, 20, 20, 1, 7), (5, 20, 1, 7, 20), (5, 20, 1, 7, 20)]
Если вы хотите получить наилучшее соответствие для любого заданного размера, результаты можно сгруппировать по размер
from collections import defaultdict
from pprint import pprint
combs_by_size = defaultdict(list)
for comb, size, _ in sorted(combins(group), key=itemgetter(1, 2)):
combs_by_size[size].append(comb)
pprint(combs_by_size)
, который производит
defaultdict(<class 'list'>,
{1: [(59,), (33,), (22,), (20,), (20,), (20,), (7,), (5,), (1,)],
2: [(59, 7),
(59, 5),
(59, 1),
(33, 22),
(20, 33),
(20, 33),
(33, 20),
(20, 22),
(20, 22),
(22, 20),
(20, 20),
(7, 33),
(20, 20),
(20, 20),
(5, 33),
(1, 33),
(7, 22),
(20, 7),
(20, 7),
(5, 22),
(7, 20),
(5, 20),
(5, 20),
(5, 20),
(1, 22),
(20, 1),
(20, 1),
(1, 20),
(5, 7),
(1, 7),
(5, 1)],
3: [(59, 5, 1),
(20, 20, 22),
(7, 33, 22),
(20, 22, 20),
(20, 22, 20),
(20, 7, 33),
(20, 7, 33),
(5, 33, 22),
(20, 20, 20),
(7, 33, 20),
(5, 20, 33),
(5, 20, 33),
(5, 33, 20),
(1, 33, 22),
(20, 1, 33),
(20, 1, 33),
(1, 33, 20),
(20, 7, 22),
(20, 7, 22),
(7, 22, 20),
(20, 20, 7),
(5, 20, 22),
(5, 20, 22),
(20, 7, 20),
(20, 7, 20),
(5, 22, 20),
(5, 20, 20),
(5, 7, 33),
(5, 20, 20),
(5, 20, 20),
(20, 1, 22),
(20, 1, 22),
(1, 22, 20),
(20, 20, 1),
(1, 7, 33),
(20, 1, 20),
(20, 1, 20),
(5, 1, 33),
(5, 7, 22),
(5, 20, 7),
(5, 20, 7),
(5, 7, 20),
(1, 7, 22),
(20, 1, 7),
(20, 1, 7),
(5, 1, 22),
(1, 7, 20),
(5, 20, 1),
(5, 20, 1),
(5, 1, 20),
(5, 1, 7)],
4: [(5, 20, 7, 33),
(5, 20, 7, 33),
(5, 20, 20, 20),
(5, 7, 33, 20),
(20, 20, 1, 22),
(1, 7, 33, 22),
(20, 1, 22, 20),
(20, 1, 22, 20),
(20, 1, 7, 33),
(20, 1, 7, 33),
(5, 1, 33, 22),
(20, 20, 1, 20),
(1, 7, 33, 20),
(5, 20, 1, 33),
(5, 20, 1, 33),
(5, 1, 33, 20),
(5, 20, 7, 22),
(5, 20, 7, 22),
(5, 7, 22, 20),
(5, 20, 20, 7),
(5, 20, 7, 20),
(5, 20, 7, 20),
(20, 1, 7, 22),
(20, 1, 7, 22),
(1, 7, 22, 20),
(20, 20, 1, 7),
(5, 20, 1, 22),
(5, 20, 1, 22),
(20, 1, 7, 20),
(20, 1, 7, 20),
(5, 1, 22, 20),
(5, 20, 20, 1),
(5, 1, 7, 33),
(5, 20, 1, 20),
(5, 20, 1, 20),
(5, 1, 7, 22),
(5, 20, 1, 7),
(5, 20, 1, 7),
(5, 1, 7, 20)],
5: [(5, 20, 1, 7, 33),
(5, 20, 1, 7, 33),
(5, 20, 20, 1, 20),
(5, 1, 7, 33, 20),
(5, 20, 1, 7, 22),
(5, 20, 1, 7, 22),
(5, 1, 7, 22, 20),
(5, 20, 20, 1, 7),
(5, 20, 1, 7, 20),
(5, 20, 1, 7, 20)]})
Таким образом, получение первого элемента списка заданного размера дает вам наилучшее соответствие для такого размера, например
print(combs_by_size[3][0])
дает
(59, 5, 1)
Как вы могли заметить, выходные комбинации показывают значения только в том виде, в каком они есть, поэтому элементы группы нельзя отличить друг от друга, если они имеют одно и то же значение, например, число 20 кажется кратным раз в выборочной группе. Чтобы узнать, что это за 20, нам нужно добавить атрибут рядом со значением, возможно, в виде кортежа (filename, length)
вместо length
только как сейчас.