位生成器#

Generator 生成的随机值源自位生成器。位生成器不直接提供随机数,只包含用于播种、获取或设置状态、跳转或推进状态以及访问底层包装器以供代码高效访问所提供函数(例如 numba)的方法。

支持的位生成器#

包含的位生成器有:

  • PCG-64 - 默认。一个快速的生成器,可以任意推进。有关 advance 的文档,请参阅。PCG-64 的周期为 \(2^{128}\)。有关此类 PRNG 的更多详细信息,请参阅 PCG 作者页面

  • PCG-64 DXSM - PCG-64 的升级版本,在并行环境中具有更好的统计特性。有关这些改进的更多信息,请参阅 使用 PCG64DXSM 升级 PCG64

  • MT19937 - 标准 Python 位生成器。添加了一个 MT19937.jumped 函数,该函数返回一个新生成器,其状态如同已经进行了 \(2^{128}\) 次抽取。

  • Philox - 一个计数器生成器,能够任意步进或生成独立流。有关此类位生成器的更多详细信息,请参阅 Random123 页面。

  • SFC64 - 一个基于随机可逆映射的快速生成器。通常是这四种生成器中最快的。有关(少量)更多细节,请参阅 SFC 作者页面

BitGenerator([seed])

通用位生成器的基类,它们基于不同的算法提供随机比特流。

播种和熵#

位生成器提供一个随机值流。为了生成可重现的流,位生成器支持通过种子设置其初始状态。所有提供的位生成器都将接受任意大小的非负整数或整数列表作为种子。位生成器需要接收这些输入并将其处理成位生成器的高质量内部状态。numpy 中的所有位生成器都将此任务委托给 SeedSequence,它使用哈希技术来确保即使低质量的种子也能生成高质量的初始状态。

from numpy.random import PCG64

bg = PCG64(12345678903141592653589793)

SeedSequence 的设计是为了方便实现最佳实践。我们建议随机程序默认使用来自操作系统的熵,以便每次运行都不同。程序应打印或记录该熵。为了重现过去的值,程序应允许用户通过某种机制(通常是命令行参数)提供该值,以便用户可以重新输入该熵来重现结果。SeedSequence 可以处理除与用户通信之外的所有事情,这取决于您。

from numpy.random import PCG64, SeedSequence

# Get the user's seed somehow, maybe through `argparse`.
# If the user did not provide a seed, it should return `None`.
seed = get_user_seed()
ss = SeedSequence(seed)
print(f'seed = {ss.entropy}')
bg = PCG64(ss)

我们默认使用来自操作系统的熵收集的 128 位整数。这是初始化 numpy 中所有生成器的一个良好熵量。我们不建议在一般用途中使用低于 32 位的较小种子。仅使用一小组种子来实例化较大的状态空间意味着有些初始状态是无法达到的。如果每个人都使用这些值,这会产生一些偏差。

结果本身不会有任何“错误”;即使是种子 0,由于 SeedSequence 的处理,也是完全可以的。如果您只需要一些用于单元测试或调试的固定值,可以随意使用您喜欢的任何种子。但如果您想从结果中推断或发布它们,从更大的种子集中抽取是一个好习惯。

如果您需要“离线”生成一个好的种子,那么 SeedSequence().entropy 或使用标准库中的 secrets.randbits(128) 都是方便的方法。

如果您需要并行运行多个随机模拟,最佳实践是为每个模拟构建一个随机生成器实例。为了确保随机流具有不同的初始状态,您可以使用 SeedSequencespawn 方法。例如,我们在这里构造一个包含 12 个实例的列表。

from numpy.random import PCG64, SeedSequence

# High quality initial entropy
entropy = 0x87351080e25cb0fad77a44a3be03b491
base_seq = SeedSequence(entropy)
child_seqs = base_seq.spawn(12)    # a list of 12 SeedSequences
generators = [PCG64(seq) for seq in child_seqs]

如果您已经有一个初始随机生成器实例,可以使用 spawn 方法缩短上面的代码。

from numpy.random import PCG64, SeedSequence
# High quality initial entropy
entropy = 0x87351080e25cb0fad77a44a3be03b491
base_bitgen = PCG64(entropy)
generators = base_bitgen.spawn(12)

另一种方法是利用 SeedSequence 可以由一组元素初始化。这里我们使用一个基础熵值和一个整数 worker_id

from numpy.random import PCG64, SeedSequence

# High quality initial entropy
entropy = 0x87351080e25cb0fad77a44a3be03b491
sequences = [SeedSequence((entropy, worker_id)) for worker_id in range(12)]
generators = [PCG64(seq) for seq in sequences]

请注意,后一种方法产生的序列将与通过 spawn 构建的序列不同。

SeedSequence([entropy, spawn_key, ...])

SeedSequence 以可重现的方式混合熵源,以设置独立且非常可能不重叠的位生成器的初始状态。