随机数与伪随机数生成器:Python中random模块、numpy.random、种子设置与可重复性

做量化金融的人,天天跟随机数打交道。蒙特卡洛模拟说白了,就是用随机数去逼近真实世界的分布。但这里有个坑——计算机生成的随机数,其实都是伪随机数。嗯,这个区别很重要,我当年刚入行时就吃过亏。

真随机 vs 伪随机:到底差在哪?

真随机数,比如用物理过程(放射性衰变、热噪声)产生的,理论上不可预测。但问题是,太慢了,而且没法复现。你想想看,做回测时如果每次跑出来的结果都不一样,你怎么调试策略?

伪随机数就不一样了。它基于一个确定的算法,只要初始状态(种子)相同,生成的序列就完全一样。这恰恰是我们想要的——可重复性。

核心要点:伪随机数不是真正的随机,但足够「随机」用于金融模拟。关键在于,它让我们能复现结果。

我个人习惯,在正式跑蒙特卡洛之前,一定会先固定种子。为什么?因为调试时你需要知道,这次结果变了是因为代码改了,还是随机数变了。

Python random模块:入门级但够用

Python自带的random模块,是最基础的伪随机数生成器。它用的是梅森旋转算法(Mersenne Twister),周期长达2^19937-1。说实话,对于大多数衍生品定价场景,这个周期已经绰绰有余了。

import random

# 设置种子
random.seed(42)

# 生成均匀分布 [0,1)
print(random.random())  # 0.6394267984578837

# 生成正态分布
print(random.gauss(0, 1))  # 均值0,标准差1

# 从列表中随机选
choices = ['call', 'put', 'binary']
print(random.choice(choices))

我在项目中遇到过一个问题:用random模块生成大量随机数时,速度明显跟不上。特别是做路径依赖型期权(比如亚式期权)的模拟,动辄上百万条路径,random模块就有点吃力了。

我的建议:如果模拟路径少于10万条,random模块完全够用。超过这个量级,建议用numpy.random。

numpy.random:量化金融的标配

numpy.random比random模块快得多,因为它底层用C语言实现,而且支持向量化操作。说白了,一次生成一批随机数,比循环生成快一个数量级。

import numpy as np

# 设置种子
np.random.seed(42)

# 生成1000个标准正态分布随机数
samples = np.random.randn(1000)

# 生成均匀分布
uniform_samples = np.random.rand(1000)

# 生成指定形状的随机数矩阵
matrix = np.random.normal(loc=0, scale=1, size=(100, 100))

我记得有一次做障碍期权的定价,需要生成100万条路径。用random模块跑了将近两分钟,换成numpy.random后,十几秒就搞定了。这个差距,在实盘交易中可能就是能不能及时给出报价的区别。

注意:numpy.random和random模块的种子是独立的。你设了random.seed(42),不影响numpy.random的结果。两个模块要分别设种子。

种子设置与可重复性:避坑指南

种子设置看似简单,但坑不少。我曾经因为种子没设好,导致回测结果无法复现,被风控部门追着问了一下午。

常见的坑有这几个:

  • 种子设在了循环里——每次循环都重置种子,生成的都是同一个「随机数」
  • 多线程环境下种子冲突——每个线程需要独立的随机数流
  • 不同版本的numpy种子行为不同——numpy 1.x和2.x的随机数算法有变化
# 正确的做法
np.random.seed(42)  # 只在程序开头设一次

# 错误的做法
for i in range(100):
    np.random.seed(42)  # 每次循环都重置,结果都一样
    print(np.random.randn())

避坑指南:我曾经在写回测框架时,把种子设在了策略类的__init__方法里。结果每个策略实例都用了相同的随机数序列,回测结果完全不可信。后来改成在程序入口统一设种子,问题才解决。

知识体系结构图

下面这张图,帮你理清随机数生成的核心脉络:

随机数与伪随机数生成器知识体系 随机数分类 真随机数(物理过程) 伪随机数(算法生成) Python random模块 numpy.random 种子设置与可重复性 种子设置与可重复性

实际应用中的选择策略

说了这么多,到底该用哪个?我整理了一个对比表,你直接照着选就行:

场景 推荐模块 原因
简单模拟(<10万条路径) random 够用,无需额外依赖
大规模模拟(>10万条路径) numpy.random 速度快,支持向量化
需要复现结果 两者皆可 记得设种子
多线程环境 numpy.random.SeedSequence 避免种子冲突
生产环境/回测框架 numpy.random 性能稳定,社区支持好

总结一下:伪随机数不是缺陷,而是特性。它让我们能在确定性和随机性之间找到平衡。做量化金融,你不需要真正的随机,你需要的是可控的随机。

嗯,关于随机数生成器的基础就聊到这儿。种子设置这个看似简单的问题,其实藏着不少细节。我建议你每次写模拟代码前,先问问自己:这个结果,我明天还能复现吗?


专注资料整理