跳到主要内容

NumPy 随机数生成

随机数生成是数值计算、模拟仿真和机器学习中的重要组成部分。NumPy 提供了功能强大的随机数生成模块 numpy.random,支持多种概率分布和采样方法。本章将详细介绍 NumPy 随机数生成的使用方法和最佳实践。

Generator:新一代随机数生成器

从 NumPy 1.17 开始,推荐使用新的 Generator API。相比旧的 RandomStateGenerator 具有更好的统计特性和更高的性能。

创建 Generator

使用 default_rng 函数创建 Generator 实例:

import numpy as np

# 创建默认的随机数生成器
rng = np.random.default_rng()
print(f"Generator 类型: {type(rng)}")

# 使用种子创建可复现的生成器
rng = np.random.default_rng(seed=42)
print(f"带种子的 Generator: {rng}")

为什么使用种子?

种子决定了随机数序列的起点。使用相同的种子可以保证结果可复现,这在调试、测试和科学研究中非常重要。

import numpy as np

# 相同种子产生相同的随机数序列
rng1 = np.random.default_rng(seed=42)
rng2 = np.random.default_rng(seed=42)

print(f"rng1 第一个随机数: {rng1.random()}")
print(f"rng2 第一个随机数: {rng2.random()}")
# 两者输出相同

# 不使用种子,每次运行结果不同
rng3 = np.random.default_rng()
rng4 = np.random.default_rng()
print(f"\n不同种子:")
print(f"rng3: {rng3.random()}")
print(f"rng4: {rng4.random()}")

推荐的种子生成方式

对于需要高质量随机性的场景,可以使用 secrets 模块生成种子:

import numpy as np
import secrets

# 生成一个 128 位的随机种子
seed = secrets.randbits(128)
print(f"随机种子: {seed}")

rng = np.random.default_rng(seed)
print(f"随机数: {rng.random()}")

基本随机数生成

浮点数随机数

random 方法生成 [0, 1) 区间内均匀分布的浮点数:

import numpy as np

rng = np.random.default_rng(seed=42)

# 生成单个随机浮点数
single = rng.random()
print(f"单个随机数: {single}")

# 生成指定数量的随机数
arr = rng.random(5)
print(f"5 个随机数: {arr}")

# 生成指定形状的随机数组
matrix = rng.random((3, 4))
print(f"3x4 随机矩阵:\n{matrix}")

整数随机数

integers 方法生成指定范围内的随机整数:

import numpy as np

rng = np.random.default_rng(seed=42)

# 生成 [0, 10) 区间的随机整数(不包含 10)
single_int = rng.integers(0, 10)
print(f"单个整数: {single_int}")

# 生成多个随机整数
arr_int = rng.integers(0, 10, size=5)
print(f"5 个整数: {arr_int}")

# 只指定上限,默认下限为 0
arr_int2 = rng.integers(10, size=5)
print(f"[0, 10) 的 5 个整数: {arr_int2}")

# 包含上限(endpoint=True)
arr_int3 = rng.integers(0, 5, size=10, endpoint=True)
print(f"[0, 5] 包含端点: {arr_int3}")

# 多维数组
matrix_int = rng.integers(0, 100, size=(3, 4))
print(f"3x4 整数矩阵:\n{matrix_int}")

从数组中随机选择

choice 方法从给定的一维数组中随机采样:

import numpy as np

rng = np.random.default_rng(seed=42)

# 从列表中随机选择
arr = np.array([10, 20, 30, 40, 50])
single_choice = rng.choice(arr)
print(f"单个选择: {single_choice}")

# 选择多个元素(可重复)
multi_choice = rng.choice(arr, size=5)
print(f"多次选择(可重复): {multi_choice}")

# 不重复选择
unique_choice = rng.choice(arr, size=3, replace=False)
print(f"不重复选择: {unique_choice}")

# 带概率权重的选择
probabilities = [0.1, 0.1, 0.1, 0.1, 0.6] # 50 被选中的概率最大
weighted_choice = rng.choice(arr, size=10, p=probabilities)
print(f"加权选择: {weighted_choice}")

随机字节

bytes 方法生成随机字节:

import numpy as np

rng = np.random.default_rng(seed=42)

# 生成随机字节
random_bytes = rng.bytes(10)
print(f"10 个随机字节: {random_bytes}")

均匀分布

均匀分布是最基本的概率分布,所有值在指定区间内出现的概率相等。

连续均匀分布

uniform 方法生成指定区间内的均匀分布随机数:

import numpy as np

rng = np.random.default_rng(seed=42)

# [low, high) 区间的均匀分布
uniform_samples = rng.uniform(0, 10, size=10)
print(f"[0, 10) 均匀分布: {uniform_samples}")

# 默认是 [0, 1),与 random() 类似
uniform_default = rng.uniform(size=5)
print(f"[0, 1) 均匀分布: {uniform_default}")

# 验证分布特性
large_samples = rng.uniform(0, 1, size=100000)
print(f"\n100000 个样本统计:")
print(f"均值(理论值 0.5): {np.mean(large_samples):.4f}")
print(f"标准差(理论值 {np.sqrt(1/12):.4f}): {np.std(large_samples):.4f}")

离散均匀分布

使用 integers 方法生成离散均匀分布:

import numpy as np

rng = np.random.default_rng(seed=42)

# 模拟掷骰子(1-6)
dice_rolls = rng.integers(1, 7, size=100)
print(f"100 次骰子投掷: {dice_rolls[:20]}...")

# 统计各点数出现次数
unique, counts = np.unique(dice_rolls, return_counts=True)
print(f"\n点数分布:")
for u, c in zip(unique, counts):
print(f" {u}: {c} 次 ({c/100:.1%})")

正态分布

正态分布(高斯分布)是最重要的概率分布,在自然科学和社会科学中广泛应用。

标准正态分布

standard_normal 方法生成标准正态分布(均值=0,标准差=1):

import numpy as np

rng = np.random.default_rng(seed=42)

# 标准正态分布
standard_normal = rng.standard_normal(10)
print(f"标准正态分布: {standard_normal}")

# 验证统计特性
large_samples = rng.standard_normal(100000)
print(f"\n100000 个样本统计:")
print(f"均值(理论值 0): {np.mean(large_samples):.4f}")
print(f"标准差(理论值 1): {np.std(large_samples):.4f}")

一般正态分布

normal 方法可以指定均值和标准差:

import numpy as np

rng = np.random.default_rng(seed=42)

# 指定均值和标准差
# normal(loc=均值, scale=标准差, size=形状)
normal_samples = rng.normal(loc=50, scale=10, size=10)
print(f"均值50,标准差10的正态分布: {normal_samples}")

# 模拟身高分布(假设平均身高 170cm,标准差 8cm)
heights = rng.normal(loc=170, scale=8, size=1000)
print(f"\n1000 人身高统计:")
print(f"平均身高: {np.mean(heights):.1f} cm")
print(f"最高: {np.max(heights):.1f} cm")
print(f"最矮: {np.min(heights):.1f} cm")
print(f"95% 置信区间: [{np.percentile(heights, 2.5):.1f}, {np.percentile(heights, 97.5):.1f}] cm")

对数正态分布

当变量的对数服从正态分布时,使用对数正态分布:

import numpy as np

rng = np.random.default_rng(seed=42)

# 对数正态分布
# lognormal(mean=对数均值, sigma=对数标准差)
lognormal_samples = rng.lognormal(mean=0, sigma=1, size=10)
print(f"对数正态分布样本: {lognormal_samples}")

# 对数正态分布常用于模拟收入、价格等正偏态数据
incomes = rng.lognormal(mean=10.5, sigma=0.5, size=1000)
print(f"\n模拟收入分布:")
print(f"中位数收入: {np.median(incomes):.0f}")
print(f"平均收入: {np.mean(incomes):.0f}")

其他常用分布

指数分布

指数分布常用于模拟等待时间:

import numpy as np

rng = np.random.default_rng(seed=42)

# 指数分布(scale = 1/λ,即平均等待时间)
# 模拟顾客到达的平均等待时间为 5 分钟
wait_times = rng.exponential(scale=5, size=1000)
print(f"平均等待时间: {np.mean(wait_times):.2f} 分钟")
print(f"标准差: {np.std(wait_times):.2f} 分钟")
print(f"最大等待时间: {np.max(wait_times):.2f} 分钟")

泊松分布

泊松分布用于模拟单位时间内事件发生的次数:

import numpy as np

rng = np.random.default_rng(seed=42)

# 泊松分布(lam = 平均发生次数)
# 模拟每小时平均接到 5 个电话
calls_per_hour = rng.poisson(lam=5, size=24) # 24 小时
print(f"24 小时每小时电话数: {calls_per_hour}")
print(f"总电话数: {np.sum(calls_per_hour)}")

# 二项分布:n 次试验中成功的次数
# 模拟抛 10 次硬币,正面朝上的次数(重复 1000 次)
coin_flips = rng.binomial(n=10, p=0.5, size=1000)
print(f"\n10 次抛硬币中正面次数分布:")
unique, counts = np.unique(coin_flips, return_counts=True)
for u, c in zip(unique, counts):
print(f" {u} 次: {c} 次 ({c/1000:.1%})")

Beta 分布

Beta 分布常用于贝叶斯统计,表示概率的概率分布:

import numpy as np

rng = np.random.default_rng(seed=42)

# Beta 分布在 [0, 1] 区间
# 参数 a, b 控制分布形状
# a=1, b=1: 均匀分布
# a > b: 左偏
# a < b: 右偏
# a = b: 对称

# 模拟转化率的先验分布
# 假设之前观察到 80 次成功,20 次失败
conversion_rates = rng.beta(a=81, b=21, size=10)
print(f"转化率样本: {conversion_rates}")
print(f"平均转化率: {np.mean(conversion_rates):.4f}")

Gamma 分布

Gamma 分布是指数分布的推广:

import numpy as np

rng = np.random.default_rng(seed=42)

# Gamma 分布
# shape (k): 形状参数
# scale (θ): 尺度参数
# 均值 = k * θ

gamma_samples = rng.gamma(shape=2, scale=2, size=10)
print(f"Gamma 分布样本: {gamma_samples}")
print(f"理论均值: {2 * 2}")
print(f"实际均值: {np.mean(gamma_samples):.2f}")

卡方分布

卡方分布广泛用于假设检验和置信区间估计:

import numpy as np

rng = np.random.default_rng(seed=42)

# 卡方分布(df = 自由度)
# 常用于假设检验
chi2_samples = rng.chisquare(df=5, size=10)
print(f"卡方分布样本 (df=5): {chi2_samples}")

# 验证:卡方分布的均值 = df
large_samples = rng.chisquare(df=10, size=100000)
print(f"\ndf=10 的卡方分布:")
print(f"均值(理论值 10): {np.mean(large_samples):.4f}")
print(f"方差(理论值 20): {np.var(large_samples):.4f}")

F 分布

F 分布用于比较两个方差的比值,在方差分析(ANOVA)中常用:

import numpy as np

rng = np.random.default_rng(seed=42)

# F 分布(dfnum = 分子自由度, dfden = 分母自由度)
f_samples = rng.f(dfnum=5, dfden=10, size=10)
print(f"F 分布样本 (df1=5, df2=10): {f_samples}")

# 应用:ANOVA 中的 F 检验模拟
# 模拟 F 统计量的分布
f_stats = rng.f(dfnum=3, dfden=27, size=10000)
critical_value = np.percentile(f_stats, 95)
print(f"\nF(3, 27) 分布 95% 分位点: {critical_value:.4f}")

威布尔分布

威布尔分布常用于可靠性分析和寿命测试:

import numpy as np

rng = np.random.default_rng(seed=42)

# 威布尔分布(a = 形状参数)
# a < 1: 失效率递减(早期失效)
# a = 1: 失效率恒定(随机失效,即指数分布)
# a > 1: 失效率递增(磨损失效)

# 形状参数 a = 1.5(失效率递增,模拟设备老化)
weibull_samples = rng.weibull(a=1.5, size=10)
print(f"威布尔分布样本 (a=1.5): {weibull_samples}")

# 应用:设备寿命模拟
device_lifetimes = rng.weibull(a=2.0, size=1000) * 1000 # 单位:小时
print(f"\n设备寿命统计:")
print(f"平均寿命: {np.mean(device_lifetimes):.0f} 小时")
print(f"中位数寿命: {np.median(device_lifetimes):.0f} 小时")
print(f"可靠度 90% 对应时间: {np.percentile(device_lifetimes, 10):.0f} 小时")

负二项分布

负二项分布用于计算达到指定成功次数所需的试验次数:

import numpy as np

rng = np.random.default_rng(seed=42)

# 负二项分布(n = 成功次数, p = 成功概率)
# 返回达到 n 次成功所需的失败次数

# 模拟销售电话:平均需要打多少个失败电话才能获得 5 个客户
# 假设成功率 p = 0.2
failed_calls = rng.negative_binomial(n=5, p=0.2, size=10)
total_calls = failed_calls + 5 # 加上成功的 5 次
print(f"获得 5 个客户所需的总电话数: {total_calls}")

# 大样本统计
all_calls = rng.negative_binomial(n=5, p=0.2, size=10000) + 5
print(f"\n获得 5 个客户所需电话数统计:")
print(f"平均: {np.mean(all_calls):.1f} 次")
print(f"理论均值: {5/0.2:.1f} 次")

多项分布

多项分布是二项分布的推广,用于多种可能结果的试验:

import numpy as np

rng = np.random.default_rng(seed=42)

# 多项分布(n = 试验次数, pvals = 各结果的概率)
# 模拟掷骰子 10 次
dice_probs = [1/6] * 6 # 每个面概率相等
results = rng.multinomial(n=10, pvals=dice_probs)
print(f"掷骰子 10 次各点数出现次数: {results}")
print(f"验证总和: {sum(results)}")

# 应用:市场调研模拟
# 4 个品牌的市场份额调查
market_share = [0.35, 0.30, 0.20, 0.15] # 品牌 A, B, C, D 的市场份额
survey = rng.multinomial(n=1000, pvals=market_share)
print(f"\n1000 人调查结果:")
brands = ['品牌A', '品牌B', '品牌C', '品牌D']
for brand, count in zip(brands, survey):
print(f" {brand}: {count} 人 ({count/10:.1f}%)")

三角分布

三角分布适用于只知道最小值、最大值和最可能值的情况:

import numpy as np

rng = np.random.default_rng(seed=42)

# 三角分布(left = 最小值, mode = 众数, right = 最大值)
# 项目工期估计:最少 5 天,最可能 8 天,最多 15 天
durations = rng.triangular(left=5, mode=8, right=15, size=10)
print(f"项目工期估计(天): {durations}")

# 大样本分析
all_durations = rng.triangular(left=5, mode=8, right=15, size=10000)
print(f"\n工期估计统计:")
print(f"均值: {np.mean(all_durations):.1f} 天")
print(f"中位数: {np.median(all_durations):.1f} 天")
print(f"80% 完工时间: {np.percentile(all_durations, 80):.1f} 天")

几何分布

几何分布计算首次成功所需的试验次数:

import numpy as np

rng = np.random.default_rng(seed=42)

# 几何分布(p = 成功概率)
# 返回首次成功前的失败次数

# 模拟销售电话:需要打多少次才能成交(成功率 20%)
failures = rng.geometric(p=0.2, size=10)
print(f"首次成交所需的电话次数: {failures}")

# 验证:几何分布的均值 = 1/p
large_samples = rng.geometric(p=0.2, size=10000)
print(f"\n统计:")
print(f"平均电话次数: {np.mean(large_samples):.1f}")
print(f"理论均值: {1/0.2:.1f}")

拉普拉斯分布

拉普拉斯分布比正态分布有更尖锐的峰值和更重的尾部:

import numpy as np

rng = np.random.default_rng(seed=42)

# 拉普拉斯分布(loc = 位置参数, scale = 尺度参数)
laplace_samples = rng.laplace(loc=0, scale=1, size=10)
print(f"拉普拉斯分布样本: {laplace_samples}")

# 对比拉普拉斯分布和正态分布
laplace_data = rng.laplace(loc=0, scale=1, size=10000)
normal_data = rng.normal(loc=0, scale=1, size=10000)

print(f"\n分布对比(10000 样本):")
print(f"拉普拉斯 - 峰度: {np.mean((laplace_data - np.mean(laplace_data))**4) / np.var(laplace_data)**2:.2f}")
print(f"正态 - 峰度: {np.mean((normal_data - np.mean(normal_data))**4) / np.var(normal_data)**2:.2f}")
# 拉普拉斯分布的峰度大于正态分布(3),表示更尖锐的峰值

多元正态分布

生成相关的多维随机变量:

import numpy as np

rng = np.random.default_rng(seed=42)

# 多元正态分布
mean = [0, 0] # 均值向量
cov = [[1, 0.8], # 协方差矩阵
[0.8, 1]]

# 生成二维正态分布样本
samples = rng.multivariate_normal(mean, cov, size=1000)
print(f"样本形状: {samples.shape}")

# 计算相关系数
correlation = np.corrcoef(samples[:, 0], samples[:, 1])[0, 1]
print(f"生成数据的相关系数: {correlation:.4f}")
print(f"理论相关系数: 0.8")

随机排列与洗牌

shuffle:原地洗牌

shuffle 方法直接修改原数组:

import numpy as np

rng = np.random.default_rng(seed=42)

# 一维数组洗牌
arr = np.arange(10)
print(f"原数组: {arr}")
rng.shuffle(arr)
print(f"洗牌后: {arr}")

# 多维数组:默认按第一个轴洗牌
arr_2d = np.arange(12).reshape(3, 4)
print(f"\n原二维数组:\n{arr_2d}")
rng.shuffle(arr_2d)
print(f"洗牌后(按行):\n{arr_2d}")

# 洗牌 Python 列表
my_list = ['A', 'B', 'C', 'D', 'E']
rng.shuffle(my_list)
print(f"\n列表洗牌后: {my_list}")

permutation:返回排列副本

permutation 方法不修改原数组,返回新的排列:

import numpy as np

rng = np.random.default_rng(seed=42)

# 一维数组
arr = np.arange(10)
permuted = rng.permutation(arr)
print(f"原数组: {arr}")
print(f"排列后: {permuted}")
print(f"原数组未变: {arr}")

# 多维数组按轴排列
arr_2d = np.arange(12).reshape(3, 4)
permuted_2d = rng.permutation(arr_2d, axis=0)
print(f"\n按行排列:\n{permuted_2d}")

permuted_2d_col = rng.permutation(arr_2d, axis=1)
print(f"\n按列排列:\n{permuted_2d_col}")

permuted:独立排列各切片

permuted 方法可以独立地排列每个切片:

import numpy as np

rng = np.random.default_rng(seed=42)

arr = np.arange(12).reshape(3, 4)
print(f"原数组:\n{arr}")

# 每行独立洗牌
permuted = rng.permuted(arr, axis=1)
print(f"\n每行独立洗牌:\n{permuted}")

# 原地操作
rng.permuted(arr, axis=1, out=arr)
print(f"\n原地洗牌后:\n{arr}")

三种排列方法的区别

import numpy as np

rng = np.random.default_rng(seed=42)
arr = np.arange(12).reshape(3, 4)

print("=== 三种排列方法对比 ===")
print(f"原数组:\n{arr}")

# shuffle: 原地修改,按第一轴整体洗牌
arr1 = arr.copy()
rng.shuffle(arr1)
print(f"\nshuffle: 原地,整体洗牌\n{arr1}")

# permutation: 返回副本,按指定轴整体洗牌
arr2 = rng.permutation(arr, axis=1)
print(f"\npermutation: 副本,整体洗牌(按列)\n{arr2}")

# permuted: 可以原地或副本,每切片独立洗牌
arr3 = rng.permuted(arr, axis=1)
print(f"\npermuted: 每行独立洗牌\n{arr3}")

并行随机数生成

在并行计算中,需要确保各个进程的随机数序列相互独立:

import numpy as np

# 使用 SeedSequence 创建独立的随机数生成器
seed = 42
ss = np.random.SeedSequence(seed)

# 创建多个独立的子种子
child_seeds = ss.spawn(4) # 生成 4 个独立的种子

# 为每个子种子创建独立的 Generator
generators = [np.random.default_rng(s) for s in child_seeds]

# 每个生成器产生独立的随机数序列
for i, rng in enumerate(generators):
print(f"Generator {i}: {rng.random(3)}")

实战示例

示例1:模拟股票价格走势

使用几何布朗运动模拟股票价格:

import numpy as np

def simulate_stock_price(S0, mu, sigma, T, dt, n_simulations):
"""
模拟股票价格走势

参数:
S0: 初始价格
mu: 预期收益率
sigma: 波动率
T: 总时间(年)
dt: 时间步长
n_simulations: 模拟次数
"""
rng = np.random.default_rng(seed=42)
n_steps = int(T / dt)

# 生成随机增量
Z = rng.standard_normal((n_simulations, n_steps))

# 计算价格路径
drift = (mu - 0.5 * sigma ** 2) * dt
diffusion = sigma * np.sqrt(dt) * Z

# 累积对数收益
log_returns = drift + diffusion
log_prices = np.log(S0) + np.cumsum(log_returns, axis=1)
prices = np.exp(log_prices)

# 添加初始价格
prices = np.column_stack([np.full(n_simulations, S0), prices])

return prices

# 模拟参数
S0 = 100 # 初始价格
mu = 0.1 # 年化收益率 10%
sigma = 0.2 # 年化波动率 20%
T = 1.0 # 1 年
dt = 1/252 # 日频

prices = simulate_stock_price(S0, mu, sigma, T, dt, 5)

print("股票价格模拟(5 条路径):")
print(f"初始价格: {prices[0, 0]:.2f}")
print(f"最终价格范围: [{prices[:, -1].min():.2f}, {prices[:, -1].max():.2f}]")

示例2:蒙特卡洛积分

使用随机采样估计积分值:

import numpy as np

def monte_carlo_integral(func, a, b, n_samples):
"""
使用蒙特卡洛方法估计积分
∫[a,b] f(x) dx ≈ (b-a) * mean(f(X))
"""
rng = np.random.default_rng(seed=42)

# 在 [a, b] 区间均匀采样
samples = rng.uniform(a, b, n_samples)

# 计算函数值
values = func(samples)

# 积分估计
integral = (b - a) * np.mean(values)

# 标准误差
std_error = (b - a) * np.std(values) / np.sqrt(n_samples)

return integral, std_error

# 估计 ∫[0, π] sin(x) dx = 2
integral, error = monte_carlo_integral(np.sin, 0, np.pi, 100000)
print(f"估计值: {integral:.6f}")
print(f"真实值: {2.0:.6f}")
print(f"误差: {abs(integral - 2):.6f}")
print(f"标准误差: {error:.6f}")

示例3:数据增强

在机器学习中使用随机采样进行数据增强:

import numpy as np

def augment_data(X, noise_level=0.1, n_augment=5):
"""
对数据进行随机增强

参数:
X: 原始数据 (n_samples, n_features)
noise_level: 噪声强度
n_augment: 每个样本增强的次数
"""
rng = np.random.default_rng(seed=42)
n_samples, n_features = X.shape

augmented = []
for _ in range(n_augment):
# 添加高斯噪声
noise = rng.normal(0, noise_level, X.shape)
augmented.append(X + noise)

# 合并原始数据和增强数据
return np.vstack([X] + augmented)

# 模拟数据
X = np.array([[1, 2], [3, 4], [5, 6]])
print(f"原始数据:\n{X}")

X_augmented = augment_data(X, noise_level=0.1, n_augment=2)
print(f"\n增强后数据形状: {X_augmented.shape}")
print(f"增强后数据:\n{X_augmented}")

示例4:随机采样模拟

模拟抽样调查:

import numpy as np

rng = np.random.default_rng(seed=42)

# 总体(假设 10000 人)
population_size = 10000
support_rate = 0.52 # 真实支持率
population = rng.binomial(1, support_rate, population_size)

print(f"总体支持率: {population.mean():.4f}")

# 不同样本量的抽样
for sample_size in [100, 500, 1000, 5000]:
sample = rng.choice(population, size=sample_size, replace=False)
sample_rate = sample.mean()
margin_error = 1.96 * np.sqrt(sample_rate * (1 - sample_rate) / sample_size)
print(f"样本量 {sample_size:4d}: 支持率 {sample_rate:.4f} ± {margin_error:.4f}")

性能优化

使用 Generator 而非全局函数

import numpy as np
import time

# 旧方式:使用全局 RandomState
start = time.time()
for _ in range(1000):
np.random.rand(100)
time_old = time.time() - start

# 新方式:使用 Generator
rng = np.random.default_rng()
start = time.time()
for _ in range(1000):
rng.random(100)
time_new = time.time() - start

print(f"旧方式: {time_old:.4f}s")
print(f"新方式: {time_new:.4f}s")

预生成随机数

如果需要大量随机数,一次性生成比分批生成更高效:

import numpy as np
import time

rng = np.random.default_rng(seed=42)

# 方式1:分批生成
start = time.time()
batch_results = []
for _ in range(1000):
batch_results.append(rng.random(100))
time_batch = time.time() - start

# 方式2:一次性生成
rng = np.random.default_rng(seed=42)
start = time.time()
all_at_once = rng.random((1000, 100))
time_once = time.time() - start

print(f"分批生成: {time_batch:.4f}s")
print(f"一次性生成: {time_once:.4f}s")

Generator vs RandomState

NumPy 仍保留旧的 RandomState API,但推荐使用新的 Generator

import numpy as np

# 旧方式(不推荐)
np.random.seed(42)
old_random = np.random.rand(5)
print(f"旧方式: {old_random}")

# 新方式(推荐)
rng = np.random.default_rng(42)
new_random = rng.random(5)
print(f"新方式: {new_random}")

# 主要区别:
# 1. Generator 使用更好的算法(PCG64)
# 2. Generator 方法更一致
# 3. Generator 更快
# 4. Generator 支持更多分布

小结

本章介绍了 NumPy 随机数生成的核心功能:

功能方法说明
创建生成器default_rng(seed)推荐的创建方式
浮点数random(size)[0, 1) 均匀分布
整数integers(low, high, size)指定范围整数
选择choice(arr, size)从数组采样
正态分布normal(loc, scale, size)高斯分布
均匀分布uniform(low, high, size)均匀分布
洗牌shuffle(arr)原地洗牌
排列permutation(arr)返回排列副本

最佳实践

  1. 使用 Generator 而非 RandomState
  2. 使用种子确保可复现性
  3. 大量随机数时一次性生成
  4. 并行计算时使用 SeedSequence.spawn

练习

  1. 模拟掷两个骰子 10000 次,统计点数之和的分布
  2. 使用蒙特卡洛方法估计圆周率
  3. 生成符合指定均值和方差的正态分布数据
  4. 实现一个简单的随机游走模拟
  5. 使用 Bootstrap 方法估计数据的置信区间