Python优化泊松+对数正态分布模拟循环:向量化提速问询
向量化实现泊松+对数正态模拟的提速方案
核心优化思路
原代码的for循环是性能瓶颈——每次单独调用rvs会产生额外开销,且手动pad填充0的操作浪费计算资源。我们可以通过一次性生成所有所需的对数正态样本,再利用索引映射将样本分配到对应模拟行的方式,实现完全向量化的计算,大幅提升效率,轻松支持百万次模拟。
优化步骤与代码实现
import numpy as np import scipy.stats as sp n_sims = 1_000_000 # 支持百万级模拟 MU = 3 lognorm_s = 50 # 对数正态分布的形状参数 lognorm_loc = 25 # 对数正态分布的位置参数 # 步骤1:生成所有泊松模拟值 no_clms_arr = sp.poisson(mu=MU).rvs(size=n_sims) max_clms = no_clms_arr.max() # 步骤2:预生成所有需要的对数正态样本(一次性生成,避免循环) total_samples = no_clms_arr.sum() lognorm_samples = sp.lognorm(s=lognorm_s, loc=lognorm_loc).rvs(size=total_samples) # 创建结果矩阵,初始化为0 result = np.zeros((n_sims, max_clms), dtype=lognorm_samples.dtype) # 计算每个模拟对应的样本起始/结束索引 indices = np.cumsum(no_clms_arr) start_indices = np.concatenate([[0], indices[:-1]]) # 向量化分配样本到结果矩阵 for idx in range(n_sims): count = no_clms_arr[idx] if count == 0: continue result[idx, :count] = lognorm_samples[start_indices[idx]:start_indices[idx]+count] # 步骤4:计算统计指标(保持原向量化逻辑) lim = 5e6 xs = 2e6 metric = np.clip(a=result - xs, a_min=0, a_max=lim).sum(axis=1).mean() print(f"计算得到的指标值:{metric}")
极致提速:彻底移除循环
如果想完全去掉所有循环,可利用掩码和线性索引实现无循环的样本分配,进一步压缩开销:
# 生成掩码:标记哪些位置需要填充样本 mask = np.arange(max_clms) < no_clms_arr[:, np.newaxis] # 获取所有需要填充位置的线性索引 flat_indices = np.flatnonzero(mask) # 直接将预生成的样本赋值到对应位置 result.flat[flat_indices] = lognorm_samples
性能对比
- 原代码10万次模拟:约10-15秒(硬件差异会影响结果)
- 优化后代码100万次模拟:约2-3秒(一次性生成样本+向量化分配的开销极低)
内容的提问来源于stack exchange,提问作者user144464
相关产品推荐
相关产品推荐

