random.random()究竟是如何在python中运行的?

时间:2017-02-02 09:24:00

标签: python random

我对random.random()函数在python中的工作方式有点困惑。

docs表示它'返回[0.0,1.0)范围内的下一个随机浮点数。 我知道伪随机数生成器通过对值执行某些操作来工作。通常,此值是生成器生成的先前数字。所以我认为'下一个随机浮点'在这里意味着什么。 (如果我错了,请纠正我)

但是当我看到随机库的source code时,class Random中没有定义随机函数。相反,它在class SystemRandom中定义如下(代码的第671行):

 def random(self):
        """Get the next random number in the range [0.0, 1.0)."""
        return (int.from_bytes(_urandom(7), 'big') >> 3) * RECIP_BPF

如果我理解正确,这个函数使用os.urandom生成一个随机数。根据{{​​3}},返回来自OS特定随机源的随机字节。所以这不会给'下一个'浮点随机数。

两者如何相连?或者他们是两个不同的东西?

我在这里很困惑。任何形式的帮助将不胜感激。

谢谢!

4 个答案:

答案 0 :(得分:2)

Python中的random模块包含pseudorandom number generators(PRNGs)的两个接口(类)。您可以将其视为生成随机数的两种方式。


Random.random()如何工作?

random.random()在第749行的“ random.py”模块中定义(对我来说)

_inst = Random()
...
random = _inst.random

random.Random()本身并没有定义random()方法,而是继承了_random.Random()(它确实定义了称为random()的方法),它是名为{{ 1}}位于模块Random()上。

_random(它是一个内置模块)模块的C源代码可以在here(实际上称为_random)中找到。请参见下面的说明)

  

Naming convention for modules in C/C++

     

(从历史上看,如果模块称为垃圾邮件,则C文件包含   实现称为spammodule.c;如果模块名称非常   很长,就像spammify一样,模块名称可以只是spammify.c。)

_randommodule.c(或_random.Random.random())方法在random.random()文件中定义为_random_Random_random_impl()

_randommodule.c

static PyObject * _random_Random_random_impl(RandomObject *self) { uint32_t a=genrand_int32(self)>>5, b=genrand_int32(self)>>6; return PyFloat_FromDouble((a*67108864.0+b)*(1.0/9007199254740992.0)); } 是由Mersenne Twister PRNG实现定义的函数,该函数返回4字节数字。

SystemRandom()。random()如何工作?

(我知道您没有要求SystemRandom(),但是在我写这篇文章时,我还没有意识到)

我将这张图片作为我的答案的概述(但是,我鼓励您阅读所有内容)

Overview of SystemRandom's random() method

genrand_int32()在模块SystemRandom().random()中定义。

random.py

该函数使用模块 ... def random(self): """Get the next random number in the range [0.0, 1.0).""" return (int.from_bytes(_urandom(7), 'big') >> 3) * RECIP_BPF**strong text**

中定义的另一个名为urandom()的函数。
os.py

from os import urandom as _urandom 模块并未定义函数os.py本身,而是从内置模块导入的。如果您使用POSIX OS,则urandom()将导入os.py内置模块,如果您使用的是Windows NT OS,则将导入posix内置模块。这些模块包含urandom()的定义。

nt

OR

if 'posix' in _names:
    name = 'posix'
    linesep = '\n'
    from posix import *
  

elif 'nt' in _names: name = 'nt' linesep = '\r\n' from nt import * posix是内置模块,因此它们没有   nt属性。

潜入source code

POSIX

  
posixmodule.c
  
      
  • static PyObject * os_urandom_impl(PyObject *module, Py_ssize_t size) { ... bytes = PyBytes_FromStringAndSize(NULL, size); ... result = _PyOS_URandom(PyBytes_AS_STRING(bytes), PyBytes_GET_SIZE(bytes)); ... return bytes } _PyOS_URandom()文件中定义,然后调用pyurandom()
  •   
bootstrap_hash.c
  
      
  • int _PyOS_URandom(void *buffer, Py_ssize_t size) { return pyurandom(buffer, size, 1, 1); } pyurandom()文件中定义,然后调用dev_urandom()
  •   
bootstrap_hash.c
  
      
  • static int pyurandom(void *buffer, Py_ssize_t size, int blocking, int raise) { ... return dev_urandom(buffer, size, raise); ... } dev_urandom文件中定义,然后使用bootstrap_hash.c目录获取随机字节。
  •   
/dev/urandom

Windows NT

  

它可能看起来有点怪(我也以为是),但是static int dev_urandom(char *buffer, Py_ssize_t size, int raise) { ... fd = _Py_open("/dev/urandom", O_RDONLY); ... do { n = _Py_read(fd, buffer, (size_t)size); ... } while (0 < size); ... } 文件也用于NT系统,这是文件开头的引号(注释)

     

此文件还用于Windows NT / MS-Win。在这种情况下
  模块实际上称自己为“ nt”,而不是“ posix”,还有一些函数   没有实现或实现方式不同。来源
  假设对于Windows NT,定义了宏“ MS_WINDOWS”   独立于所使用的编译器。不同的编译器定义它们的   自己的功能测试宏,例如'_MSC_VER'。

对于Windows NT,直到pyurandom()函数,函数调用链与POSIX相同

  
      
  • posixmodule.cpyurandom()文件中定义,然后调用win32_urandom()
  •   
bootstrap_hash.c
  
      
  • static int pyurandom(void *buffer, Py_ssize_t size, int blocking, int raise) { ... #ifdef MS_WINDOWS return win32_urandom((unsigned char *)buffer, size, raise); #else ... } win32_urandom()文件中定义,然后调用bootstrap_hash.c
  •   
CryptGenRandom()
  
      
  • static int win32_urandom(unsigned char *buffer, Py_ssize_t size, int raise) { ... if (!CryptGenRandom(hCryptProv, chunk, buffer)) { ... } ... return 0; } CryptGenRandom()文件中声明,并在wincrypt.hAdvapi32.lib库中定义(这些文件由Microsoft提供)
  •   

答案 1 :(得分:1)

random.random()实际定义为here

random = _inst.random

但是,它只是对C实现的引用。

以下是来源:

  

关于底层Mersenne Twister核心生成器的一般说明:

     
      
  • 期限是2 ** 19937-1。
  •   
  • 它是现存最广泛测试的发电机之一。
  •   
  • random()方法在C中实现,在单个Python步骤中执行,因此是线程安全的。
  •   

您可能希望查看Mersenne Twister上的文章。简单地说,发电机的状态与“之前的数字”不一样,它要复杂得多。所以你错了«...伪随机数生成器通过对值执行某些操作来工作。通常,此值是生成器生成的先前数字»。

至于SystemRandom.random(),它与random.random()无关。在Python中,从不同模块导入的具有相同名称的函数是不同的,因此您不能在此处依赖函数的名称。

答案 2 :(得分:1)

这是你错过的片段(CPython32):

import _random

class Random(_random.Random):

_random.Random

>>> import _random
>>> dir(_random.Random)
[(...) 'getrandbits', 'getstate', 'jumpahead', 'random', 'seed', 'setstate']

,并且来自同一个random.py实现文件

开头的docstring
  

关于底层Mersenne Twister核心生成器的一般说明:

     
      
  • 期限是2 ** 19937-1。
  •   
  • 它是现存最广泛测试的发电机之一。
  •   
  • random()方法在C中实现,在单个Python步骤中执行,   因此,线程安全。
  •   

_random是一个已编译的C库,它包含一些基本操作,这些操作在random.py文件中包含的Python实现中进一步扩展。

答案 3 :(得分:0)

你是对的,这是使用os.urandom函数

from os import urandom as _urandom

BPF = 53        # Number of bits in a float
RECIP_BPF = 2**-BPF

def random():
    """Get the next random number in the range [0.0, 1.0)."""
    return (int.from_bytes(_urandom(7), 'big') >> 3) * RECIP_BPF

print(random())
print(RECIP_BPF)
print(int.from_bytes(_urandom(7), 'big')>> 3)
print(int.from_bytes(_urandom(7), 'little') >> 3)

玩它,你会得到它。