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

Posted

技术标签:

【中文标题】如何创建包含 n 个非零随机小数的 L 个列表,其中每个列表的总和为 1.0?【英文标题】:How to create L lists of n non-zero random decimals where each list sums to 1.0? 【发布时间】:2015-11-01 18:38:18 【问题描述】:

寻找一种快速的方法来创建 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 的碎片如何相应地划分?

【问题讨论】:

应该以什么方式快速——快速打字?读者可以快速理解吗?快速让您从 *** 复制粘贴? 完全随机是什么意思? “不为零”是什么意思? 0.00000000001 你还好吗? 数字是随机生成的吗?数字总是固定到小数点后 2 位吗?所有列表条目都不同吗?这是作业吗? @ mkriger1 好问题。应该更清楚,所有数字都应该 >= 0.01。 【参考方案1】:

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

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

在while循环之后,L×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

此第二版将在 16GB 内存的 AMD FX-8150 上在大约 1/3 秒内计算 100 万个大小为 10 的列表:

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

【讨论】:

你需要import numpy as np 我认为我下面的算法要快一些,我对此进行了计时,L = 1,000,000, n=10 花了超过一分钟,我不得不关闭我的解释器,而我的解释器花了 20 秒 @isosceleswheel 是的,它正在尝试构建一个 1,000,000 x 1,000,000 矩阵。我们可以解决这个问题。 @isosceleswheel 谢谢。现在比较。 @Paul 非常好,numpy 占上风!【参考方案2】:

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

【讨论】:

但由于所有小数必须 >= 0.01,最大范围只有 16,给出的可能列表少于 16^6 个 我的意思是有很多可能的组合不可能由您的算法生成。如果没有办法让例如0,99 出现,就不能称它完全随机【参考方案3】:

这应该可以解决问题:

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)

【讨论】:

def paritions 应该是def partitions TypeError: uniform() takes exactly 3 arguments (1 given) 如果你的第一个随机数等于“total”怎么办?【参考方案4】:

我没有检查其他人的速度,但是这个算法在 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

【讨论】:

以上是关于如何创建包含 n 个非零随机小数的 L 个列表,其中每个列表的总和为 1.0?的主要内容,如果未能解决你的问题,请参考以下文章

确定数组中 N 个非零最小值的索引

如何在 Python 中列出 n 个数字并随机选择任意数字?

如何找到至少有k个非零元素的列的索引?

常用正则表达式列表

如何在一定范围内创建随机数组

如何创建一个包含“n”个随机整数的数组?