且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

快速python算法,从子集总和等于给定比率的数字列表中找到所有可能的分区

更新时间:2023-02-10 11:29:34

是的,需要递归.基本逻辑是将一个部分分为一部分和其余部分,然后以所有可能的方式递归拆分其余部分.我已经按照你的假设假设一切都是可区分的,这会产生很多可能性,可能太多而无法一一列举.尽管如此:

Yes, recursion is called for. The basic logic is to do a bipartition into one part and the rest and then recursively split the rest in all possible ways. I've followed your lead in assuming that everything is distinguishable, which creates a lot of possibilities, possibly too many to enumerate. Nevertheless:

import itertools


def totals_from_ratios(sum_numbers, ratios):
    sum_ratios = sum(ratios)
    totals = [(sum_numbers * ratio) // sum_ratios for ratio in ratios]
    residues = [(sum_numbers * ratio) % sum_ratios for ratio in ratios]
    for i in sorted(
        range(len(ratios)), key=lambda i: residues[i] * ratios[i], reverse=True
    )[: sum_numbers - sum(totals)]:
        totals[i] += 1
    return totals


def bipartitions(numbers, total):
    n = len(numbers)
    for k in range(n + 1):
        for combo in itertools.combinations(range(n), k):
            if sum(numbers[i] for i in combo) == total:
                set_combo = set(combo)
                yield sorted(numbers[i] for i in combo), sorted(
                    numbers[i] for i in range(n) if i not in set_combo
                )


def partitions_into_totals(numbers, totals):
    assert totals
    if len(totals) == 1:
        yield [numbers]
    else:
        for first, remaining_numbers in bipartitions(numbers, totals[0]):
            for rest in partitions_into_totals(remaining_numbers, totals[1:]):
                yield [first] + rest


def partitions_into_ratios(numbers, ratios):
    totals = totals_from_ratios(sum(numbers), ratios)
    yield from partitions_into_totals(numbers, totals)


lst = [2, 0, 1, 7, 2, 4, 9, 7, 6, 0, 5, 4, 7, 4, 5, 0, 4, 5, 2, 3]
for part in partitions_into_ratios(lst, [4, 3, 3]):
    print(part)