在Python中生成1000000个随机数的最快方法

27 投票
8 回答
42233 浏览
提问于 2025-04-15 22:00

我现在正在用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 个回答

4

试试这个公式: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
7

编辑 创建了可以返回完整数字集的函数,而不是一次只返回一行。编辑 2 让这些函数更符合 Python 的风格(也更快),并为第二个问题添加了解决方案。

对于第一组数字,你可以考虑使用 numpy.random.randintnumpy.random.uniform,这两个函数需要你提供 lowhigh 的参数。生成一个包含 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 组数据。

14

你可以通过做你最开始描述的事情来稍微加快速度,也就是生成一堆随机数,然后进行相应的乘法和除法运算……

另外,你可能已经知道这一点,但在处理比较大的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……当然,我也可能完全错了!

撰写回答