跳到主要内容

NumPy 随机数生成

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

Generator:新一代随机数生成器​

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

创建 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 方法估计数据的置信区间