在Python中生成1000000个随机数的最快方法
我现在正在用Python写一个应用程序,需要快速生成大量随机数。目前我使用numpy一次性生成大约50万个随机数,这比Python自带的生成方式快一些,但我还是希望能更快。有谁有什么建议吗?我愿意尝试用C语言编写代码并嵌入到程序中,或者做任何能加快速度的事情。
关于随机数的要求:
- 一组7个数字,它们的范围可以不同:
- 比如:[0-X1, 0-X2, 0-X3, 0-X4, 0-X5, 0-X6, 0-X7]
- 现在我生成7个随机数,范围是[0-1),然后再乘以[X1..X7]。
- 一组13个数字,它们的总和为1
- 现在我只是生成13个数字,然后把它们的和作为分母进行归一化。
有什么建议吗?预先计算这些数字并存储在文件中会让速度更快吗?
谢谢!
8 个回答
试试这个公式:r = 1664525*r + 1013904223
这个公式来自《C语言数值计算》(第二版),作者是Press等人,书号是0521431085,第284页。
np.random的确“更随机”;你可以查看一下线性同余生成器的相关内容。
在Python中,可以这样使用np.uint32
:
python -mtimeit -s '
import numpy as np
r = 1
r = np.array([r], np.uint32)[0] # 316 py -> 16 us np
# python longs can be arbitrarily long, so slow
' '
r = r*1664525 + 1013904223 # NR2 p. 284
'
如果想一次生成大量数据,可以这样做:
# initialize --
np.random.seed( ... )
R = np.random.randint( 0, np.iinfo( np.uint32 ).max, size, dtype=np.uint32 )
...
R *= 1664525
R += 1013904223
编辑 创建了可以返回完整数字集的函数,而不是一次只返回一行。编辑 2 让这些函数更符合 Python 的风格(也更快),并为第二个问题添加了解决方案。
对于第一组数字,你可以考虑使用 numpy.random.randint
或 numpy.random.uniform
,这两个函数需要你提供 low
和 high
的参数。生成一个包含 7 行、每行 1,000,000 个数字的数组,范围在你指定的区间内,在我这台 2 GHz 的电脑上大约只需要不到 0.7 秒:
def LimitedRandInts(XLim, N):
rowlen = (1,N)
return [np.random.randint(low=0,high=lim,size=rowlen) for lim in XLim]
def LimitedRandDoubles(XLim, N):
rowlen = (1,N)
return [np.random.uniform(low=0,high=lim,size=rowlen) for lim in XLim]
>>> import numpy as np
>>> N = 1000000 #number of randoms in each range
>>> xLim = [x*500 for x in range(1,8)] #convenient limit generation
>>> fLim = [x/7.0 for x in range(1,8)]
>>> aa = LimitedRandInts(xLim, N)
>>> ff = LimitedRandDoubles(fLim, N)
这个函数会返回在 [0,xLim-1] 范围内的整数,或者在 [0,fLim) 范围内的浮点数。整数版本大约用了 0.3 秒,而浮点版本用了大约 0.66 秒,都是在我这台 2 GHz 的单核机器上测试的。
对于第二组数字,我使用了 @Joe Kingston 的建议。
def SumToOneRands(NumToSum, N):
aa = np.random.uniform(low=0,high=1.0,size=(NumToSum,N)) #13 rows by 1000000 columns, for instance
s = np.reciprocal(aa.sum(0))
aa *= s
return aa.T #get back to column major order, so aa[k] is the kth set of 13 numbers
>>> ll = SumToOneRands(13, N)
这个大约用了 1.6 秒。
在所有情况下,result[k]
可以让你获取第 k 组数据。
你可以通过做你最开始描述的事情来稍微加快速度,也就是生成一堆随机数,然后进行相应的乘法和除法运算……
另外,你可能已经知道这一点,但在处理比较大的numpy数组时,务必要使用原地操作(比如 *=, /=, += 等)。这样做会大大减少内存的使用,而且也能显著提高速度。
In [53]: def rand_row_doubles(row_limits, num):
....: ncols = len(row_limits)
....: x = np.random.random((num, ncols))
....: x *= row_limits
....: return x
....:
In [59]: %timeit rand_row_doubles(np.arange(7) + 1, 1000000)
10 loops, best of 3: 187 ms per loop
和下面这个相比:
In [66]: %timeit ManyRandDoubles(np.arange(7) + 1, 1000000)
1 loops, best of 3: 222 ms per loop
虽然差别不大,但如果你真的很在意速度的话,这也是有帮助的。
为了证明这一点:
In [68]: x.max(0)
Out[68]:
array([ 0.99999991, 1.99999971, 2.99999737, 3.99999569, 4.99999836,
5.99999114, 6.99999738])
In [69]: x.min(0)
Out[69]:
array([ 4.02099599e-07, 4.41729377e-07, 4.33480302e-08,
7.43497138e-06, 1.28446819e-05, 4.27614385e-07,
1.34106753e-05])
同样,对于你提到的“行的和为一”的部分……
In [70]: def rand_rows_sum_to_one(nrows, ncols):
....: x = np.random.random((ncols, nrows))
....: y = x.sum(axis=0)
....: x /= y
....: return x.T
....:
In [71]: %timeit rand_rows_sum_to_one(1000000, 13)
1 loops, best of 3: 455 ms per loop
In [72]: x = rand_rows_sum_to_one(1000000, 13)
In [73]: x.sum(axis=1)
Out[73]: array([ 1., 1., 1., ..., 1., 1., 1.])
老实说,即使你用C语言重新实现这些东西,我也不确定你能在这方面超越numpy……当然,我也可能完全错了!