【问题标题】:Fastest Way to generate 1,000,000+ random numbers in python在 python 中生成 1,000,000+ 随机数的最快方法
【发布时间】:2011-02-12 04:21:20
【问题描述】:

我目前正在用 python 编写一个需要快速生成大量随机数的应用程序。目前我有一个计划,它使用 numpy 生成一个大批量的所有数字(一次大约 500,000 个)。虽然这似乎比 python 的实现要快。我仍然需要它走得更快。有任何想法吗?我愿意用 C 语言编写它并将其嵌入到程序中或做 w/e 它需要。

对随机数的约束:

  • 一组可以有不同界限的 7 个数字:
    • 例如:[0-X1、0-X2、0-X3、0-X4、0-X5、0-X6、0-X7]
    • 目前我正在生成一个包含 7 个数字的列表,其中包含 [0-1) 的随机值,然后乘以 [X1..X7]
  • 一组 13 个数字加起来等于 1
    • 目前只生成 13 个数字,然后除以它们的总和

有什么想法吗?预先计算这些数字并将它们存储在文件中会加快速度吗?

谢谢!

【问题讨论】:

  • 这几乎可以保证磁盘 I/O 不会使其更快,因此文件存储方法可能不是您想要的。
  • 你能提供你缓慢的实现吗?生成 1000_000 个随机整数需要 12 毫秒。你需要多快? python -mtimeit -s'import numpy as np' 'np.random.randint(low=0, high=500, size=(1000000,1))' -> 100 loops, best of 3: 11.9 msec per loop
  • 放大 aaa,不知道您将它们放入的应用程序,很难回答。有比模块 random 中的默认值更快的伪随机数生成器,它们的周期更短,这取决于应用程序 - 可能是合适的。
  • [4] * 1000000 应该是最快的。但是,就随机性而言,您可能会发现 xkcd 随机数生成器不能满足您的需求。你能说出这些需求是什么吗?
  • 你可以使用浮点数而不是双精度数吗?我认为 numpy 默认为双打。这应该将你的内存减少一半(和带宽)

标签: python performance random numpy


【解决方案1】:

您可以通过执行您最初描述的操作(生成一堆随机数并相应地相乘和相除)来加快 mtrw 上面发布的内容...

此外,您可能已经知道这一点,但在使用大型 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...不过我可能大错特错!

【讨论】:

  • @Joe - 我尝试了有限数量的方法,发现它在我的机器上速度较慢。我很好奇你能不能试试我的比较一下?我还偷了你的求和到 1 数字的方法;这比我以前尝试的要快得多。谢谢!
  • @mtrw- 现在你的更新函数比我的快很多。 (166ms vs 184ms)你的不需要整个内存块是连续的,只需要每列的内存,我认为这是造成差异的主要原因。缺点是在创建数据后访问数据。你必须为你的使用列表推导(或类似的),而我的返回一个 2D numpy 数组(稍微更快和更灵活的索引)。不过,如果您一次只需要访问一行,这并不重要。干杯!
【解决方案2】:

EDIT 创建了返回完整数字集的函数,而不是一次只返回一行。 编辑 2 使函数更 Pythonic(更快),为第二个问题添加解决方案

对于第一组数字,您可以考虑numpy.random.randintnumpy.random.uniform,它们采用lowhigh 参数。在我的 2 GHz 机器上生成一个指定范围内的 7 x 1,000,000 个数字的数组似乎需要

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) 中的浮点数。在我的 2 GHz 单核机器上,整数版本耗时约 0.3 秒,双倍约 0.66 秒。

对于第二组,我使用了@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 组数据。

【讨论】:

  • 如果你乘以倒数而不是除以浮点性能,你可能会赢得几个周期。
  • 我得试试看。你知道水平堆叠数组(不知道怎么写)来组合数组的性能吗?
  • @aaa - 谢谢,我将您的建议纳入答案。 @Sandro - 我认为堆栈不是很好。您也许可以预先分配数组。我会看看我是否可以做到这一点并更新答案。
  • 您可以做的另一件事是生成随机的 N/13 个数字并顺时针或逆时针旋转它们。这将生成随机集(但通常不是随机成员)。真的需要知道瓶颈在哪里
  • @J.F.塞巴斯蒂安 - 哎呀。谢谢你抓住那个。 @aaa - 我得到的印象是 OP 希望每个集合准确地加到 1。我在您的第二个建议中没有看到这一点。我错过了什么吗?
【解决方案3】:

试试r = 1664525*r + 1013904223
来自“更快的发电机” 在“C 中的数字食谱”第 2 版中,Press 等人,isbn 0521431085,p。 284.
np.random 肯定是“更随机”;看 Linear congruential generator.

在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

【讨论】:

    【解决方案4】:

    让您的代码并行运行当然不会有什么坏处。尝试使用 Parallel Python 将其适应 SMP

    【讨论】:

    • 实际上由于需要大内存,复制内存或通过管道发送内存非常昂贵,到目前为止实际上一直在减慢我的速度。
    【解决方案5】:

    正如其他人已经指出的那样,numpy 是一个非常好的开始,快速且易于使用。

    如果您需要大规模的随机数,请考虑使用 eas-ecb 或 rc4。两者都可以并行化,您应该可以达到几 GB/s 的性能。

    achievable numbers posted here

    【讨论】:

      【解决方案6】:

      如果您可以访问多个内核,则可以与 dask.array 并行完成计算:

      import dask.array as da
      x = da.random.random(size=(rows, cols)).compute()
      # .compute is not necessary here, because calculations
      # can continue in a lazy form and .compute is used
      # on the final result
      

      【讨论】:

        【解决方案7】:

        只是numpy 的一个简单示例:

        data = numpy.random.rand(1000000)
        

        不需要循环,你可以传入你想生成多少个数字。

        【讨论】:

          猜你喜欢
          • 2017-01-21
          • 2010-10-26
          • 2020-08-28
          • 1970-01-01
          • 2022-10-04
          • 2013-10-12
          • 2023-03-22
          • 2012-02-05
          • 2017-02-27
          相关资源
          最近更新 更多