我试图找出在python中生成许多随机数的最佳方法。困难的部分是我不知道在运行之前我需要多少个数字
我有一个程序,一次使用一个随机数,但它需要多次这样做。
我迄今为止尝试过的事情是:
random.random()
np.random.rand()
np.random.rand(N)
np.random.rand(N)
在一批N中生成随机数,并在第一个N全部使用后生成一个新批处理(我尝试了两个不同的实现,两者都比生成一个数字慢一次)在下面的脚本中,我比较了前三种方法(均匀和正态分布的随机数)。
我不知道p
功能是否真的有必要,但我想在每种情况下用随机数做相同的事情,这似乎是最简单的方法。
#!/bin/python3
import time
import random
import numpy as np
def p(x):
pass
def gRand(n):
for i in range(n):
p(random.gauss(0,1))
def gRandnp1(n):
for i in range(n):
p(np.random.randn())
def gRandnpN(n):
rr=np.random.randn(n)
for i in rr:
p(i)
def uRand(n):
for i in range(n):
p(random.random())
def uRandnp1(n):
for i in range(n):
p(np.random.rand())
def uRandnpN(n):
rr=np.random.rand(n)
for i in rr:
p(i)
tStart=[]
tEnd=[]
N=1000000
for f in [uRand, uRandnp1, uRandnpN]:
tStart.append(time.time())
f(N)
tEnd.append(time.time())
for f in [gRand, gRandnp1, gRandnpN]:
tStart.append(time.time())
f(N)
tEnd.append(time.time())
print(np.array(tEnd)-np.array(tStart))
此时间脚本输出的代表示例是:
[ 0.26499939 0.45400381 0.19900227 1.57501364 0.49000382 0.23000193]
前三个数字用于[0,1]上的均匀随机数,接下来三个用于正态分布数(mu = 0,sigma = 1)。
对于任何一种随机变量,最快的方法(这三种方法)是一次生成所有随机数,将它们存储在数组中,然后迭代数组。问题是,在我运行程序之前,我还不知道我需要多少这些数字。
我想做的是大批量生成随机数。然后当我在一批中使用所有数字时,我只会重新填充它们存储的对象。问题是我不知道实现这一目标的干净方法。我想出的一个解决方案如下:
N=1000000
numRepop=4
N1=N//numRepop
__rands__=[]
irand=-1
def repop():
global __rands__
__rands__=np.random.rand(N1)
repop()
def myRand():
global irand
try:
irand += 1
return __rands__[irand]
except:
irand=1
repop()
return __rands__[0]
但这实际上比任何其他选项都慢。
如果我将numpy数组转换为列表然后关闭元素,我的性能类似于只使用numpy一次生成一个随机变量:
__r2__=[]
def repop2():
global __r2__
rr=np.random.rand(N1)
__r2__=rr.tolist()
repop2()
def myRandb():
try:
return __r2__.pop()
except:
repop2()
return __r2__.pop()
有更好的方法吗?
编辑 by"更好"我的意思是更快。我也更喜欢确定性(伪)随机数
答案 0 :(得分:2)
如果一次生成大量数字的速度更快,您可以制作一个可以缓存批次的生成器。这适用于python 3.5
def randoms(batchsize=10000):
while True:
yield from numpy.random.rand(batchsize)
不知道它是否比其他实现更快,但它是一个永无止境的生成器。
您可以像任何迭代器一样使用它:
prng = randoms()
for _ in range(1000000):
foo(next(prng))
或者像这样(但循环永远不会退出):
for x in randoms():
foo(x)
修改强>
我自己尝试对此进行基准测试,我认为差异主要是因为python中函数调用的额外成本。我试图通过在所有情况下循环range
来使基准更具可比性,并且使用预生成阵列的优势更小。
我通过使用微优化技巧获得了几乎同样好的速度,其中numpy.random.rand
被分配给局部变量,这大大加快了函数调用。
我还包括用于比较的发电机方法。
def randoms(batchsize):
rand = numpy.random.rand
while True:
yield from rand(batchsize)
def test_generator(times):
rand = randoms(1000).__next__
for n in range(times):
rand()
def test_rand(times):
for n in range(times):
numpy.random.rand()
def test_rand_micro_opt(times):
rand = numpy.random.rand
for n in range(times):
rand()
def test_array(times):
array = numpy.random.rand(times)
for n in range(times):
array[n]
# ipython / jupyter magic %timeit command
%timeit -n 1000 test_generator(10000)
%timeit -n 1000 test_rand(10000)
%timeit -n 1000 test_rand_micro_opt(10000)
%timeit -n 1000 test_array(10000)
1000 loops, best of 3: 2.09 ms per loop
1000 loops, best of 3: 2.93 ms per loop
1000 loops, best of 3: 1.74 ms per loop
1000 loops, best of 3: 1.57 ms per loop
答案 1 :(得分:1)
通过不一直查找模块及其功能,您可以大大加快速度。
def uRand_2(n):
r = random.random
for i in range(n):
p(r())
def uRandnp1_2(n):
r = np.random.rand
for i in range(n):
p(r())
您的版本在我的电脑上定时:
[ 0.14439154 0.24865651 0.13786387 0.85637093 0.28924942 0.13338685]
我的上述两个版本(对应于前两个版本):
[ 0.10629296 0.15638423]
哦,我没有看到调用p
的重点。我认为这只会增加噪音并使实际随机数生成的速度变慢。以下是我没有打电话p
的时间,即只是做r()
:
[ 0.04560113 0.1083169]
答案 2 :(得分:0)
不是很漂亮,但这应该有效:
import numpy as np
class BatchedPRNG(object):
def __init__(self, seed=0, batch_size=10000, dist='uniform'):
self.prng = np.random.RandomState(seed) # own random-stream !
self.batch_size = batch_size
self.dist = dist
self.index = 0
if self.dist == 'uniform':
self.pool = self.prng.random_sample(size=self.batch_size)
else:
self.pool = self.prng.normal(size=self.batch_size)
def sample_one(self):
if self.index < self.batch_size:
self.index += 1
return self.pool[self.index-1]
else:
self.index = 1
if self.dist == 'uniform':
self.pool = self.prng.random_sample(size=self.batch_size)
else:
self.pool = self.prng.normal(size=self.batch_size)
return self.pool[self.index-1]
dist = BatchedPRNG()
for i in range(11):
print(dist.sample_one())
这遵循封装/面向对象方法的想法,以每次需要新样本时函数调用为代价。它还使用自己的PRNG-Stream,因此代码其他部分的np.random.X
全局调用不会更改此对象的内部状态。
如果您想使用其他发行版或需要其他功能,显然您需要修改它。
可悲的是,你也必须照顾基准。
编辑:意外缓慢