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

时间:2015-08-09 22:42:09

标签: python

寻找快速创建L量的n位小数列表,其总和为1.每个数字应为> = 0.01

期望输出:

其中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

其中L = 200,n = 3

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

我无法想到实际解决方案的棘手部分是确保每个列表中没有零。当n达到大数时,这变得尤为困难。你如何相应地分配价值1的碎片?

4 个答案:

答案 0 :(得分:2)

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

如果获得任何0.0分,它会自动重复随机化,但这不太可能。在OP调整非零要求之前写入while循环,使其高于0.01。要解决此问题,您可以修改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)

编辑:上面产生了一个LxL矩阵用于缩放,这是一个内存效率低下的矩阵。在L = 10 ** 6之前它将是OOM。我们可以通过使用this answer

建议的广播规范化程序来解决这个问题
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

第二个版本将在AMD FX-8150(16GB内存)的1/3秒内计算出100万个10号的列表:

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

答案 1 :(得分:1)

以下是您获得n个数字的方法:在您选择的任意范围内选择n个随机数(例如,从1到10),然后将它们全部除以总和。

答案 2 :(得分:1)

这应该可以解决问题:

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)

答案 3 :(得分:1)

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

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