如何创建包含 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?的主要内容,如果未能解决你的问题,请参考以下文章