随机数生成:伪随机数原理、Python中的random库、numpy.random模块

做Monte Carlo模拟,说白了就是跟随机数打交道。你想想看,我们要模拟成千上万条资产价格路径,每一步都得靠随机数来驱动。所以随机数生成这件事,是整个模拟的基石。

我个人习惯把随机数生成比作「炒菜的盐」——用量不大,但少了它整道菜就废了。今天我们就来聊聊这个「盐」到底是怎么来的。

伪随机数原理:计算机真的能「随机」吗?

先问个问题:计算机能生成真正的随机数吗?

答案是:不能。至少普通计算机不行。

计算机是确定性的机器。给它同样的输入,它永远给你同样的输出。所以它没法「凭空」产生随机数。那怎么办?

嗯,这里有个巧妙的方法——用算法模拟随机性。这就是伪随机数生成器(PRNG)的核心思想。

最常见的算法叫线性同余生成器(LCG)。公式很简单:

X_{n+1} = (a * X_n + c) mod m

其中:

  • X_n 是当前随机数
  • a 是乘数
  • c 是增量
  • m 是模数

说白了,就是拿上一个数做一顿数学运算,然后取余数。这个余数看起来就像随机数。

关键点:伪随机数不是真随机,但它的统计性质(分布、相关性)可以做到非常接近真随机。对于金融模拟来说,够用了。

我在项目中遇到过一个问题:用默认的随机数生成器跑100万次模拟,结果发现某些极端场景出现的频率异常高。排查了半天,原来是随机数生成器的周期太短,导致序列重复了。所以选对生成器很重要。

Python中的random库:入门级选手

Python自带的random库,用的是梅森旋转算法(Mersenne Twister)。这个算法周期长达2^19937-1,质量相当不错。

常用的函数就这几个:

函数 说明 示例
random.random() 返回[0,1)的浮点数 0.3745
random.uniform(a, b) 返回[a,b]的均匀分布 3.1415
random.gauss(mu, sigma) 返回正态分布 0.0234
random.seed(n) 设置随机种子 random.seed(42)

举个例子,模拟一次标准正态随机数:

import random

# 设置种子,保证结果可复现
random.seed(42)

# 生成10个标准正态随机数
for _ in range(10):
    z = random.gauss(0, 1)
    print(f"{z:.4f}")

我的习惯:每次做模拟前,一定先设种子。不然第二天跑同样的代码,结果不一样,你都不知道是代码改了还是随机性在作怪。

但说实话,random库有个缺点——慢。你要生成几百万个随机数,它就不太行了。而且它不支持向量化操作,一次只能生成一个数。

numpy.random模块:专业选手的武器

做金融模拟,我几乎只用numpy.random。为什么?

  • 速度快:底层用C实现,批量生成效率极高
  • 向量化:一次生成整个数组,配合numpy的广播机制
  • 分布全:正态、对数正态、泊松、卡方……应有尽有

来看看怎么用:

import numpy as np

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

# 生成10000个标准正态随机数
z = np.random.standard_normal(10000)

# 生成10000个均值为0.05,标准差为0.2的正态随机数
returns = np.random.normal(0.05, 0.2, 10000)

# 生成1000x252的随机矩阵(模拟1000条路径,每条252天)
paths = np.random.standard_normal((1000, 252))

你看,一行代码就能生成上万个随机数。这在Monte Carlo模拟里太关键了。

注意:numpy 1.17版本之后,推荐使用新的随机数API:np.random.default_rng()。旧的np.random.seed()虽然还能用,但新API在统计性质上更优。

新API的用法:

from numpy.random import default_rng

# 创建生成器
rng = default_rng(42)

# 生成随机数
z = rng.standard_normal(10000)
uniform = rng.uniform(0, 1, 5000)

我曾经踩过一个坑:用旧API在多线程环境下生成随机数,结果不同线程拿到了相同的序列。换成新API的default_rng后,每个线程独立生成器,问题就解决了。

知识体系一览

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

随机数生成知识体系 随机数生成 真随机数(硬件) 伪随机数(算法) 线性同余 / 梅森旋转 random库(基础) numpy.random(高效) 金融模拟中,99%的场景用伪随机数 + numpy.random 就足够了

选哪个?我的建议

简单总结一下:

  • 小规模测试(几百个随机数):用random库就行,简单直接
  • 大规模模拟(几万到几百万):必须用numpy.random,性能差距是数量级的
  • 需要可复现:永远记得设种子,np.random.seed(42)default_rng(42)
  • 多线程/多进程:用新API的default_rng,每个线程独立生成器

核心原则:随机数生成器的质量,直接决定Monte Carlo模拟的可靠性。别在这个环节偷懒。

好了,随机数这块就聊到这儿。记住一句话:伪随机数不是真随机,但用对了,它就是真随机的完美替身。


专注资料整理