Python - 如何创建一个浮动列表,当它们的大小在特定范围内随机化并且它们的总和是预先确定的

Python - How to create a list of floats when their sizes randomized within a specific range and their sum is predetermined

正如标题中所写,我对一种生成长度为 'N 的浮点数列表的简单方法很感兴趣,其值范围从 0.1 到 0.25 但其总和将是固定的和预定义的

例子一:

N = 9,总和 = 1.575,列表 = (0.11, 0.24, 0.225, 0.16, 0.169, 0.233, 0.102, 0.2, 0.136)

示例 B:

N = 2,总和 = 0.35,列表 = (0.2, 0.15)

谢谢!

尝试这将提供从 0.1 到 0.25 的值范围列表,其长度为 N,总和如提供的那样

 import random
 def num_pieces(num):
    resp_list = []
    while True:
       rnd_num = random.uniform(0.1, 0.25)
       resp_list.append(rnd_num)
       if sum(resp_list) >= num:
          break

    return resp_list

  a = num_pieces(.9)

如果你想限制范围使用这个 round(random.uniform(1,2), N)

已更新

import random
def num_pieces(num):
  deci_points = len(str(num).split('.')[1])
  resp_list = []
  while True:
    rnd_num = round(random.uniform(0.1, 0.25),deci_points)
    resp_list.append(rnd_num)
    if sum(resp_list) >= num:
      break

return resp_list

人们很想使用 Dirichlet distribution,其中 return 是 rank 小于 1 的正实数的向量,其和被限制为等于 1.

但是,有一个问题:即使是线性尺度的狄利克雷分布{xi} --> {a*xi + b} 只有 2 个自由参数。我们的问题有 3:xMin = 0.1、xMax = 0.25、xSum = 1.575,以从您的第一个示例中获取值。所以我们没有明显的方法来直接实现 xi <= xMax 约束。

9 阶的合法单位 Dirichlet 变量可以由一个 1.0 值后跟 8 个零值组成。合法缩放的 Dirichlet 变量可以由单个值 0.775 和后面的 8 个值 0.1 组成,给出所需的总和值。所以我们有一种“赢家通吃”的可能性。

但是我们总是可以通过直接拒绝来实现 xMax 约束。根据确切的参数,拒绝的代价可能会很高,但至少我们的统计基础是可靠的。

我们只是计算缩放狄利克雷变量,直到我们得到一个符合 xi <= xMax 约束的变量。我们 return 那个给来电者。

这可以通过以下Python代码实现:

import numpy as np
import numpy.random as nprd

def getRandList(rng, rank, xMin, xMax, xSum):
    delta = xSum - rank*xMin
    allowedMaxShare = (xMax-xMin) / delta
    #### print("allowedMaxShare = %f" % allowedMaxShare)
    alphas = rank * [1.0]  #  uniform list
    rejectionCount = 0
    maxShare = 2.0  #  certainly above allowedMaxShare
    while (maxShare > allowedMaxShare):
        rejectionCount += 1
        v1 = rng.dirichlet(alphas)
        maxShare = max(v1)

    #### print("rejectionCount = %d" % rejectionCount)
    # scale the successful vector:
    v2 = list (map(lambda r: xMin + r*delta, v1))
    return v2

试用运行:

$ python3
Python 3.9.6 (default, Jul 16 2021, 00:00:00) 
>>> 
>>> import numpy as np
>>> import numpy.random as nprd
>>> from soq import getRandList
>>>
>>> rank=9
>>> xMin=0.1
>>> xMax=0.25
>>> xSum=1.575
>>> randomSeed=42
>>> g0 = nprd.default_rng(randomSeed)
>>> 
>>> v = getRandList(rg0, rank, xMin, xMax, xSum)
>>> 
>>> v
[0.14802819801539244, 0.12496501874018585, 0.19078664992587546, 0.18722819613343228, 0.22566529897386292, 0.14102174372855228, 0.20502851332070077, 0.24961569590423102, 0.10266068525776693]
>>> 
>>> sum(v)
1.575
>>> 
>>> min(v)
0.10266068525776693
>>> 
>>> max(v)
0.24961569590423102
>>> 

看起来不错。让我们再测试一下:

for _ in range(5):
    v = getRandList(rg0, rank, xMin, xMax, xSum)
    #### print("v = %s" % v)
    sv = sum(v) ; mx = max(v) ; mn = min(v)
    print("sum = %f  min=%f  max=%f" % (sv, mn, mx))

测试脚本输出:

sum = 1.575000  min=0.115170  max=0.248764
sum = 1.575000  min=0.113341  max=0.239323
sum = 1.575000  min=0.100097  max=0.228379
sum = 1.575000  min=0.101264  max=0.246103
sum = 1.575000  min=0.107969  max=0.238713

所以看起来基本正确。通过拥有一个专用的生成器对象,代码仍然可以进行一些优化,同时保留拒绝原则。该对象可以缓冲大量矢量尺度 Dirichlet 变量,并将原始生成器与计算常量(例如 allowedMaxShare.

一起托管