NumPy 2.x 完全指南【三十八】伪随机数生成器
文章目录
- 1. 概述
- 2. 种子(Seed)
- 3. Generator 类
- 3.1 BitGenerator
- 3.2 创建子生成器
- 3.3 生成简单随机数
- 3.4 生成随机排列
1. 概述
numpy.random
模块是 NumPy
库中用于生成随机数的核心模块,它实现了伪随机数生成器(Pseudorandom Number Generators
, 简称 PRNGs
或 RNGs
)。这些生成器能够从各种概率分布中抽取样本,是科学计算、数据分析和机器学习中不可或缺的工具。
一般使用 random.default_rng()
创建一个 Generator
实例,然后调用生成器的各种方法从不同分布中获取样本。
示例 1 ,生成一个在 [0, 1)
范围内均匀分布的随机浮点数:
# 创建生成器实例
rng = np.random.default_rng()
# 生成随机数
res=rng.random()
print(res) # 输出可能类似:0.5737686083064519
注意事项:伪随机数生成器设计用于统计建模和模拟,它们不适用于安全或加密目的。
2. 种子(Seed)
伪随机数生成器的本质是确定性。它从一个称为种子的初始值开始,通过预先设计好的数学算法进行计算,生成看似随机的数字序列,只要种子相同,算法相同,产生的序列就完全一致。
示例 1,设置相同种子时,不同的伪随机数实例,输出结果完全一样:
rng = np.random.default_rng(1)
res=rng.random()
print(res) # 0.5118216247002567rng = np.random.default_rng(1)
res=rng.random()
print(res) # 0.5118216247002567
default_rng()
有且仅有一个 seed
参数用于设置种子,可选值有:
None
:从操作系统获取随机熵源,产生不可预测的随机序列。int
:使用特定整数值初始化种子,相同的整数种子会产生相同的随机数序列。array_like[ints]
:使用整数数组初始化种子,提供更多的熵源,可能增强随机数生成的初始状态多样性。SeedSequence
:使用一个SeedSequence
实例来初始化,支持生成独立且可重复的并行随机数流。BitGenerator
:直接传递一个BitGenerator
(底层随机比特生成器),它将被Generator
包装。Generator
:直接传递一个Generator
实例,函数会将其原样返回。RandomState
:传递一个旧的RandomState
实例,它将被转换为新的Generator
实例。
在设置 seed
参数时,为了确保统计独立性和可重现性,确保你的种子与他人的不同,最好使用大且独一无二的正整数作为种子。获取此类种子数字的一个简便方法是使用 secrets.randbits
获取一个任意的 128
位整数。
示例 2 ,生成并使用一个 128
位的随机种子:
import secrets
seed = secrets.randbits(128) # 输出可能类似:122807528840384100672342137672332424406
rng1 = np.random.default_rng(seed)
rng1.random() # 输出:0.5363922081269535
3. Generator 类
random.default_rng()
会创建一个 Generator
实例,Generator
部分类定义如下:
class Generator:def __init__(self, bit_generator: BitGenerator) -> None: ...def __repr__(self) -> str: ...def __str__(self) -> str: ...def __getstate__(self) -> None: ...def __setstate__(self, state: dict[str, Any] | None) -> None: ...def __reduce__(self) -> tuple[Callable[[BitGenerator], Generator],tuple[BitGenerator],None]: ...@propertydef bit_generator(self) -> BitGenerator: ...
3.1 BitGenerator
Generator
类是面向用户的用户接口,内部的 BitGenerator
才是真正的底层引擎,用于生成高质量的原始随机比特序列,并提供给上层的 Generator
来转换为各种分布的随机数。
NumPy
实现了多种 BitGenerator
类,采用了不同的随机数生成算法:
PCG64
:默认算法。MT19937
:经典的梅森旋转算法。Philox
:计数器模式的密码学启发算法。SFC64
:Small Fast Chaotic
算法。
直接使用 np.random.default_rng()
即可,它会使用默认且推荐的 PCG64
算法:
# 创建一个主生成器
main_rng = np.random.default_rng(seed=42)# 访问 bit_generator 属性
bit_gen = main_rng.bit_generator
print("底层 BitGenerator 类型:", type(bit_gen)) # 例如: <class 'numpy.random._pcg64.PCG64'>
更换底层随机数算法时,无需改动上层 Generator 的接口和功能,用户只需在创建 Generator
时指定不同的 BitGenerator
即可:
from numpy.random import Generator, PCG64, MT19937, Philox# 用于旧式 MT19937 算法
rng = np.random.default_rng(MT19937(seed=42))# 生成随机数
res = rng.random()
print(res) # 输出可能类似:0.5419938930062744
3.2 创建子生成器
spawn(n_children)
方法可以创建多个独立的、新的子生成器,常用于并行计算。
示例:
# 创建一个主生成器
main_rng = np.random.default_rng(seed=42)
# 使用 spawn 方法创建独立的子生成器
num_children = 3
child_generators = main_rng.spawn(num_children)# 这些子生成器可以用于并行任务,确保每个任务都有独立的随机源
for i, child_rng in enumerate(child_generators):print(f"子生成器 {i} 的随机数: {child_rng.random(3)}")
3.3 生成简单随机数
函数名称及常用参数 | 主要功能 | 重要参数说明 |
---|---|---|
integers(low[, high, size, dtype, endpoint]) | 生成随机整数。 | - low : 下限(包含)。- high : 上限(默认不包含,endpoint=True 时包含)。- size : 输出形状。- dtype : 数据类型。- endpoint : 若为 True ,则区间包含 high 。 |
random([size, dtype, out]) ** | 生成 [0.0, 1.0) 范围内的随机浮点数。 | - size : 输出形状。 |
choice(a[, size, replace, p, axis, shuffle]) | 从给定数组 a 中随机抽取元素。 | - a : 输入数组或整数。- size : 输出形状。- replace : 是否允许重复抽取(有放回抽样)。- p : 各元素被抽中的概率。 |
bytes(length) | 生成随机字节。 | - length : 生成的字节数。 |
np.random.Generator.integers
是生成随机整数的首选方法。其核心参数是 low
和 high
,用于指定随机整数的范围。默认情况下,这个区间是左闭右开 [low, high)
。如果需要包含 high
,可以设置 endpoint=True
。
示例:
rng = np.random.default_rng(seed=42) # 创建随机数生成器# 生成 1 个 [0, 5) 之间的随机整数
print(rng.integers(5)) # 输出:例如 3# 生成 3 个 [0, 5) 之间的随机整数
print(rng.integers(5, size=3)) # 输出:例如 array([3, 4, 0])# 生成 3 个 [2, 8) 之间的随机整数
print(rng.integers(2, 8, size=3)) # 输出:例如 array([7, 2, 6])# 生成 2x3 的数组,元素在 [10, 20) 之间
print(rng.integers(10, 20, size=(2, 3)))
# 输出:例如 array([[18, 15, 11],
# [13, 19, 10]])# 生成 2 个 [1, 4] 之间的随机整数 (使用 endpoint=True 使区间包含 high)
print(rng.integers(1, 4, endpoint=True, size=2)) # 输出:例如 array([1, 4])
np.random.Generator.random
用于生成半开区间 [0.0, 1.0)
内的随机浮点数。可以通过 size
参数指定输出数组的形状。
示例:
rng = np.random.default_rng(seed=42) # 创建随机数生成器# 生成 1 个随机浮点数
print(rng.random()) # 输出:例如 0.77395605# 生成 5 个随机浮点数的一维数组
print(rng.random(size=5))
# 输出:例如 array([0.43887844, 0.85859792, 0.69736803, 0.09417735, 0.97562235])# 生成 2x3 的随机浮点数数组
print(rng.random(size=(2, 3)))
# 输出:例如 array([[0.7611397 , 0.78606431, 0.12811363],
# [0.45038594, 0.37079802, 0.92676499]])
np.random.Generator.choice
可以从给定的数组或整数序列中随机抽取元素。参数 a
可以是一个一维数组,也可以是一个整数(此时相当于从 np.arange(a)
中抽取)。replace
参数控制是否允许重复抽取(默认为 True
,即有放回抽样),p
参数可以指定每个元素被抽中的概率。
示例:
rng = np.random.default_rng(seed=42) # 创建随机数生成器
data = np.array([10, 20, 30, 40, 50])# 从 data 中随机抽取 1 个元素
print(rng.choice(data)) # 输出:例如 30# 从 data 中随机抽取 3 个元素(有放回)
print(rng.choice(data, size=3)) # 输出:例如 array([10, 30, 20])# 从 data 中随机抽取 3 个元素(无放回,replace=False)
print(rng.choice(data, size=3, replace=False)) # 输出:例如 array([40, 10, 50])# 从 0 到 4 (即 np.arange(5)) 中随机抽取 3 个数
print(rng.choice(5, size=3)) # 输出:例如 array([0, 3, 3])# 指定抽取概率:p=[0.1, 0.1, 0.2, 0.3, 0.3],分别对应 data 中的每个元素
print(rng.choice(data, size=3, p=[0.1, 0.1, 0.2, 0.3, 0.3]))
# 输出:例如 array([50, 40, 40]) (40 和 50 被抽中的概率更高)
np.random.Generator.bytes
用于生成随机字节字符串。它接受一个参数 length
,指定要生成的字节数。这在需要生成加密密钥、随机令牌或任何需要原始随机字节的场景中非常有用。
示例:
rng = np.random.default_rng(seed=42) # 创建随机数生成器# 生成 5 个随机字节
random_bytes = rng.bytes(5)
print(random_bytes) # 输出:例如 b'\x1a\x2b\x3c\x4d\x5e' (显示为十六进制形式)# 生成 10 个随机字节
print(rng.bytes(10)) # 输出:例如 b'\x6f\x7a\x8b\x9c\xad\xbe\xcf\xd0\xe1\xf2'
3.4 生成随机排列
方法 | 是否修改原数组 | 返回值 | 轴处理方式 |
---|---|---|---|
shuffle(x) | 是 (原地) | None | 将输入视作一维序列,沿给定轴整体打乱。 |
permutation(x) | 否 | 新数组(副本) | 将输入视作一维序列,沿给定轴整体打乱。 |
permuted(x) | 可选 | 打乱后的数组 | 沿指定轴独立地对每个切片进行重排,非整体打乱。 |
shuffle
会直接修改原始数组(原地操作),不返回任何值(返回 None
)。它默认将数组视作一维序列进行打乱。对于多维数组,axis
参数指定哪个轴被当作这个“一维序列”来处理。
示例:
rng = np.random.default_rng(seed=42) # 创建随机数生成器# 打乱列表 (非NumPy数组也行,会就地修改)
my_list = [1, 2, 3, 4, 5]
rng.shuffle(my_list)
print("打乱后的列表:", my_list) # 输出: 例如 [2, 5, 1, 4, 3]# 打乱一维数组
arr_1d = np.array([1, 2, 3, 4, 5])
rng.shuffle(arr_1d)
print("打乱后的一维数组:", arr_1d) # 输出: 例如 [2, 5, 1, 4, 3]# 打乱二维数组 - 默认沿 axis=0(行)整体打乱
arr_2d = np.arange(9).reshape(3, 3)
print("原始二维数组:")
print(arr_2d)
# 输出:
# [[0 1 2]
# [3 4 5]
# [6 7 8]]rng.shuffle(arr_2d) # 整体打乱行顺序
print("整体打乱行后:")
print(arr_2d)
# 输出(示例):
# [[3 4 5] # 原第二行
# [0 1 2] # 原第一行
# [6 7 8]] # 原第三行
# 每行内部元素顺序不变
permutation
不修改原始数组,而是返回一个打乱后的新数组副本。和 shuffle
一样,它默认也将输入视作一维序列进行处理,axis
参数的含义相同。
示例:
rng = np.random.default_rng(seed=42) # 创建随机数生成器# 输入整数 n:返回 0 到 n-1 的随机排列
permuted_range = rng.permutation(5)
print("0到4的随机排列:", permuted_range) # 输出: 例如 [2 0 3 1 4]# 输入一维数组:返回打乱后的新数组,原数组不变
arr_1d = np.array([1, 2, 3, 4, 5])
permuted_1d = rng.permutation(arr_1d)
print("原一维数组:", arr_1d) # 输出: [1 2 3 4 5] (不变)
print("打乱后的新数组:", permuted_1d) # 输出: 例如 [2 5 1 4 3]# 输入二维数组:沿 axis=0(行)整体打乱,返回新数组
arr_2d = np.arange(9).reshape(3, 3)
print("原始二维数组:")
print(arr_2d)
# 输出:
# [[0 1 2]
# [3 4 5]
# [6 7 8]]permuted_2d = rng.permutation(arr_2d) # 整体打乱行顺序
print("整体打乱行后的新数组:")
print(permuted_2d)
# 输出(示例):
# [[3 4 5] # 原第二行
# [0 1 2] # 原第一行
# [6 7 8]] # 原第三行
# 原数组不变,每行内部元素顺序不变
permuted
在轴处理上与前两者不同,它是沿指定轴独立地对每个切片进行重排,而不是将输入视作一个整体序列。它可以通过 out
参数选择是返回新数组还是原地修改。
示例:
rng = np.random.default_rng(seed=42) # 创建随机数生成器arr_2d = np.arange(9).reshape(3, 3)
print("原始二维数组:")
print(arr_2d)
# 输出:
# [[0 1 2]
# [3 4 5]
# [6 7 8]]# 沿 axis=1 (列) 独立打乱:每行内部元素独立重排
permuted_indep = rng.permuted(arr_2d, axis=1)
print("每行内部独立打乱后的新数组:")
print(permuted_indep)
# 输出(示例):
# [[1 0 2] # 第一行内部打乱
# [5 3 4] # 第二行内部打乱
# [8 6 7]] # 第三行内部打乱
# 原数组不变# 使用 out 参数进行原地操作
rng.permuted(arr_2d, axis=1, out=arr_2d) # 将结果写回原数组
print("原地每行独立打乱后的数组:")
print(arr_2d)
# 输出(示例):
# [[1 0 2]
# [5 3 4]
# [8 6 7]]