【问题标题】:devide int into lower whole ints将 int 划分为较低的整数
【发布时间】:2016-10-24 12:15:53
【问题描述】:

我有一个 30-60 范围内的随机整数,我使用 randint(30,60) 获得。假设它是 40。我想将这个数字分成 7 个随机整数。因此,例如 [5,5,5,5,5,5,10] 是一个有效的结果。但是有很多可能的解决方案,比如这个[6,6,6,6,6,6,4][4,2,9,13,8,1,3] ... 我知道有很多解决方案,但我正在寻找一种快速的方法来解决它们。我并不是想获得每一个解决方案,而是寻找一种快速的方法来在短时间内迭代其中的很多。实现它的一种方法是随机选择一个数字(假设在 1-15 的范围内)并将其保存到一个列表中,然后执行一个 while 循环,直到总和正好为 40。我试过了,但效率不高全部。我认为选择[5,5,5,5,5,5,10] 之类的起始值并以"1st digit -2"3rd +2 之类的精确方式更改数字以产生[3,5,7,5,5,5,10] 将是一个更快的解决方案。有谁知道如何做到这一点或有一个好的建议?谢谢。我更喜欢python 3。

【问题讨论】:

  • 您说您“不是试图获得每一个解决方案”,但仍在“在短时间内迭代很多解决方案”。如果不是全部,你需要通过多少?你认为什么是短时间?当你找到这样的组合时,你究竟在用这些数据做什么?了解这些问题的答案将有助于缩小潜在解决方案的范围。

标签: python python-3.x


【解决方案1】:

一组整数和一个数n 称为partitionn;如果顺序很重要,那么它被称为组合。

这是一种产生随机组合的相当快速的方法。

import random

def random_partition(n, size):
    seq = []
    while size > 1:
        x = random.randint(1, 1 + n - size)
        seq.append(x)
        n -= x
        size -= 1
    seq.append(n)
    return seq

n = 40 
for _ in range(20):
    print(random_partition(n, 7))

典型输出

[26, 2, 8, 1, 1, 1, 1]
[30, 2, 1, 3, 1, 1, 2]
[26, 5, 3, 1, 2, 2, 1]
[2, 25, 9, 1, 1, 1, 1]
[28, 2, 2, 2, 1, 2, 3]
[23, 1, 9, 3, 2, 1, 1]
[3, 26, 1, 7, 1, 1, 1]
[25, 1, 7, 1, 2, 1, 3]
[10, 8, 11, 5, 3, 1, 2]
[19, 16, 1, 1, 1, 1, 1]
[12, 23, 1, 1, 1, 1, 1]
[1, 14, 15, 7, 1, 1, 1]
[29, 5, 1, 1, 2, 1, 1]
[25, 1, 3, 3, 1, 2, 5]
[10, 12, 10, 4, 1, 2, 1]
[13, 4, 6, 14, 1, 1, 1]
[31, 3, 1, 1, 1, 1, 2]
[16, 11, 9, 1, 1, 1, 1]
[3, 26, 5, 3, 1, 1, 1]
[31, 2, 1, 2, 2, 1, 1]

我们使用1 + n - size 作为上限,因为其他size - 1 数字至少为1。

这是一种相当有效的方法来生成给定整数的所有分区。请注意,这些是有序的;如果你想从这些分区产生随机组合,你可以使用random.shuffle

我们首先打印大小为 5 的 16 个分区,然后我们计算大小为 7 的 40 个分区的数量 (= 2738)。

此代码源自Jerome Kelleher 的算法。

def partitionR(num, size):
    a = [0, num] + [0] * (num - 1)
    size -= 1
    k = 1
    while k > 0:
        x = a[k - 1] + 1
        y = a[k] - 1
        k -= 1
        while x <= y and k < size:
            a[k] = x
            y -= x
            k += 1
        a[k] = x + y
        if k == size:
            yield a[:k + 1]

for u in partitionR(16, 5):
    print(u)

print('- ' * 32)
print(sum(1 for _ in partitionR(40, 7)))

输出

[1, 1, 1, 1, 12]
[1, 1, 1, 2, 11]
[1, 1, 1, 3, 10]
[1, 1, 1, 4, 9]
[1, 1, 1, 5, 8]
[1, 1, 1, 6, 7]
[1, 1, 2, 2, 10]
[1, 1, 2, 3, 9]
[1, 1, 2, 4, 8]
[1, 1, 2, 5, 7]
[1, 1, 2, 6, 6]
[1, 1, 3, 3, 8]
[1, 1, 3, 4, 7]
[1, 1, 3, 5, 6]
[1, 1, 4, 4, 6]
[1, 1, 4, 5, 5]
[1, 2, 2, 2, 9]
[1, 2, 2, 3, 8]
[1, 2, 2, 4, 7]
[1, 2, 2, 5, 6]
[1, 2, 3, 3, 7]
[1, 2, 3, 4, 6]
[1, 2, 3, 5, 5]
[1, 2, 4, 4, 5]
[1, 3, 3, 3, 6]
[1, 3, 3, 4, 5]
[1, 3, 4, 4, 4]
[2, 2, 2, 2, 8]
[2, 2, 2, 3, 7]
[2, 2, 2, 4, 6]
[2, 2, 2, 5, 5]
[2, 2, 3, 3, 6]
[2, 2, 3, 4, 5]
[2, 2, 4, 4, 4]
[2, 3, 3, 3, 5]
[2, 3, 3, 4, 4]
[3, 3, 3, 3, 4]
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
2738

【讨论】:

  • FWIW,如果您有兴趣为某个数字生成所有给定大小的组合,请参阅我写的 this answer
【解决方案2】:

如果您只关心获得一组任意数字,这些数字加起来等于您的总数,而不是对 所有 组合进行详尽的迭代,那么以下内容应该会让您得到您想要的需要。

def get_parts(total, num_parts=7, max_part=15):
    running_total = 0
    for i in range(num_parts - 1):
        remaining_total = total - running_total
        upper_limit = min(max_part, remaining_total - num_parts + 1 + i)
        # need to make sure there will be enough left
        lower_limit = max(1, remaining_total - max_part*(num_parts - i - 1))
        part = randint(lower_limit, upper_limit)
        running_total += part
        yield part
    yield total - running_total

>>> list(get_parts(40))
[2, 7, 10, 11, 1, 4, 5]

>>> list(get_parts(40))
[7, 13, 11, 6, 1, 1, 1]

>>> list(get_parts(50, 4))
[6, 14, 15, 15]

当然,上面每个列表中的项目并不是真正随机的,并且列表中前面的数字较大,后面的数字较小。如果你想要更多的伪随机元素,你可以通过 random.shuffle() 提供这些列表。

【讨论】:

  • 生成随机数(相对而言)真的很慢。
  • 在您的讨论之间跳来跳去,但与什么相比却很慢?如果他只需要一个任意集合,就像这个答案旨在做的那样,使用这个生成器比迭代所有可能性然后选择其中一个(就像在你的解决方案中一样)要快得多。
  • @TeemuRisikko OP 希望在“短时间内”迭代“很多”可能性。在我的解决方案中使用循环中的 timeit,在我的机器上生成所有 40 亿次迭代需要 1.3 秒。用比利的方法生成大约 1/1000 需要 73 秒。此外,修改我的循环以随时停止、将其转换为生成器等也非常简单。
  • @glibdud 是的,当然。我认为你只是以不同的方式解释这个问题。如果 OP 在短时间内确实需要“很多”值,那么随机化它不是最好的解决方案。可能是您的解决方案更接近 OP 想要的。
  • @glibdud 因此我的回答中的修饰符。 OP 明确表示“我并没有试图获得每一个解决方案。”
【解决方案3】:

来自Python Integer Partitioning with given k partitions

def partitionfunc(n,k,l=1):
    '''n is the integer to partition, k is the length of partitions, l is the min partition element size'''
    if k < 1:
        raise StopIteration
    if k == 1:
        if n >= l:
            yield (n,)
        raise StopIteration
    for i in range(l,n//k+1):
        for result in partitionfunc(n-i,k-1,i):
            yield (i,)+result
list(partitionfunc(40,7))

【讨论】:

  • PM 2Ring 解决方案更有效。
【解决方案4】:

您可以对前 6 个值(总和不超过 40)的所有可能组合进行简单迭代,并计算第 7 个值。

for a in range(41):
    for b in range(41-a):
        for c in range(41-(a+b)):
            for d in range(41-(a+b+c)):
                for e in range(41-(a+b+c+d)):
                    for f in range(41-(a+b+c+d+e)):
                        g = 40 - (a+b+c+d+e+f)
                        # Do what you need to do here

通过预先计算总和,您可以将循环所需的时间几乎减少一半(根据使用 timeit 的测试):

for a in range(41):
    for b in range(41-a):
        ab = a + b
        for c in range(41-ab):
            abc = ab + c
            for d in range(41-abc):
                abcd = abc + d
                for e in range(41-abcd):
                    abcde = abcd + e
                    for f in range(41-abcde):
                        g = 40 - (abcde + f)
                        # Do what you need to do here

【讨论】:

  • 当零件数量从 7 变为 10 时会发生什么? 20? 2?此解决方案不能很好地响应规范的变化。
  • @HaiVu 我提供了一种算法来回答所提出的问题。参数化它以适应其他情况对于新的 Python 程序员来说应该是微不足道的。
  • 那么,如果parts数量变为40,那么你会有近40个嵌套for循环?我说的是零件的数量,而不是总数,所以参数化不是小事。
  • @HaiVu 是的,如果你想要 40 个零件,这个方法需要 40 个循环。这可能很乏味,但它仍然是微不足道的。 (不,我认为这不是那种特定情况下的最佳方法。它不打算这样做。)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-04
  • 2023-03-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多