我尝试通过scipy.integrate.quad()
计算这样一个积分(实际上是pdf的指数分布及其pdf):
import numpy as np
from scipy.integrate import quad
def g(x):
return .5 * np.exp(-.5 * x)
print quad(g, a=0., b=np.inf)
print quad(g, a=0., b=10**6)
print quad(g, a=0., b=10**5)
print quad(g, a=0., b=10**4)
结果如下:
(1.0, 3.5807346295637055e-11)
(0.0, 0.0)
(3.881683817604194e-22, 7.717972744764185e-22)
(1.0, 1.6059202674761255e-14)
尽管使用np.inf
解决了问题,但所有尝试使用较大的上限积分限制都会产生错误的答案。
scipy issue #5428 at GitHub中讨论了类似的案例。
如何避免在集成其他密度函数时出现这样的错误?
答案 0 :(得分:4)
我认为问题是np.exp(-x)
随着x
的增加而变得非常小,导致由于数值精度有限而评估为零。例如,即使x
小到x=10**2*
,np.exp(-x)
评估为3.72007597602e-44
,而x
或10**3
以上的值会导致0
。
我不知道quad
的实现细节,但它可能会执行某种类型的函数抽样,以便在给定的积分范围内进行集成。对于较大的上积分限制,np.exp(-x)
的大多数样本评估为零,因此低估了积分值。 (请注意,在这些情况下,quad
提供的绝对误差与积分值的顺序相同,后者是不可靠的指标。)
避免这个问题的一种方法是将积分上限限制在一个值之上,在该值之上,数值函数变得非常小(因此,对积分值略微有贡献)。从您的代码snipet中,10**4
的值似乎是一个不错的选择,但是,10**2
的值也会导致对积分的准确评估。
避免数值精度问题的另一种方法是使用在任意精度算术中执行计算的模块,例如mpmath
。例如,对于x=10**5
,mpmath
按如下方式评估exp(-x)
(使用本机mpmath
指数函数)
import mpmath as mp
print(mp.exp(-10**5))
3.56294956530937e-43430
注意这个值有多小。使用标准硬件数值精度(由numpy
使用),此值变为0
。
mpmath
提供了一个积分函数(mp.quad
),它可以准确估计上限积分的任意值的积分。
import mpmath as mp
print(mp.quad(lambda x : .5 * mp.exp(-.5 * x), [0, mp.inf]))
print(mp.quad(lambda x : .5 * mp.exp(-.5 * x), [0, 10**13]))
print(mp.quad(lambda x : .5 * mp.exp(-.5 * x), [0, 10**8]))
print(mp.quad(lambda x : .5 * mp.exp(-.5 * x), [0, 10**5]))
1.0 0.999999650469474 0.999999999996516 0.999999999999997
我们还可以通过将精度提高到50
小数点(来自15
这是标准精度)来获得更准确的估算值
mp.mp.dps = 50;
print(mp.quad(lambda x : .5 * mp.exp(-.5 * x), [0, mp.inf]))
print(mp.quad(lambda x : .5 * mp.exp(-.5 * x), [0, 10**13]))
print(mp.quad(lambda x : .5 * mp.exp(-.5 * x), [0, 10**8]))
print(mp.quad(lambda x : .5 * mp.exp(-.5 * x), [0, 10**5]))
1.0 0.99999999999999999999999999999999999999999829880262 0.99999999999999999999999999999999999999999999997463 0.99999999999999999999999999999999999999999999999998
通常,获得此准确性的成本是增加的计算时间。
P.S。:不言而喻,如果您能够首先分析评估您的积分(例如,在Sympy
的帮助下),您可以忘记以上所有内容。
答案 1 :(得分:2)
使用points
参数告诉算法大致支持函数的位置:
import numpy as np
from scipy.integrate import quad
def g(x):
return .5 * np.exp(-.5 * x)
print quad(g, a=0., b=10**3, points=[1, 100])
print quad(g, a=0., b=10**6, points=[1, 100])
print quad(g, a=0., b=10**9, points=[1, 100])
print quad(g, a=0., b=10**12, points=[1, 100])