当前位置: 首页 > 知识库问答 >
问题:

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

齐俊贤
2023-03-14

假设我有一个从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])

输出:

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

如果你计算每个子集的总和,你会发现比率是44: 33 = 4: 3的,这正是输入值。但是,我希望该函数适用于任意数量的子集。比如说我希望

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))

我已经思考这个问题一个月了,我发现这非常困难。我的结论是这个问题只能通过递归来解决。我想知道是否有相对快速的算法。有什么建议吗?

共有1个答案

蓝华皓
2023-03-14

是的,需要递归。基本逻辑是将二分为一部分和其余部分,然后以所有可能的方式递归地分割其余部分。我遵循你的思路,假设一切事物都是可区分的,这就创造了很多可能性,可能太多了,无法列举。然而:

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)
 类似资料:
  • 如果有一个包含一组正数和负数的数组,请打印所有等于0的子集和。 我可以想出一种方法,在那里我可以制作givcen阵列的所有功率集,并检查它们的总和是否为0。但对我来说,这不像是优化的解决方案。 看完网上看起来有点类似的问题,好像可以用下面的程序动态编程来解决,看看是否有组合存在,让和11只是一个例子? 但是我不知道如何将上述程序扩展到 1)包括负数 2)找到使和为零的元素组合(上面的程序只是发现它

  • 问题内容: 我有绳子。我想通过更改字符串中的字符顺序来从该字符串生成所有排列。例如,说: 我想要的是这样的清单, 目前,我正在迭代字符串的列表强制转换,随机选择2个字母并将它们换位以形成新的字符串,然后将其添加到设置的l强制转换中。根据字符串的长度,我正在计算可能的排列数量,并继续迭代直到集合大小达到极限。必须有更好的方法来做到这一点。 问题答案: itertools模块具有一个有用的方法,称为p

  • 本文向大家介绍在Python中给定的嵌套列表中找到具有最大值的子列表,包括了在Python中给定的嵌套列表中找到具有最大值的子列表的使用技巧和注意事项,需要的朋友参考一下 列表可以包含其他列表作为其元素。在本文中,我们等于找到给定列表中存在的具有最大值的子列表。 带有max和lambda max和Lambda函数可以一起使用,以给出具有最大值的子列表。 示例 输出结果 运行上面的代码给我们以下结果

  • 问题内容: 我有从0到8的数字。我想结果是这些数字的所有可能集合,每个集合都应使用所有数字,每个数字在集合中只能出现一次。 我希望看到用PHP制作的解决方案可以打印出结果。或者,至少,我希望在组合理论上有所收获,因为我早已忘记了它。计算多少排列的公式是什么? 示例集: 0-1-2-3-4-5-6-7-8 0-1-2-3-4-5-6-8-7 0-1-2-3-4-5-8-6-7 0-1-2-3-4-8

  • 我一直在学习动态规划,我想通过打印出所有相加为一个数字的子集来进一步研究经典的子集和问题。我该怎么做呢?到目前为止,我知道如何根据是否存在相加的子集来打印true或false 如果可能,我想返回所有子集的数组。

  • 示例输入: 第二个数=数的个数,S(S<=12) S numbers=这些数字的值(每个值<100)。(重复可能发生,输入按非递增顺序给出) 我的工作是使用列表中加起来等于T的数字来找到所有“不同的和”。 等等。 我不确定这个算法是否有效 我在实现该算法方面遇到了困难。我不能把我的头缠在如何循环结构以便得到想要的组合上。