第 11 章 随机数与蒙特卡洛模拟
学习目标
- 掌握
np.random.default_rng创建随机数生成器 - 理解种子(seed)与可复现性
- 掌握
integers、uniform、normal、choice - 会用
shuffle打乱数组 - 会用蒙特卡洛方法做数值估算
11.1 随机数生成器:default_rng
NumPy 2.x 推荐用 np.random.default_rng(种子) 创建一个随机数生成器对象(简称 rng),再通过它的方法生成随机数:
import numpy as np
rng = np.random.default_rng(42)
print(rng.integers(1, 7, size=5)) # 5 个 1~6 的整数(掷骰子)输出:
[1 5 4 3 3]integers(最小, 最大, size=个数) 生成整数,包含最小值、不包含最大值——和 Python 的 range 规则一致。
size 可以是整数或元组,生成多维随机数组:
import numpy as np
rng = np.random.default_rng(42)
print(rng.integers(0, 2, size=(3, 4))) # 3×4 的 0/1 矩阵输出(以你实际运行为准,种子相同则结果相同):
[[1 1 0 0]
[1 1 1 0]
[1 1 1 1]]11.2 种子:让随机可复现
种子(seed)是随机数生成器的初始状态。同一个种子生成完全相同的序列:
import numpy as np
r1 = np.random.default_rng(42)
r2 = np.random.default_rng(42)
print(r1.integers(1, 7, size=5))
print(r2.integers(1, 7, size=5)) # 与上一行完全相同输出:
[1 5 4 3 3]
[1 5 4 3 3]为什么要种子?
- 调试:随机导致的 bug 难以复现;固定种子后每次运行一样,可以定位问题。
- 实验可重复:别人的结果能复现,结果可验证。
- 不给种子时,每次运行序列不同(真正的「随机」)。
本书所有随机示例都给出种子,方便你复现。
11.3 常用分布
均匀分布 uniform(最小, 最大, size):区间内每个值等可能。
正态分布 normal(均值, 标准差, size):钟形曲线,均值附近概率最高。
import numpy as np
rng = np.random.default_rng(7)
print(rng.uniform(0, 1, size=3)) # 0~1 均匀分布
print(rng.uniform(0, 1, size=3)) # 继续生成,序列不同输出:
[0.62509547 0.8972138 0.77568569]
[0.22520719 0.30016628 0.87355345]注意:同一个 rng 连续调用会依次产生不同随机数;只有「重新用同一个种子创建 rng」才会从头复现。
正态分布模拟人群身高(均值 170 cm、标准差 5 cm):
import numpy as np
rng = np.random.default_rng(3)
heights = rng.normal(170, 5, size=1000)
print("模拟平均身高:", round(heights.mean(), 2))
print("模拟标准差:", round(heights.std(), 2))输出:
模拟平均身高: 170.19
模拟标准差: 5.031000 个样本的平均≈170、标准差≈5,接近设定参数;样本越多越接近——这正是「大数定律」的体现。
11.4 choice:按概率抽取
rng.choice(数组, size=个数) 从数组中随机抽取,replace=False 表示不重复抽取;p=概率列表 指定每个元素的概率:
import numpy as np
rng = np.random.default_rng(1)
items = np.array(["苹果", "香蕉", "橘子", "葡萄"])
print(rng.choice(items, size=3, replace=False)) # 不重复抽 3 个
print(rng.choice(items, size=6, p=[0.4, 0.3, 0.2, 0.1])) # 按概率抽 6 次输出:
['香蕉' '苹果' '葡萄']
['葡萄' '苹果' '香蕉' '橘子' '香蕉' '香蕉']第二个例子中,苹果概率 0.4 最高,抽到最多;葡萄概率 0.1 最低。概率列表必须与数组等长且和为 1。
rng.shuffle(数组) 原地打乱顺序(返回 None):
import numpy as np
rng = np.random.default_rng(9)
deck = np.arange(1, 11) # 10 张牌
rng.shuffle(deck)
print(deck)输出:
[ 8 3 6 10 9 4 7 5 1 2]11.5 蒙特卡洛方法:用随机模拟估算
蒙特卡洛方法(Monte Carlo method) 用大量随机样本来估计数值结果。经典例子:估算圆周率 π。
思路:在边长为 2 的正方形(范围 -1~1)内随机撒点,统计落在单位圆内的比例。圆面积/正方形面积 = π/4,所以 π ≈ 4 × 圆内点数/总点数:
import numpy as np
rng = np.random.default_rng(2026)
N = 100_000 # 撒 10 万个点
x = rng.uniform(-1, 1, size=N)
y = rng.uniform(-1, 1, size=N)
inside = (x**2 + y**2 <= 1).sum() # 布尔掩码计数
pi_est = 4 * inside / N
print("估算 π:", pi_est)输出:
估算 π: 3.14404真实 π≈3.14159,误差约 0.08%。点越多,估计越准——把 N 改成 100 万试试,误差会进一步缩小。
再举一例:模拟掷 1000 次骰子,验证「每个点数出现约 1/6」:
import numpy as np
rng = np.random.default_rng(5)
rolls = rng.integers(1, 7, size=1000)
print("平均点数:", rolls.mean()) # 理论上 3.5
print("掷出 6 的比例:", (rolls == 6).mean()) # 理论上 ≈0.167输出:
平均点数: 3.451
掷出 6 的比例: 0.159平均点数 3.451 接近理论值 3.5,6 的比例 0.159 接近 1/6≈0.167。
11.6 旧式随机接口(了解)
老教程常见 np.random.rand()、np.random.randint()、np.random.seed() 这套旧接口。NumPy 2.x 仍然兼容,但官方推荐用 default_rng(新版接口类型更安全、速度更快、可复现性更好)。遇到老代码能看懂即可,新代码用 default_rng。
动手实践
- 用
default_rng(100)生成 10 个 1~100 的整数,再重新创建同一个种子的 rng,验证两次结果一致。 - 用
normal(身高均值, 5)模拟 10000 人的身高,计算平均与标准差,观察是否接近设定值。 - 用
choice按概率模拟「猜硬币」:硬币两面概率各 0.5,抛 10000 次,统计正面比例。 - 用蒙特卡洛方法估算 π,分别取
N=1000、N=100000、N=1000000,记录三次估计值,观察误差变化。
常见错误
| 错误写法 | 现象 | 原因 |
|---|---|---|
直接用 np.random.default_rng() 生成随机数 | TypeError: ... object is not callable | default_rng 返回生成器对象,要用 rng.integers(...) 调用 |
| 期望「同一 rng 重新生成相同序列」 | 每次调用结果不同 | 复现需要重新用同一个种子创建 rng,不是继续调用 |
integers(1, 7) 想包含 7 | 最大只到 6 | integers 不包含上界 |
choice(..., replace=False) 抽取个数超过元素数 | ValueError: Cannot take a larger sample than population | 不重复抽取的个数不能超过数组长度 |
p 概率之和不是 1 | ValueError: probabilities do not sum to 1 | 概率列表必须归一化 |
rng.shuffle(a) 后打印返回值 | 输出 None | shuffle 原地修改,返回 None |
章末练习
基础
- 用
default_rng(1)生成 5 个 1~10 的整数并打印;再用default_rng(1)重复一次,验证相同。 - 生成 100 个 0~1 均匀分布的数,计算平均值,解释为什么接近 0.5。
- 从
np.array(["红", "绿", "蓝"])中不重复抽取 2 个,打印结果。
提高
- 模拟掷两枚骰子 10000 次,统计点数之和的平均值(理论上 7),并计算「和为 7」出现的比例。
- 用
normal(100, 15, 100000)模拟 IQ 分布,统计大于 130 的比例(理论上约 2.3%)。
挑战
- 用蒙特卡洛方法估算定积分 ∫₀¹ x² dx(理论值 1/3):在 [0,1]×[0,1] 正方形内撒点,统计落在曲线
y=x²下方的比例,解释它与 π 例子的相似之处。 - 写一个函数
simulate_dice(seed, n),返回 n 次掷骰子的平均点数;用多个不同种子运行,观察结果围绕 3.5 波动的规律。
章末自测
每题选择一个最佳答案。本书不附答案:完成后交由老师或 AI 老师批改讲解。
- 创建随机数生成器的推荐写法是?
- A.
np.random.rand() - B.
np.random.default_rng(42) - C.
np.random.seed(42) - D.
rng = np.random()
- A.
rng.integers(1, 7, size=3)可能生成?- A.
[7, 1, 3] - B.
[1, 6, 5] - C.
[0, 1, 2] - D.
[1.5, 2.5]
- A.
- 种子(seed)的作用是?
- A. 让随机序列更快
- B. 让相同种子生成相同序列(可复现)
- C. 让随机数更均匀
- D. 没有作用
- 同一个 rng 连续调用两次
uniform,两次结果?- A. 一定相同
- B. 通常不同
- C. 一定不同
- D. 报错
rng.choice(items, size=3, replace=False)中replace=False表示?- A. 允许重复抽取
- B. 不重复抽取
- C. 按顺序抽取
- D. 只抽一次
rng.shuffle(a)的返回值是?- A. 打乱后的数组
- B.
None - C.
a的副本 - D. 布尔数组
- 蒙特卡洛估算 π 的正确比例关系是?
- A. π = 4 × 圆内点数/总点数
- B. π = 圆内点数/总点数
- C. π = 总点数/圆内点数
- D. π = 4 × 总点数/圆内点数
- 蒙特卡洛方法的估计误差随样本量增加?
- A. 增大
- B. 减小
- C. 不变
- D. 随机波动
- 模拟「掷骰子」应使用哪个方法?
- A.
rng.uniform(1, 6) - B.
rng.integers(1, 7, size=n) - C.
rng.normal(3.5, 1) - D.
rng.shuffle
- A.
- 概率参数
p的要求是?- A. 必须都是 0.5
- B. 与数组等长且和为 1
- C. 必须是整数
- D. 与数组无关
