文章目录伪随机数的基本概念线性同余法Mersenne 旋转算法Xoshiro256++PCG64
伪随机数的基本概念伪随机数(Pseudo-random Number)是通过确定性算法生成的序列,看似随机但可重现。其核心特点是:
确定性:相同种子产生相同序列。统计特性:需满足均匀性、独立性等统计检验。周期性:序列最终会重复(周期需足够长)。与真随机数(依赖物理熵源如热噪声、放射性衰变)不同,伪随机数因高效可控,广泛用于仿真、蒙特卡罗方法等过程辨识场景。
线性同余法在过程辨识课程中介绍机器生成随机数(特别是伪随机数)时,需从理论基础、生成方法、算法实现到应用场景进行系统阐述。以下为分步骤的讲解框架,重点聚焦于生成**[0,1]均匀分布随机数**的具体方法及其原理。 线性同余生成器: linear congruential generator(LCG)
原理:通过线性递推公式生成整数序列,再映射到[0,1]区间。
递推公式:
X
n
+
1
=
(
a
X
n
+
c
)
m
o
d
m
X_{n+1} = (a X_n + c) \mod m
Xn+1=(aXn+c)modm(X_n):当前状态(种子)(a):乘数(如1103515245)(c):增量(如12345)(m):模数(取(2^{31})等大整数)。转换到[0,1]:
U
n
=
X
n
m
U_n = \frac{X_n}{m}
Un=mXn示例代码(Python):
def lcg(seed, a=1664525, c=1013904223, m=2**32):
while True:
seed = (a * seed + c) % m
yield seed / m # 映射到[0,1]
直方图测试
import numpy as np
import matplotlib.pyplot as plt
seed=0
iters=1000;
v=np.zeros(iters)
for k in range(iters):
seed= lcg(seed=seed)
v[k]=seed/2**32
plt.hist(v)
plt.show()
直方图测试
优缺点:
✅ 实现简单、计算快。❌ 周期短、低维均匀性差(需谨慎选择参数)。Mersenne 旋转算法梅森旋转算法(Mersenne Twister,MT. 以MT19937著称)是一种基于线性反馈移位寄存器的伪随机数生成算法,由松本真和西村拓士于1997年提出。其核心优势在于极长周期(2¹⁹⁹³⁷ -1)、高维均匀分布性(623维内均匀),以及高效性。以下是其具体算法步骤及原理详解,结合MT19937(32位版本)为例:
算法基础与核心参数MT19937-32的关键参数如下表所示: 参数 符号 取值(16进制) 作用 状态数组长度 n 624 存储内部状态 偏移量 m 397 状态混合偏移 位掩码 r 31 (0x7FFFFFFF) 分割高低位 旋转矩阵 a 0x9908B0DF 非线性变换 初始化乘数 f 1812433253 种子扩展 位移参数 u, s, t, l 11, 7, 15, 18 输出调和 掩码参数 b, c, d 0x9D2C5680, 0xEFC60000, 0xFFFFFFFF 输出调和
注:d 通常取全1掩码(0xFFFFFFFF),在32位运算中可省略。
算法详细步骤(1) 初始化阶段:构建初始状态数组
输入一个32位种子 seed,生成长度为 n=624 的状态数组 MT[0…623]:
def initialize(seed):
MT = [0] * 624
MT[0] = seed
for i in range(1, 624):
# 高位保留30位,低位2位参与运算
prev = MT[i-1]
temp = f * (prev ^ (prev >>
30)) + i
MT[i] = temp &
0xFFFFFFFF # 取低32位
关键操作: • prev ^ (prev >> 30):保留高位随机性,消除低位规律性。
• 乘数 f=1812433253 扩散随机性,加法 +i 避免重复序列。
(2) 扭转阶段(Twist):更新状态数组
当所有状态值使用后(即生成624个随机数),需重构整个状态数组:
def twist():
for i in range(624):
# 组合MT[i]的高1位和MT[i+1]的低31位
x = (MT[i] &
0x80000000) | (MT[(i+1) % 624] &
0x7FFFFFFF)
# 右移1位并可能异或a
xA = x >>
1
if x % 2 != 0: # 若x为奇数
xA ^= a # 异或旋转矩阵a
# 更新状态:异或偏移m处的状态
MT[i] = MT[(i + m) % 624] ^ xA
核心目的: • 通过非线性移位(异或 a)和状态混合(异或 MT[i+m])打破线性相关性。
(3) 输出处理阶段(Tempering):生成最终随机数
从当前状态 MT[index] 提取值,经4步位操作调和:
def extract_number():
y = MT[index]
y ^= (y >> u) # 消除高位局部相关性
y ^= (y << s) & b # 消除低位局部相关性
y ^= (y << t) & c
y ^= (y >> l)
return y &
0xFFFFFFFF # 输出32位整数
调和步骤的作用: • 通过位移和掩码(b, c)将状态位的线性关系转化为混沌输出,增强统计均匀性。
代码实现(Python精简版)
class MersenneTwister
:
def __init__(self, seed):
self.n, self.m = 624, 397
self.MT = [0] * self.n
self.index = 0
self.init_genrand(seed)
def init_genrand(self, seed):
self.MT[0] = seed
for i in range(1, self.n):
temp = 1812433253 * (self.MT[i-1] ^ (self.MT[i-1] >>
30)) + i
self.MT[i] = temp &
0xFFFFFFFF
def twist(self):
for i in range(self.n):
x = (self.MT[i] &
0x80000000) | (self.MT[(i+1) % self.n] &
0x7FFFFFFF)
xA = x >>
1
if x % 2 != 0:
xA ^= 0x9908B0DF
self.MT[i] = self.MT[(i + self.m) % self.n] ^ xA
self.index = 0
def genrand_int32(self):
if self.index >= self.n:
self.twist()
y = self.MT[self.index]
y ^= y >>
11
y ^= (y <<
7) &
0x9D2C5680
y ^= (y <<
15) &
0xEFC60000
y ^= y >>
18
self.index += 1
return y &
0xFFFFFFFF
直方图测试
import numpy as np
import matplotlib.pyplot as plt
iters=1000
v=np.zeros(iters)
N=0
for k in range(iters):
mt = MersenneTwister(N)
rand_uniform = mt.genrand_int32() / (2**32) # 转换为[0,1)区间
v[k]=rand_uniform
N=mt.genrand_int32()
plt.hist(v)
plt.show()
直方图测试
性能优化与变体
空间优化: • TinyMT:状态数组仅127位,适用于嵌入式系统。
速度优化: • SFMT(SIMD-Friendly MT):利用CPU向量指令并行生成随机数,提速200%。
安全增强: • CryptMT:通过过滤函数输出,满足密码学安全要求。
算法验证与应用场景
• 统计检验:
通过Diehard、TestU01等测试验证623维内均匀性。 • 典型应用:
• 蒙特卡洛模拟(如金融风险评估)
• 游戏引擎(如地图生成、NPC行为)
• 机器学习(如参数初始化、数据增强)
关键注意事项
• 种子选择:避免全0种子(导致状态全0失效),推荐使用时间戳。
• 非密码安全:攻击者可反向推导状态(需340个连续输出),加密场景需换用安全算法。
梅森旋转算法以其平衡的性能与随机性质量,成为Python(random模块)、NumPy等库的默认生成器。理解其状态更新机制(Twist)和输出调和(Tempering)是掌握现代PRNG设计的关键。
Xoshiro256++Xoshiro256++ 是由 David Blackman 和 Sebastiano Vigna 设计的伪随机数生成器(PRNG),属于 xoshiro(xor/shift/rotate)系列算法。其核心设计目标是:
高速度:利用位运算(移位、异或、旋转)实现极低开销的随机数生成。长周期:周期为 (2^{256}-1),远高于传统算法(如梅森旋转算法的 (2^{19937}-1))。统计质量优异:通过严苛的随机性测试(如 TestU01 的 BigCrush 套件)。1. 状态维护 算法维护一个包含 4 个 64 位整数的状态数组 S = [s0, s1, s2, s3],初始状态需非全零。
2. 状态更新步骤 每生成一个随机数,状态按以下步骤更新:
def next_state(s0, s1, s2, s3):
t = (s0 + s3) &
0xFFFFFFFFFFFFFFFF # 64位模加
s0_new = (s0 ^ (s1 <<
17)) &
0xFFFFFFFFFFFFFFFF
s1_new = s1 ^ s2
s2_new = s2 ^ s3
s3_new = s3 ^ ((s3 <<
45) | (s3 >>
19)) # 循环左移45位
return s0_new, s1_new, s2_new, s3_new, t
关键操作说明:
异或移位(s0 ^ (s1 << 17)):破坏线性相关性,增强混沌性。循环移位((s3 << 45) | (s3 >> 19)):避免状态退化,维持长周期。模加法(t = s0 + s3):作为输出前的非线性变换,改善低维均匀性。3. 输出生成 最终输出的随机数为 t(64 位整数),可通过除法映射到 [0, 1) 区间:
output = t / (2**64) # 转换为[0,1)浮点数
三、Python 完整实现
class Xoshiro256PP
:
def __init__(self, seed=0):
# 初始化状态:使用SplitMix64生成初始状态(确保非零)
self.state = [0] * 4
temp_seed = seed
for i in range(4):
temp_seed = self._splitmix64(temp_seed)
self.state[i] = temp_seed
def _splitmix64(self, seed):
# SplitMix64 用于种子扩展
seed = (seed + 0x9E3779B97F4A7C15) &
0xFFFFFFFFFFFFFFFF
z = seed
z = (z ^ (z >>
30)) * 0xBF58476D1CE4E5B9 &
0xFFFFFFFFFFFFFFFF
z = (z ^ (z >>
27)) * 0x94D049BB133111EB &
0xFFFFFFFFFFFFFFFF
return z ^ (z >>
31)
def next(self):
s0, s1, s2, s3 = self.state
# 计算临时输出值(非线性变换)
t = (s0 + s3) &
0xFFFFFFFFFFFFFFFF
# 更新状态
s0_new = (s0 ^ (s1 <<
17)) &
0xFFFFFFFFFFFFFFFF
s1_new = s1 ^ s2
s2_new = s2 ^ s3
s3_new = (s3 ^ ((s3 <<
45) | (s3 >>
19))) &
0xFFFFFFFFFFFFFFFF # 循环左移45位
self.state = [s0_new, s1_new, s2_new, s3_new]
return t
def random(self):
# 生成[0,1)区间的浮点数
return self.next() / (2**64)
直方图测试
import numpy as np
import matplotlib.pyplot as plt
iters=1000
v=np.zeros(iters)
N=0
for k in range(iters):
rng = Xoshiro256PP(seed=k)
rand_uniform = rng.random() # 转换为[0,1)区间
v[k]=rand_uniform
plt.hist(v)
plt.show()
直方图测试
关键代码解析:
_splitmix64:用于将种子扩展为 4 个 64 位状态值,避免初始状态弱熵问题。循环移位:(s3 << 45) | (s3 >> 19) 等效于循环左移 45 位(64 位环境下)。位掩码:& 0xFFFFFFFFFFFFFFFF 确保结果在 64 位范围内(Python 无整数溢出需手动处理)。四、算法特性与对比
特性Xoshiro256++梅森旋转(MT19937)周期(2^{256}-1)(2^{19937}-1)状态空间32 字节2.5 KB速度⭐⭐⭐⭐ (比 MT 快 3-5 倍)⭐⭐统计质量通过 TestU01 BigCrush通过,但高维有缺陷适用场景游戏、模拟、轻量加密通用场景注:Xoshiro256++ 不适用于密码学(非加密安全),但适合高性能模拟。
五、应用场景建议
蒙特卡洛模拟:高吞吐量需求下的随机数生成。游戏开发:NPC 行为、地图生成等高频随机事件。机器学习:数据增强、参数初始化(需结合种子管理)。六、扩展优化方向
并行化:每个线程独立维护状态数组,避免锁竞争。跳跃函数:预计算 (2^{128}) 步状态,支持分布式 RNG 同步。如需加密安全方案,可替换为 SHA-256 或 ChaCha20 等算法。
PCG64PCG64(Permuted Congruential Generator)是一种高性能的伪随机数生成器(PRNG),由Melissa O’Neill于2014年提出。它结合了线性同余生成器(LCG)的高效性和输出变换函数的统计优化,适用于科学计算、模拟和高并发场景。以下从算法原理、关键特性及Python实现三方面展开详解。
一、算法原理 PCG64的核心结构分为两部分:状态更新(LCG)和输出变换(非线性位操作)。1. 状态更新(LCG) 采用128位线性同余生成器:
s
n
+
1
=
(
a
×
s
n
+
c
)
m
o
d
2
128
s_{n+1} = (a \times s_n + c) \mod 2^{128}
sn+1=(a×sn+c)mod2128
$s_n$:当前状态(128位整数)
a
a
a$:乘数(固定常数,需满足 (a \equiv 5 \mod 8) 以保证周期)
c
c
c:增量(奇数,确保满周期 (2^{128}))2. 输出变换 通过位操作打破LCG的序列相关性,生成64位随机数:
u = s_n >>
(64 - p) # 取高64位,p为位移参数(通常为32)
output = u ^ (u >> p) # 右移后异或,增强随机性
位移参数:p 控制混淆强度(默认32)位旋转:部分变体引入循环移位(如PCG64DXSM),进一步改善统计质量。⚡ 二、关键特性1. 统计质量
通过 TestU01的BigCrush测试,高维均匀性优于梅森旋转(MT19937)。改进版PCG64DXSM:解决原始版本在大规模并行流(>百万级)中的统计弱点,采用DXSM输出函数(xorshift-multiply哈希构造),雪崩效应更优。2. 性能与资源
速度:比MT19937快2-3倍(避免大状态数组操作)。内存:仅需16字节(128位状态),MT19937需2.5KB。周期:(2^{128})(可扩展)。3. 并行能力
种子派生:通过SeedSequence.spawn()生成独立子流,避免序列重叠。状态跳跃:jumped(n) 跳过 (2^n) 步状态,快速创建并行流。 三、Python实现1. 基础用法(NumPy) NumPy 1.17+ 默认使用PCG64(或PCG64DXSM):
import numpy as np
# 创建生成器(默认PCG64)
rng = np.random.default_rng(seed=42)
# 生成随机数
uniform = rng.random(size=5) # [0,1) 均匀分布
normal = rng.normal(0, 1, size=5) # 标准正态分布
integers = rng.integers(0, 10, size=5) # [0,10) 整数
2. 并行流管理
# 生成10个独立子流
parent_rng = np.random.default_rng(42)
child_streams = parent_rng.spawn(10)
# 从子流生成随机数
for stream in child_streams:
print(stream.random())
3. 自定义位参数(高级) 若需实现原生PCG64(非NumPy封装):
class PCG64
:
def __init__(self, seed, a=0x5851F42D4C957F2D, c=0x14057B7EF767814F, p=32):
self.state = seed
self.a = a # 乘数(固定常数)
self.c = c # 增量(奇数)
self.p = p # 位移参数
def next(self):
# 状态更新
self.state = (self.a * self.state + self.c) &
0xFFFFFFFFFFFFFFFF
# 输出变换(取高64位)
u = self.state >>
(64 - self.p)
output = u ^ (u >> self.p)
return output &
0xFFFFFFFF # 返回32位整数
# 使用示例
pcg = PCG64(seed=42)
print(pcg.next()) # 输出随机整数
直方图测试
import numpy as np
import matplotlib.pyplot as plt
seed=0
iters=1000;
v=np.zeros(iters)
for k in range(iters):
pcg= PCG64(seed=seed)
seed=pcg.next()
v[k]=seed/2**32
plt.hist(v)
plt.show()
直方图测试
四、应用场景与最佳实践
蒙特卡洛模拟
金融衍生品定价(百万次路径并行计算)。物理粒子运动仿真(需高吞吐量随机流)。机器学习
数据增强(图像/文本扰动)。模型参数初始化(可复现性要求高)。游戏开发
地图生成、NPC行为(避免序列重复)。最佳实践:
加密场景:PCG64不适用(非加密安全),改用secrets模块。大规模并行:优先使用PCG64DXSM(NumPy 1.17+默认)。种子管理:用SeedSequence派生子流,而非手动设置种子偏移。PCG64以简洁的设计平衡了速度、内存与统计质量,成为现代科学计算的首选PRNG。其Python实现依托NumPy的default_rng()接口,兼具易用性与扩展性,尤其适合需高并发和高性能的场景。