为什么 norm.cdf 在 scipy 中比 norm.pdf 快?

Why norm.cdf is faster than norm.pdf in scipy?

我现在使用 scipy 进行一些 norm.pdfnorm.cdf 计算。我想知道为什么 cdfpdf 快?

我知道 norm.cdf 有一些渐进的方法,而 scipy 似乎使用了 norm.pdf 的积分。这就是为什么我无法想象 cdfpdf 快。如果集成是这样的话,cdf 应该比 pdf 慢很多(也许并行计算能帮上大忙?);如果应用渐近方法,我仍然认为 cdf 可能比 pdf.

慢一点

下面显示了一些简单的示例:

import scipy.stats as st
from datetime import datetime
import numpy as np
num_iter = 100000
x_lower = 0.25
x_upper = 0.75

time_start = datetime.now()
for x in np.arange(x_lower, x_upper, (x_upper - x_lower) / (num_iter - 1)):
    y = st.norm.pdf(x)
time_end = datetime.now()
print(time_end - time_start)

time_start = datetime.now()
for x in np.arange(x_lower, x_upper, (x_upper - x_lower) / (num_iter - 1)):
    y = st.norm.cdf(x)
time_end = datetime.now()
print(time_end - time_start)

这里是 运行 结果:

0:00:05.736985
0:00:04.896390

快速查看源代码表明 scipy.stats.norm.pdf 只是 returns 使用 NumPy 的 pdf x 的值:

def _norm_pdf(x):
return np.exp(-x**2/2.0) / _norm_pdf_C

其中 _norm_pdf_C = np.sqrt(2*np.pi).

对于cdf,因为我们说的是正态分布,所以使用了特殊函数(关于它们与正态分布的关系,see here)。

SciPy 实现特殊功能 directly in C. In particular, the cumulative distribution function is computed from ndtr.c。所以,即使 NumPy 真的很快,我认为在这种情况下 C 仍然更快。

编辑

抱歉,我刚刚意识到我的回答并没有完全回答你的问题。

首先,NumPy也在C中实现了数学运算。 因此,要了解时代差异的原因,应该了解 C 中发生的情况。

  • 如果你看这个question,似乎数值和硬件架构会影响时间。

所以我再次检查了 cdf 的 C 实现,我看到评估特殊函数的多项式的常数和系数不是计算而是存储在数组和变量中!例如,1/sqrt(2) 包含在 NPY_SQRT1_2 中。这可能是 cdf 比 pdf 更快的原因!

因此我尝试计算具有已初始化常量的 pdf:

import scipy.stats as st
from datetime import datetime
import numpy as np
num_iter = 100000
x_lower = 0.25
x_upper = 0.75

const = np.sqrt(2*np.pi)
time_start = datetime.now()
for x in np.arange(x_lower, x_upper, (x_upper - x_lower) / (num_iter - 1)):
    # y = st.norm.pdf(x)
    y = np.exp((x**2 / 2)) / const
time_end = datetime.now()
print(time_end - time_start)

time_start = datetime.now()
for x in np.arange(x_lower, x_upper, (x_upper - x_lower) / (num_iter - 1)):
    y = st.norm.cdf(x)
time_end = datetime.now()

这段代码给我:

0:00:00.202531
0:00:07.703083

请注意,norm.pdf 也预先初始化了 pdf 的分母,但是在 for 循环中,您每次都在调用该方法,从而减慢速度。

P.S.: 如果您尝试摆脱原始代码中的循环并只使用 x = np.arange(x_lower, x_upper, (x_upper - x_lower) / (num_iter - 1)),cdf 再次更快。原因可能是 cdf 是用多项式近似值计算的。但是我没有找到关于 C 究竟如何处理指数以获得比较的信息。