簡體   English   中英

快速 python 算法,用於從子集總和等於給定比率的數字列表中找到所有可能的分區

[英]Fast python algorithm to find all possible partitions from a list of numbers that has subset sums equal to given ratios

假設我有一個從 0 到 9 的 20 個隨機整數的列表。我想將該列表划分為N個子集,以便子集和的比率等於給定值,並且我想找到所有可能的分區。 我編寫了以下代碼並使其適用於N = 2的情況。

import random
import itertools

#lst = [random.randrange(10) for _ in range(20)]
lst = [2, 0, 1, 7, 2, 4, 9, 7, 6, 0, 5, 4, 7, 4, 5, 0, 4, 5, 2, 3]

def partition_sum_with_ratio(numbers, ratios):
    target1 = round(int(sum(numbers) * ratios[0] / (ratios[0] + ratios[1])))
    target2 = sum(numbers) - target1
    p1 = [seq for i in range(len(numbers), 0, -1) for seq in
          itertools.combinations(numbers, i) if sum(seq) == target1
          and sum([s for s in numbers if s not in seq]) == target2]

    p2 = [tuple(n for n in numbers if n not in seq) for seq in p1]

    return list(zip(p1, p2))

partitions = partition_sum_with_ratios(lst, ratios=[4, 3])
print(partitions[0])

Output:

((2, 0, 1, 2, 4, 6, 0, 5, 4, 4, 5, 0, 4, 5, 2), (7, 9, 7, 7, 3))

如果你計算每個子集的總和,你會發現比例是 44:33 = 4:3,這正是輸入值。 但是,我希望 function 適用於任意數量的子集。 例如,我期望

partition_sum_with_ratio(lst, ratios=[4, 3, 3])

返回類似的東西

((2, 0, 1, 2, 4, 6, 0, 5, 4, 4, 3), (5, 0, 4, 5, 2, 7), (9, 7, 7))

我已經考慮這個問題一個月了,我發現這非常困難。 我的結論是,這個問題只能通過遞歸來解決。 我想知道是否有任何相對快速的算法。 有什么建議么?

是的,需要遞歸。 基本邏輯是將 rest 分成一個部分,然后以所有可能的方式遞歸拆分 rest。 我按照你的說法假設一切都是可區分的,這創造了很多可能性,可能太多而無法列舉。 盡管如此:

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)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM