random.random() 在 python 中究竟是如何工作的?

use*_*324 6 python random

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

的文档说,它的回归在[0.0,1.0)的范围内的下一个随机浮点数'。我知道伪随机数生成器通过对值执行一些操作来工作。通常,此值是生成器生成的前一个数字。所以我认为这就是“下一个随机浮点”在这里的意思。(如果我错了,请纠正我)

但是当我看到随机库的源代码时,在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
Run Code Online (Sandbox Code Playgroud)

如果我理解正确,这个函数会使用 os.urandom 生成一个随机数。根据文档,其中从特定于操作系统的随机性源返回随机字节。所以这不会给出“下一个”浮点随机数。

两者是如何联系起来的?或者它们是两种不同的东西?

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

谢谢!

Bla*_*ere 13

randompython中的模块包含伪随机数生成器(PRNG)的两个接口(类)。您可以将其视为生成随机数的两种方式。


关于模块的说明 secrets。

该模块secrets并没有实现任何类型的PRNG基于SystemRandom和os.urandom(其中SystemRandom基于),但提供了辅助功能(这是真棒,因为我们没有写他们自己)。评论是我的:

from random import SystemRandom
_sysrand = SystemRandom() #secrets._sysrand
randbits = _sysrand.getrandbits #secrets.randbits
choice = _sysrand.choice #secrets.choice

def randbelow(exclusive_upper_bound): #secrets.randbelow
    ...
    return _sysrand._randbelow(exclusive_upper_bound) #uses SystemRandom

def token_bytes(nbytes=None): #secrets.token_bytes
    ...
    return os.urandom(nbytes)

def token_hex(nbytes=None): #secrets.token_hex(uses token_bytes())
    ...
    return binascii.hexlify(token_bytes(nbytes)).decode('ascii')

def token_urlsafe(nbytes=None): # secrets.token_urlsafe(uses token_bytes())
    ...
    tok = token_bytes(nbytes)
    return base64.urlsafe_b64encode(tok).rstrip(b'=').decode('ascii')
Run Code Online (Sandbox Code Playgroud)

Random.random() 如何工作?

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

_inst = Random()
...
random = _inst.random
Run Code Online (Sandbox Code Playgroud)

该类random.Random()本身并不定义random()方法,而是继承_random.Random()(它定义了一个名为 的方法random()),这是一个Random()位于模块 中的类_random。

(它是一个内置模块)模块的C源代码_random可以在这里找到(它实际上被称为_randommodule.c。见下面的解释)

用 C/C++ 编写的 python 模块的命名约定

(历史上,如果一个模块被称为 spam,包含其实现的 C 文件被称为 spammodule.c;如果模块名称很长,如 spammify,模块名称可以只是 spammify.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));
}
Run Code Online (Sandbox Code Playgroud)

genrand_int32()是由Mersenne Twister PRNG实现定义的函数,它返回一个 4 字节的数字。

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

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

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

SystemRandom 的 random() 方法概述

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**
Run Code Online (Sandbox Code Playgroud)

该函数使用模块中定义的另一个名为 urandom() 的函数 os.py

from os import urandom as _urandom
Run Code Online (Sandbox Code Playgroud)

该os.py模块不定义函数urandom()本身,而是从内置模块中导入它。os.py将导入的posix,如果你是一个内置的模块 POSIX OS或nt内置模块,如果你是一个Windows NT的操作系统。这些模块包含 urandom() 的定义。

if 'posix' in _names:
    name = 'posix'
    linesep = '\n'
    from posix import *
Run Code Online (Sandbox Code Playgroud)

或者

elif 'nt' in _names:
    name = 'nt'
    linesep = '\r\n'
    from nt import *
Run Code Online (Sandbox Code Playgroud)

posix并且nt是内置模块,因此它们没有该 __file__属性。

潜入源代码:

POSIX

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
}
Run Code Online (Sandbox Code Playgroud)
  • _PyOS_URandom()在bootstrap_hash.c文件中定义,然后调用pyurandom()
int
_PyOS_URandom(void *buffer, Py_ssize_t size)
{
    return pyurandom(buffer, size, 1, 1);
}
Run Code Online (Sandbox Code Playgroud)
  • pyurandom()在bootstrap_hash.c文件中定义,然后调用dev_urandom()。
static int
pyurandom(void *buffer, Py_ssize_t size, int blocking, int raise)
{
  ...
  return dev_urandom(buffer, size, raise);
  ...
}
Run Code Online (Sandbox Code Playgroud)
  • dev_urandom在bootstrap_hash.c文件中定义,然后使用该/dev/urandom目录来获取随机字节。
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);
  ...
}
Run Code Online (Sandbox Code Playgroud)

视窗NT

它可能看起来有点奇怪(我也这么认为)但该posixmodule.c文件也用于 NT 系统,这里是文件开头的引用(评论)

此文件也用于 Windows NT/MS-Win。在这种情况下,
模块实际上称自己为“nt”,而不是“posix”,并且一些函数要么未实现,要么以不同方式实现。源代码
假定对于 Windows NT,宏“MS_WINDOWS”的定义与所使用的编译器无关。不同的编译器定义了它们自己的功能测试宏,例如'_MSC_VER'。

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

  • pyurandom()在bootstrap_hash.c文件中定义,然后调用win32_urandom()。
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
  ...
}
Run Code Online (Sandbox Code Playgroud)
  • 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;
}
Run Code Online (Sandbox Code Playgroud)
  • CryptGenRandom()在wincrypt.h文件中声明并在Advapi32.lib和Advapi32.dll库中定义(这些文件由 Microsoft 提供)


Ily*_*rov 5

random.random()实际上在这里定义:

random = _inst.random
Run Code Online (Sandbox Code Playgroud)

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

这是来自源的引用:

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

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

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

至于SystemRandom.random(),在某种程度上是无关的random.random()。在 Python 中,从不同模块导入的同名函数可能不同,所以这里不能依赖函数名。

  • 你知道在哪里可以找到 C 模块吗? (2认同)