如何创建L个包含n个非零随机小数的列表,其中每个列表的和为1.0



寻找一种快速的方法来创建n个和为1的小数的L个列表。每个数字应>= 0.01

所需输出:

where L = 200, n = 6

[0.20, 0.22, 0.10, 0.06, 0.04, 0.38]
[0.32, 0.23, 0.18, 0.07, 0.05, 0.15]
...
# There are 200 of these

where L = 200, n = 3

[0.90, 0.10, 0.10]
[0.35, 0.25, 0.30]
...
# There are also 200 of these

我想不出一个实际解决方案的棘手部分是确保每个列表中没有零。当n达到很大的数值时,这变得特别困难。如何相应地分配值1的片段呢?

这应该是非常快的,因为它使用numpy。

如果得到任何0.0,它将自动重复随机化,但这不太可能。在OP将非零要求调整为高于0.01之前编写while循环。要解决这个问题,您可以修改while块,使其包含整个后续代码,并在末尾以类似于检测零的方式计算违反任何所需约束的次数。但是当L比违反约束的概率大时,它可能会变慢。从某种意义上说,符合>0.0的原始要求是最容易的。

while循环后,L x n矩阵的每个元素均匀分布在(0.0,1.0)上,不存在0和1。对每一行求和,形成一个比例矩阵,然后矩阵乘以随机矩阵,得到自动和为1.0的行

 import numpy as np
 def random_proportions(L,n):
      zeros = 1
      while zeros>0:
          x = np.random.random(size=(L,n))
          zeros = np.sum(x==0.0)
      sums = x.sum(axis=1)
      scale = np.diag(1.0/sums)
      return np.dot(scale, x)

EDIT:上面的代码生成一个LxL矩阵用于缩放,这是内存效率低下的。它将在L=10**6之前OOM。我们可以通过使用这个答案

建议的广播规范化过程来解决这个问题。
import numpy as np
def random_proportions(L,n):
      zeros = 1
      while zeros>0:
          x = np.random.random(size=(L,n))
          zeros = np.sum(x==0.0)
      sums = x.sum(axis=1).reshape(L,1) # reshape for "broadcasting" effect
      return x/sums

这第二个版本将在1/3秒内计算100万个大小为10的列表,AMD FX-8150具有16GB ram:

%timeit l = random_proportions(1000000,10)
1 loops, best of 3: 347 ms per loop

如何得到加起来为1的n数字:在您选择的任意范围内(例如,从1到10)选择n随机数,然后将它们全部除以它们的总和。

这应该能奏效:

import random

def floatPartition(n, total):
    answer = []
    for _ in range(n-1):
        num = random.uniform(0, total)
        answer.append(num)
        total -= num
    answer.append(total)
    return answer

def paritions(n,L):
    return [floatPartition(n, 1) for _ in range(L)]

if __name__ == "__main__":
    answer = paritions(6,200)

我没有检查其他算法的速度,但是这个算法在20秒内产生1,000,000个长度为10的列表,其中元素为0.01 - 0.99,增量为0.01:

import random
def rand_list(n):
    sub_list = []
    max_val  = 100 - n + 1  # max needs to be n-1 less than 100
    for repetition in xrange(n-1):
        sub_list += [random.randrange(1, max_val)]
        max_val  -= (sub_list[-1] - 1)  # decrease the range by the latest element added - 1
    sub_list += [max_val]  # use the remainder for the last value, this way it adds to 100
    return [round(x/100.0, 2) for x in sub_list]  # convert to 0.01 - 0.99 with list comprehension

最新更新