好的,你似乎对几件事情感到困惑。让我们从头开始:你提到了“多维函数”,但接着又讨论了通常的单变量高斯曲线。这 不是 多维函数:当你对它进行积分时,你只积分一个变量(x)。区别很重要,因为有一个被称为“多元高斯分布”的怪物是真正的多维函数,如果积分,需要对两个或更多变量进行积分(使用我之前提到的昂贵的蒙特卡罗技术)。但你似乎只是在谈论常规的单变量高斯,这更容易处理、积分等。
单变量高斯分布有两个参数,sigma
和 mu
,是一个关于一个变量 x
的函数。你还带着一个归一化参数 n
(在一些应用中很有用)。归一化参数通常不包括在计算中,因为你可以在最后将它们添加回去(记住,积分是一个线性操作符:int(n*f(x), x) = n*int(f(x), x)
)。但如果你愿意,我们可以携带它;我喜欢正态分布的符号是
N(x | mu, sigma, n) := (n/(sigma*sqrt(2*pi))) * exp((-(x-mu)^2)/(2*sigma^2))
(读作“给定 sigma
、mu
和 n
,x
的正态分布是...”) 到目前为止,一切顺利;这与您拥有的函数相匹配。请注意,这里唯一的真实变量是x
:其他三个参数对于任何特定的高斯曲线都是固定的。
现在是一个数学事实:可以证明所有的高斯曲线具有相同的形状,只是稍微移动了一下。因此,我们可以使用N(x|0,1,1)
,称为“标准正态分布”,并将结果转换回一般的高斯曲线。因此,如果你有N(x|0,1,1)
的积分,你可以轻松地计算出任何高斯曲线的积分。这个积分出现得如此频繁,以至于它有一个特殊的名字:误差函数error functionerf
。由于一些旧的惯例,它不是完全等于erf
;还有一些附加和乘法因子也被带着。
如果Phi(z) = integral(N(x|0,1,1), -inf, z)
;也就是说,Phi(z)
是标准正态分布从负无穷到z
的积分,则根据误差函数的定义,有
Phi(z) = 0.5 + 0.5 * erf(z / sqrt(2))
。
同样地,如果
Phi(z | mu, sigma, n) = integral(N(x|sigma, mu, n), -inf, z)
;也就是说,
Phi(z | mu, sigma, n)
是正态分布给定参数
mu
、
sigma
和
n
在从负无穷到
z
的积分,则根据误差函数的定义,以下等式成立:
Phi(z | mu, sigma, n) = (n/2) * (1 + erf((x - mu) / (sigma * sqrt(2))))
。
如果您需要更多细节或证明这个事实,请参阅
维基百科上的正态 CDF。
好的,这应该足够背景解释了。回到您(编辑后的)帖子。您说“scipy.special中的erf(z)需要我最初定义t的确切含义”。我不知道您指的是什么;时间在这里有什么作用吗?希望上面的解释能让误差函数变得更加清晰,现在它更容易理解为什么误差函数是正确的函数。
您的Python代码没问题,但我更喜欢使用闭包而不是lambda:
def make_gauss(N, sigma, mu):
k = N / (sigma * math.sqrt(2*math.pi))
s = -1.0 / (2 * sigma * sigma)
def f(x):
return k * math.exp(s * (x - mu)*(x - mu))
return f
使用闭包可以预计算常量 k 和 s,因此返回的函数每次调用时需要做更少的工作(如果您正在集成它,则可能非常重要,因为它将被调用多次)。此外,我避免使用指数运算符 **,因为它比编写平方慢,并且将除法提升到内部循环之外并替换为乘法。我没有完全查看他们在Python中的实现,但从我上一次使用原始x87汇编调整内部循环以获取纯速度的经验来看,似乎加法,减法或乘法每个需要大约4个CPU周期,除法大约需要36个周期,指数运算大约需要200个周期。那是几年前的事了,所以这些数字应该谨慎对待;不过,它还是说明了它们的相对复杂性。另外,通过暴力方式计算 exp(x) 是一个非常糟糕的想法;当编写好的 exp(x) 实现时,有一些技巧可以使其比通用的 a**b 风格的指数运算更快、更准确。
我从未使用过 numpy 版本的常量 pi 和 e;我总是坚持使用普通的 math 模块版本。我不知道您可能更喜欢哪一个。
我不确定你使用quad()
调用的目的是什么。quad(gen_gauss, -inf, inf, (10,2,0))
应该从负无穷到正无穷积分一个经过重整化的高斯函数,应该总是输出10(您的归一化因子),因为高斯函数在实数线上积分为1。任何远离10的答案(毕竟quad()
只是一个近似值)都意味着某些地方出了问题......很难说不知道实际返回值和可能的quad()
内部工作情况。
希望这解开了一些困惑,并解释了为什么误差函数是您问题的正确答案,以及如果您好奇如何自行完成所有操作。如果我的解释有任何不清楚之处,建议先快速查看维基百科;如果您仍有疑问,请不要犹豫,随时提问。