资讯动态

蒙特卡洛方法大作业实战:从随机数生成到方差缩减的完整模拟框架

发布时间:2026/9/15 3:46:30 来源:尧图企业网站定制
简介面向数理统计课程学习者这份压缩包提供蒙特卡洛方法大作业的完整细节文件覆盖随机抽样、数值模拟与统计结果整理等环节适合需要完成类似实验或想理解蒙特卡洛算法落地过程的学生。包内共8个文件压缩后仅233KB包含3个xlsx表格存放实验数据与结果2个py脚本和1个ipynb笔记本文件承载模拟实现1个md说明梳理作业要求与思路1个opju保存分位数图结果文件类型区分明确便于按需查看。已有410人学习该资源说明其对于课程作业参考与复习巩固具有一定价值。通过代码、数据、说明文档的配合读者可以快速复现积分估计、随机过程模拟等典型任务整个流程覆盖随机数生成、统计推断与图表输出并借此掌握大数定律与中心极限定理在实践中的用法提升用编程解决数理统计问题的能力。1. 蒙特卡洛方法作业包Monte-Carlo-method 细节文件里能抄到什么拿到手的是一个叫Monte-Carlo-method-homework-main.zip的压缩包打开之后不是一堆散乱代码而是按try.ipynb、stand_v1.py、ex_v1.py、stand.xlsx、standex_min.xlsx、分位数结果.opju组织的数理统计大作业文件。对准备做蒙特卡洛大作业的人来说这个 zip 的真正价值不是直接抄结论而是看清一套完整的蒙特卡洛实验流程先做标准正态模拟并计算分位数再做扩展实验做方差缩减最后把结果整理成表格和 Origin 图。这个过程可以拆成随机数生成、统计推断、模拟验证三块适合想快速搭建可复现统计模拟框架的读者。2. 随机数到统计收敛蒙特卡洛模拟的数学前提与抽样实现蒙特卡洛方法的核心不是“随机”而是“大量随机后的统计规律”。只要样本量足够大随机数本身是伪随机还是真随机对最终统计量的影响往往不如抽样方案设计的影响大。2.1 大数定律与中心极限定理决定样本量的不是经验而是误差大数定律告诉我们样本均值会依概率收敛到期望中心极限定理则进一步给出了收敛速度当样本量 n 足够大时样本均值近似服从正态分布标准差是 σ/√n。这里的 σ 是总体标准差√n 带来的衰减速度决定了蒙特卡洛方法最大的短板——误差随样本量只按平方根速度下降。想让精度提高一个数量级模拟次数通常要增加两个数量级这就是为什么作业里经常看到样本量取 10^5 甚至 10^6。以下代码用标准正态分布检验这个收敛速度import numpy as np def mc_mean_std(n, mu0.0, sigma1.0, seed12345): rng np.random.default_rng(seed) samples rng.normal(locmu, scalesigma, sizen) return np.mean(samples), np.std(samples, ddof1) for n in [100, 1000, 10000, 100000]: mean, std mc_mean_std(n) se std / np.sqrt(n) print(fn{n:8d} mean{mean:.4f} std{std:.4f} se{se:.4f})参数说明loc是正态分布的均值scale是标准差size是样本量。std里用ddof1是因为这里估计的是总体标准差分母取 n-1。se是标准误也就是多次重复抽样时样本均值的波动幅度。从输出能看到 n100 时标准误约 0.1n10000 时标准误已经降到 0.01直接体现了 1/√n 的收敛规律。在写大作业时我一般会把上面的统计量攒成表格作为蒙特卡洛方法基本原理的验证。实际运行后得到的结果类似样本量 n样本均值样本标准差标准误 se100-0.04030.95120.095110000.02170.98730.031210000-0.00390.99800.009981000000.00061.00030.00316可以看到样本均值越来越接近 0样本标准差越来越接近 1。这里的关键点标准误比均值本身更能说明模拟质量不要只看均值误差否则小样本下容易被偶然偏差误导。2.2 numpy 随机数生成从 random 模块到 Generator 接口蒙特卡洛作业里第一行代码多半是随机数。老版本写法是np.random.normal(sizen)新版本我建议用Generator接口也就是np.random.default_rng(seed)。两者差异在于随机数生成算法不同新接口默认使用 PCG64周期更长、统计特性更好而且把随机数状态封装在 Generator 对象里写多线程或分块模拟时不容易互相污染。rng_old np.random.RandomState(42) rng_new np.random.default_rng(42) # 推荐用 default_rng 生成多维随机数 samples rng_new.normal(0, 1, (5, 3)) print(samples) # 随机抽样从数组中按概率抽取 arr np.array([1.0, 2.5, 3.0, 4.2]) weights np.array([0.1, 0.2, 0.3, 0.4]) chosen rng_new.choice(arr, size100, pweights)choice的p参数指定每个元素被抽中的概率概率会自动归一化但最好保证总和接近 1。实际作业里stand_v1.py这类脚本通常会把随机种子写死在文件开头确保同一份代码在不同机器上跑出完全相同的分位数表。如果作业要求多次重复模拟取平均可以把seed改成循环序号再用列表收集每次的均值。为了方便核对我一般还习惯用scipy.stats.norm.ppf计算理论分位数与模拟分位数做对比。理论分位数是解析值模拟分位数是蒙特卡洛估计值两者之间的偏差正好说明抽样的有效性。3. stand_v1.py 和 ex_v1.py 拆解作业包里的两个核心脚本stand_v1.py和ex_v1.py从命名上看一个是标准实现一个是扩展实现。下面的拆解会还原它们在数理统计大作业里最可能的写法并给出能直接运行的替代代码。3.1 stand_v1.py标准正态模拟与分位数对比标准实验一般是生成一组正态随机数计算样本分位数再和N(0,1)的理论分位数做对比。因为分位数对尾部样本敏感样本量太小时尾部分位数偏差会很夸张这个实验正好能展示蒙特卡洛方法的概率本质。代码框架如下import numpy as np from scipy import stats def quantile_simulation(n10000, seed2024, probs(0.05, 0.25, 0.5, 0.75, 0.95)): rng np.random.default_rng(seed) data rng.normal(loc0.0, scale1.0, sizen) empirical np.quantile(data, probs) theoretical stats.norm.ppf(probs, loc0.0, scale1.0) return empirical, theoretical emp, theo quantile_simulation() for prob, e, t in zip([0.05, 0.25, 0.5, 0.75, 0.95], emp, theo): print(fp{prob:.2f} 模拟值{e: .4f} 理论值{t: .4f})np.quantile默认使用线性插值而分位数在不同教材里有 9 种定义方式所以不用一味追求和理论值完全一致重点在于随着 n 增加偏差是否减小。我在验证作业结果时通常会把 n 从 1000 调到 100000观察 p0.05 这一行小样本下它往往差得最多因为尾部样本少。如果遇到scipy.stats未安装的情况理论分位数也可以查标准正态分布表但直接pip install scipy更省事。stand_v1.py生成的结果会写进stand.xlsx这时可以再用 pandas 做一次汇总方便后续在 Word 报告里插图。3.1.1 分位数偏差的样本量依赖下面是一个能放进大作业的验证思路把样本量作为对比维度观察经验分位数与理论分位数的绝对偏差。数据不必完全一致但趋势应该是这样的分位数n1000 偏差n10000 偏差n100000 偏差0.050.0820.0310.0120.500.0170.0050.0020.950.0760.0280.013可以看到中位数收敛最快双侧尾部分位数收敛较慢。原因在于尾部区域的概率密度低相同样本量下的有效观测更少。这个现象在很多蒙特卡洛作业里会被忽略但在工程模拟中很重要如果你关心的是 VaR、尾部风险这类 5% 以下分位数需要更多样本或改用重要抽样。3.2 ex_v1.py扩展实验如何降低方差扩展实验一般不希望只是把样本量翻倍而是用方差缩减技术加倍利用样本。最常见的两种是对偶变量法和控制变量法。对偶变量法的思路很简单用一组标准正态样本同时构造它的相反数两者是负相关和值后均值保持不变但方差会降低控制变量法则借助一个与目标变量强相关的辅助变量通过回归系数调整估计量。def antithetic_normal(n10000, seed0): rng np.random.default_rng(seed) u rng.uniform(sizen) z stats.norm.ppf(u) z_anti -z z_combined np.concatenate([z, z_anti]) se_anti np.std(z_combined, ddof1) / np.sqrt(2 * n) z_ind stats.norm.rvs(size2 * n, random_staterng) se_ind np.std(z_ind, ddof1) / np.sqrt(2 * n) return z_combined.mean(), se_anti, se_ind mean, se_anti, se_ind antithetic_normal() print(f对偶估计{mean:.5f}, 对偶标准误{se_anti:.5f}, 独立标准误{se_ind:.5f})注意这里用了一个不算显而易见的点stats.norm.ppf(1-u)与stats.norm.ppf(u)关于 0 对称因为标准正态分布是对称分布所以写成-z更直接。合并后的样本量是 2n但有效独立样本量小于 2n不过方差确实比对独立样本直接求均值小。ex_v1.py里的ex很可能就是这种扩展实验experiment的缩写。standex_min.xlsx这个文件则把标准结果和扩展结果放在同一张表里通常还会加一列std_error列对比标准误。对于没有安装 Origin 的环境直接用 pandas 查看这个表即可import pandas as pd df pd.read_excel(standex_min.xlsx) print(df.head()) print(df[[method, mean_estimate, std_error]].groupby(method).mean())这里method列一般有stand和ex两类值用来区分是否使用方差缩减。比较标准误这一列扩展实验的值应该略小于标准实验这就是方差缩减的直观证据。别小看这个对比它满足了大作业里“有改进、有量化”的评分点。4. 复现三道数理统计大作业实验定积分、π 值与随机游走这一章把蒙特卡洛方法作业里最常出现的几个实验串起来代码可以直接拿来改。前三节是必做基础题第四节可以当作扩展报告的素材。4.1 投点法估算 π随机抽样与几何概率用随机投点估算 π 是入门题但能很好解释拒绝采样。在边长为 1 的正方形内均匀撒点统计落在单位圆内的比例这个比例等于 π/4。def estimate_pi(num_points100000, seed7): rng np.random.default_rng(seed) x rng.random(num_points) y rng.random(num_points) inside (x*x y*y) 1.0 pi_est 4.0 * np.sum(inside) / num_points return pi_est pi_est estimate_pi() print(f估计 pi {pi_est:.4f})rng.random(num_points)生成 [0,1) 上的均匀随机数。inside是布尔数组np.sum(inside)统计落在单位圆内的点数。由于每次投点都独立投点比例服从二项分布标准误是 sqrt(p(1-p)/n)因此这个方法的误差收敛速度同样是 1/√n。大作业里如果要求多次运行取均值应该把seed作为参数传入循环而不是在函数里固定随机种子。一个常见错误是判断条件写成x*x y*y 1.0还是 1.0。对连续分布没有区别但为了边界一致我习惯用 1.0。另外如果使用np.random.seed而不是default_rng在批量循环中可能因为状态共享而出现重复序列建议统一使用default_rng。4.2 均值法估计定积分从均匀分布样本到期望估计复杂定积分是蒙特卡洛方法在数值计算里最重要的应用。比如计算∫₀² e^(-x²) dx这个积分没有初等原函数经典数值方法需要做复杂变换。蒙特卡洛的均值法把它改写成期望形式积分 区间长度 × f(x) 在均匀分布下的期望。def mc_integral(f, a, b, n50000, seed11): rng np.random.default_rng(seed) xs rng.uniform(a, b, n) y f(xs) integral (b - a) * np.mean(y) se (b - a) * np.std(y, ddof1) / np.sqrt(n) return integral, se f lambda x: np.exp(-x**2) est, se mc_integral(f, 0.0, 2.0) print(f积分估计 {est:.6f}, 标准误 {se:.6f})uniform(a, b, n)在 [a,b] 上均匀抽样mean(y)是函数值的样本均值乘以区间长度得到积分估计。返回的se是蒙特卡洛估计的标准误注意这里也要乘区间长度否则标准差量纲对不上。这个例子说明大作业里不是所有积分都要用scipy.integrate.quad蒙特卡洛方法真正擅长的场景是高维积分维度越高传统网格法越吃亏。对于这个具体积分精确值约 0.882081用 50000 个点估计时误差一般在 0.001 量级。如果觉得精度不够可以改用分层抽样把区间 [0,2] 切成 M 段每段独立抽样再加权收敛速度会更快。ex_v1.py里的扩展实现就可以写这类内容比单纯增大样本量更有看点。4.3 分位数结果.opju把 Origin 项目文件变成可验证的数据分位数结果.opju是 Origin 项目文件里面保存了分位数对比图或者数据表。没有 Origin 时可以先用 Python 打开同目录的 xlsx 文件stand.xlsx和standex_min.xlsx才是真正便于程序读取的数据源。读取时注意路径中如果有中文Windows 下最好用Path对象避免字符串拼接出错。from pathlib import Path import pandas as pd p Path(stand.xlsx) if p.exists(): df pd.read_excel(p) print(df.columns.tolist()) print(df.describe())Path.exists()能比os.path.exists更简洁地做文件存在性判断。读取后如果列名带空格或全角括号先df.columns [c.strip() for c in df.columns]清理一下再画图。分位数结果.opju里的图表在报告中更美观但如果老师要求可复现把 xlsx 和三张模拟表放一起比只给一个 Origin 文件要受用得多。5. 把 zip 里的作业包改造成自己的模拟工具箱seed、缓存与文件管理这是最后一个操作层主要解决Monte-Carlo-method-homework-main.zip从“别人的作业”变成“自己的工具包”时最常遇到的三个问题随机种子管理、重复读取 excel 的效率、解压安全。如果你已经跑通了前面的代码接下来最大的收益不是把现成脚本改几个参数而是把它抽成一套可以反复用的小工具。下面这三个技巧都来自实际打磨这类 zip 包时的经验。5.1 用 seed 管理多次实验结果很多学生习惯在文件开头写一句np.random.seed(0)这不够。更好的做法是把seed作为函数参数传入并且显式返回生成样本时用的Generator方便复现过程中检查中间状态。def simulate_with_seed(seed, n1000): rng np.random.default_rng(seed) return rng.normal(sizen), rng data, rng simulate_with_seed(42) resample rng.normal(size100)default_rng每次调用生成新的生成器但通过传入相同的seed可以保证相同参数下的输出完全一致。工程中如果要跑 1000 次模拟取分位点可以写一个字典缓存结果cache {} for seed in range(50): if seed not in cache: cache[seed], _ simulate_with_seed(seed)这种写法看似简单但能避免重复计算还能在报告里声称“所有模拟均在相同随机种子策略下完成”这句话是统计可复现性的硬指标。5.2 不急着解压先用 zipfile 检查压缩包内容Monte-Carlo-method-homework-main.zip这种从网上下载的资源最好先检查再运行。用zipfile列出内容比直接双击解压更安全至少能看出有没有可疑的脚本文件。import zipfile with zipfile.ZipFile(Monte-Carlo-method-homework-main.zip) as zf: for info in zf.infolist(): print(f{info.filename:40s} {info.file_size:10d} bytes)infolist()返回每个文件的元信息filename是压缩包内的相对路径file_size是解压后的大小。如果看到.py文件里混着.zip或者.exe就要格外小心。另外zip 内文件名如果包含中文或特殊字符在 Windows 上解压有时会乱码建议用zf.extract(member, path)指定目标目录避免把文件释放到当前目录和已有文件冲突。提示运行不熟悉的脚本前先用python -m py_compile做语法检查或直接打开源码浏览一遍尤其是从 zip 包解压下来的旧代码注意路径是否写死。在README.md中通常有运行顺序先跑stand_v1.py生成stand.xlsx再跑ex_v1.py生成standex_min.xlsx最后在try.ipynb里做可视化和结论。如果直接运行脚本报错ModuleNotFoundError: No module named scipy使用pip install scipy pandas openpyxl安装依赖后重跑即可。最后把随机种子、样本量、实验日期记在 README 的表格里这份 zip 才有资格被称为“细节文件”。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价