Python中numpy.random和random.random的区别

133

我有一个用Python编写的大脚本。受到其他人代码的启发,我在某些地方使用了numpy.random模块(例如用于创建从二项分布中取出的随机数数组),而在其他地方我使用了random.random模块。

请问有人可以告诉我这两者之间的主要区别吗? 从每个模块的文档网页上看,似乎numpy.random只是有更多的方法,但我不清楚生成随机数的方式有何不同。

我问这个问题的原因是因为我需要为调试目的种子化我的主程序。但除非我在导入的所有模块中使用相同的随机数生成器,否则它无法正常工作,这正确吗?

此外,在另一篇帖子中,我在这里阅读了关于不使用numpy.random.seed()的讨论,但我真的不明白为什么这样做是一个坏主意。如果有人能解释一下为什么这是这样的话,我将不胜感激。

4个回答

158
你已经做出了很多正确的观察!
除非你想同时种子两个随机生成器,从长远来看,选择一个生成器可能更简单。但如果你确实需要同时使用两个生成器,那么是的,你也需要给它们都设置种子,因为它们独立地生成随机数。
对于 numpy.random.seed(),主要困难在于它不是线程安全的 - 也就是说,如果你有许多不同的执行线程,使用它是不安全的,因为不能保证在两个不同的线程同时执行该函数时能正常工作。如果你不使用线程,并且可以合理地预计将来不需要按照这种方式重写程序,numpy.random.seed() 应该没问题。如果有任何理由怀疑将来可能会需要线程,从长远来看,按照建议做,并创建一个numpy.random.Random 类的本地实例会更安全。据我所知,random.seed() 是线程安全的(或者至少我没有找到相反的证据)。
numpy.random库包含一些在科学研究中常用的额外概率分布,以及一些方便生成随机数据数组的函数。random.random库更加轻量级,如果你不进行科学研究或其他统计工作,使用它应该足够了。
否则,它们都使用梅森旋转器序列来生成随机数,而且它们都是完全确定的 - 也就是说,如果你知道一些关键信息,就可以绝对确定地预测下一个数字会是什么。出于这个原因,无论是numpy.random还是random.random都不适用于任何严肃的加密用途。但由于序列非常非常长,所以在不担心有人试图逆向工程您的数据的情况下,两者都适用于生成随机数。这也是需要种子随机值的原因 - 如果每次都从同一个位置开始,您将始终得到相同的随机数序列!

顺便一提,如果您确实需要加密级别的随机性,则应使用secrets模块,或者如果您使用的是早于Python 3.6版本的Python,则可以使用Crypto.Random之类的模块。


15
作为一个间接相关的提醒,有时需要使用“neither”,因为Mersenne Twister算法不能生成足够用于密码学(和某些不寻常科学)目的的熵随机序列。在这些罕见情况下,通常需要使用Crypto.Random,它能够利用操作系统特定的熵源生成非确定性的随机序列,比仅从random.random可用的质量高得多。不过,通常情况下您不需要这样做。 - SingleNegationElimination
1
谢谢Hannnele。你的见解真的非常有用!事实证明,我不能仅使用一个随机数生成器(需要是numpy,因为random不会产生二项分布),因为我的程序的某些部分调用另一个使用随机数的程序。我将不得不对这两个生成器进行种子处理。 - Laura
2
如果你知道现在的数字,就可以绝对确定下一个数字是什么。我认为这个说法可能需要一些澄清。意思是,如果你知道生成器的内部状态,你就可以重现序列——这就是当你种子生成器时所做的。给定生成器输出的单个数字,你无法预测下一个数字。周期非常大,你可能需要一长串数字才能计算出你在伪随机序列上的位置,从而预测下一个数字。 - Kaushik Ghose

15

Python数据分析中得知,模块numpy.random可以高效地生成许多种概率分布的样本值数组,补充了Python自带的random模块。

相比之下,Python内置的random模块每次只能抽取一个值,而numpy.random可以更快速地生成大量样本。使用IPython魔法函数%timeit可以看出哪个模块执行更快:

In [1]: from random import normalvariate
In [2]: N = 1000000

In [3]: %timeit samples = [normalvariate(0, 1) for _ in xrange(N)]
1 loop, best of 3: 963 ms per loop

In [4]: %timeit np.random.normal(size=N)
10 loops, best of 3: 38.5 ms per loop

@ShayanAmani 对于更大的随机索引数组,Numpy 更快。 在“for idx in np.random.randint(0, 1_000_000, 100):pass”和“for idx in(random.randrange(0, 1_000_000)for _ in range(100)):pass”上运行%%timeit 命令。NumPy更快(16.9微秒与83微秒相比,是5倍)。 - Abhishek Divekar

3
我很惊讶randint(a, b)方法在numpy.randomrandom中都存在,但对于上限它们有不同的行为。 random.randint(a, b)返回一个随机整数N,使得a <= N <= b。这是randrange(a, b + 1)的别名,它包括brandom文档 然而,如果你调用numpy.random.randint(a, b),它将返回从低(包含)到高(不包含)。 Numpy文档

3
种子的来源和分布情况会影响输出结果——如果您正在寻找密码随机性,那么从os.urandom()中获取种子将获得来自设备杂音(例如以太网或磁盘)的几乎真实的随机字节(即BSD上的/dev/random)。这将避免您提供种子,从而生成确定性随机数。但是,随机调用允许您将数字适配到分布中(我称之为科学随机性——最终您想要的只是一组随机数的钟形曲线分布,numpy在交付这方面是最好的)。所以,是的,坚持使用一个生成器,但决定您想要什么样的随机数——随机的,但绝对来自分布曲线,或者尽可能接近量子设备的随机数。

非常感谢你的回答,Paul!它真的很有用!我不需要加密随机性,我正在进行数学建模,伪随机数对我来说已经足够了。但问题是,我不能像我想的那样坚持使用一个生成器,因为我需要numpy来进行二项分布,而我的程序调用另一个使用random的程序 :( - Laura

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接