如何生成15个总和等于特定数字的随机数

时间:2019-08-19 12:29:28

标签: python random

我有一个用于随机颜色选择器的代码,但没有一个随机数字。我需要它从5到20之间选择一个随机数15次,但问题是所有15个数字的总和必须等于227

我有一个用于随机颜色生成器的代码,但是我不知道如何生成数字以及如何获取总数227

import turtle
import random
turtle.bgcolor("black")
t = turtle.Pen()

t.width(4)
colours = ["yellow", "orange", "red"]
for i in range(60):
    t.pencolor(colours[random.randint(0,2)])
    t.forward(100)
    t.back(100)
    t.left(6)

5 个答案:

答案 0 :(得分:1)

让我们分析问题:

我们将生成14个数字并手动将15个数字相加以获得正确的总和。

但是我会改变@jfaccioni一次生成所有数字的方法,并更早地重新开始循环:如果我们生成5 5次,我们已经知道我们无法获得正确的和! (5 * 5 + 20 *(15-5)<227)

def rng_list():
    while True:
        rng_list = []
        current_sum = 0
        for i in range(14):
            r = random.randint(5, 20)
            rng_list.append(r)
            current_sum+= r
            if not 5*(14-i) <= 227-current_sum <= 20*(14-i): #`i` goes from 0 to 14, so 14-i is how many (from 15) numbers are still not calculated
                break
        if len(rng_list) == 14:
            rng_list.append(227-current_sum)
            return rng_list
        print("Pass failed with list {}, sum {} - trying again.".format(rng_list, current_sum)) #added to debug/show how it works

我添加了打印件以监视其在书写时的工作方式,您可以将其更改为计数器,以便重新滚动列表。但是现在,您也可以看到失败尝试的持续时间不同,因为我们每次都会监视当前的总和!

>>> rng_list()
Pass failed with list [12, 7, 18, 20, 12, 12, 15, 7, 15, 16, 7], sum 141 - trying again.
Pass failed with list [17, 6, 20, 5, 16, 18, 5, 19, 19, 7, 8], sum 140 - trying again.
Pass failed with list [10, 15, 18, 10, 8, 8, 12, 12, 13], sum 106 - trying again.
Pass failed with list [9, 12, 8, 5, 17, 20, 20, 6, 8], sum 105 - trying again.
Pass failed with list [9, 16, 9, 16, 6, 17, 20, 15, 9, 11, 15], sum 143 - trying again.
Pass failed with list [5, 14, 13, 12, 12, 13, 13, 9, 8], sum 99 - trying again.
Pass failed with list [11, 9, 5, 11, 11, 13, 18, 7], sum 85 - trying again.
Pass failed with list [10, 12, 19, 9, 14, 16, 11, 19, 5, 5], sum 120 - trying again.
Pass failed with list [11, 10, 8, 10, 10, 17, 17, 9, 19, 14], sum 125 - trying again.
Pass failed with list [13, 11, 7, 15, 14, 7, 5, 10], sum 82 - trying again.
Pass failed with list [11, 6, 7, 20, 6, 17, 18, 12, 8], sum 105 - trying again.
Pass failed with list [16, 17, 9, 18, 7, 8, 17, 14, 13, 13, 14], sum 146 - trying again.

[19, 6, 18, 9, 19, 20, 15, 14, 16, 15, 11, 18, 11, 20, 16]

>>> rng_list()
Pass failed with list [17, 6, 8, 9, 14, 17, 13, 8, 10], sum 102 - trying again.
Pass failed with list [7, 16, 12, 8, 20, 19, 18, 15, 5, 5], sum 125 - trying again.
Pass failed with list [5, 7, 6, 15, 12, 17, 6, 10], sum 78 - trying again.
Pass failed with list [17, 18, 8, 17, 18, 6, 10, 16, 18, 18, 6, 5], sum 157 - trying again.
Pass failed with list [10, 13, 9, 11, 11, 5, 18, 17, 13, 12], sum 119 - trying again.
Pass failed with list [20, 8, 8, 7, 14, 16, 17, 15, 15, 19, 13, 15, 17], sum 184 - trying again.
Pass failed with list [19, 12, 10, 15, 12, 13, 20, 14, 12, 6, 9], sum 142 - trying again.
Pass failed with list [9, 5, 13, 10, 15, 10, 13, 14, 7], sum 96 - trying again.
Pass failed with list [15, 12, 5, 19, 6, 5, 5, 17], sum 84 - trying again.
Pass failed with list [8, 5, 7, 11, 15, 16, 12, 18, 13], sum 105 - trying again.
Pass failed with list [15, 14, 10, 9, 8, 6, 10, 15, 18], sum 105 - trying again.
Pass failed with list [14, 17, 10, 13, 16, 8, 5, 6, 14], sum 103 - trying again.
Pass failed with list [10, 12, 19, 9, 5, 18, 12, 8, 9], sum 102 - trying again.
Pass failed with list [15, 10, 11, 19, 12, 12, 18, 15, 13, 8, 19, 11], sum 163 - trying again.
Pass failed with list [10, 20, 17, 11, 20, 11, 14, 13, 18, 5, 5], sum 144 - trying again.
Pass failed with list [20, 8, 11, 16, 18, 16, 15, 12, 9, 14, 15, 18, 13], sum 185 - trying again.
Pass failed with list [16, 7, 20, 11, 12, 16, 11, 9, 5, 13], sum 120 - trying again.
Pass failed with list [10, 12, 19, 9, 14, 15, 17, 19, 7, 11, 17, 17, 7], sum 174 - trying again.
Pass failed with list [5, 5, 6, 12, 10, 16, 10], sum 64 - trying again.
Pass failed with list [16, 18, 20, 14, 20, 19, 16, 7, 5, 12, 9, 11, 15], sum 182 - trying again.
Pass failed with list [14, 7, 13, 15, 16, 12, 20, 5, 5, 13], sum 120 - trying again.
Pass failed with list [17, 16, 9, 20, 13, 9, 9, 17, 19, 19, 7, 13, 18], sum 186 - trying again.
Pass failed with list [16, 11, 18, 17, 14, 16, 9, 10, 14, 19, 17, 6, 17], sum 184 - trying again.
Pass failed with list [12, 9, 9, 16, 10, 12, 18, 17, 16, 12, 18, 15], sum 164 - trying again.

[15, 14, 11, 14, 13, 13, 18, 19, 19, 13, 14, 6, 19, 16, 23]

>>> rng_list()

[13, 8, 15, 5, 17, 19, 14, 15, 17, 19, 20, 14, 17, 15, 19]

(在输出中添加了空行以提高可读性。)

答案 1 :(得分:1)

好吧,对于这类问题,multinomial distribution是正确的答案-根据定义,总和等于期望的数量。在Python中,这几乎是一个衬里

q = np.random.multinomial(227, [1/15.]*15)                                                                          
np.sum(q)  

将打印

227

然后您可以应用拒绝/接受技术:

if np.any(q < 5):
    # reject and start again

if np.any(q > 20):
    # reject and start again

# accept, do something with the sampled array

或以函数形式

import numpy as np

def sample(nof_samples, min, max, sum):
    p = np.full(nof_samples, 1.0/np.float64(nof_samples)) # probabilities

    while True:
        q = np.random.multinomial(sum, p)
        if not np.any(q > max):
            if not np.any(q < min):
                return q

t = sample(15, 5, 20, 227)
print(t)

t = sample(15, 5, 20, 227)
print(t)

t = sample(15, 5, 20, 227)
print(t)

答案 2 :(得分:1)

摆脱Severin Pappadeux的回答,您可以通过从minmax中减去5,使这个特定问题的值分别为0和15,从而消除对下限抑制的需要。这样会将目标sum减少了nof_samples * min。一旦合奏通过缩减的上限检查,就无需检查下限违规,因为它现在为0,并且所有结果均为正。我们只需要通过将min添加回所有值来转换这些结果,即可将其恢复到原始范围。

import numpy as np
import sys

def sample(nof_samples, min, max, sum):
    p = np.full(nof_samples, 1.0/np.float64(nof_samples)) # probabilities
    sum -= nof_samples * min
    max -= min
    if sum < 0 or sum > nof_samples * max:  # check that args have a feasible solutioon
        print('Inconceivable!')
        sys.exit()

    while True:
        q = np.random.multinomial(sum, p)
        if not np.any(q > max):
            return q + min

for _ in range(3):
    t = sample(15, 5, 20, 227)
    print(t)
    print(min(t), max(t), sum(t))  # confirm that all constraints have been met

请注意,此版本还可以使用负数作为总和和界限:

print(sample(5, -10, 20, -1))

产生例如

[ 0  1 -1  4 -5]

答案 3 :(得分:0)

如果您不在乎数字是否严格随机,则可以生成5到20之间的14个随机数字,请检查这些数字的总和是否足够接近所需的总数227 (这样,在5到20之间添加另一个数字将达到227)并添加相应的“缺失”数字。

以下功能可以做到这一点:

def rng_list(): 
    while True: 
        rng_list = [random.randint(5, 20) for _ in range(14)] 
        if 207 <= sum(rng_list) <= 222: 
            break 
    rng_list.append(227 - sum(rng_list))
    return rng_list

此函数并未真正优化,因为可能要花很多时间才能得出14个随机数的列表,这些和的总和位于正确的位置,但是对于您的用例而言,它应该足够快。

答案 4 :(得分:0)

您想要15个数字加起来等于227。227/15 = 15.133。因此,从[15,15,...,15,16,16,16]开始,总计为227(15 x 15 = 225)。

现在对数字对进行随机更改,以使a)总数保持不变,b)不会将数字压入5..20限制之外。

一种方法是随机选择2个数字,检查两个数字与极限的距离,然后使用该信息选择随机大小的变化。将大小更改添加到该对中的一个,然后从另一个中减去。然后选择另一对并重复。

ETA:不要随机选择该对中的两个成员。使用类似费舍尔·耶茨(Fisher-Yates)洗牌的方法,该对中的一个成员顺序地遍历列表,从而确保列表中的每个成员在遍历列表时至少被处理一次。