为什么 R 的 integrate() 在有限上限很大时返回 0,而使用 Inf 正常?
结论
主要原因是自适应积分的采样点漏掉了函数的主要贡献区域,使积分值和误差估计都失真。浮点下溢会让部分采样值变成 0;即使没有下溢,采样值过小也可能让算法提前停止。
如果要计算从 0 到正无穷的积分,应直接使用 Inf。如果上限确实有限,可以根据函数的有效范围拆分区间,或用尾积分控制截断误差。
为什么有限上限越大,结果反而越差?
对于
[ f(x)=x^2e^{-x},\qquad x\geq 0, ]
有
[ f’(x)=x(2-x)e^{-x}. ]
函数在 (x=2) 处达到峰值,之后快速衰减。积分的主要贡献集中在靠近原点的一小段区域。
R 的 integrate() 在有限区间上使用基于 Gauss-Kronrod 求积的自适应细分方法。算法先在若干节点计算函数值,再根据误差估计决定是否继续细分,不会遍历整个区间。R 官方文档
区间变大时,初始采样点也会移向远处。以底层 21 点求积规则为例,首次对整个 [0, B] 求积时,离左端点最近的节点约为:
[ x_{\min}=\frac{B}{2}(1-0.9956571630)\approx 0.0021714B. ]
当 B = 1000000 时,这个节点位于 x ≈ 2171,距离峰值 x = 2 很远。上述节点位置由 QUADPACK 的 dqk21 源码推得。
在这些位置,exp(-x) 在通常的双精度运算中已经下溢为 0。算法采样得到的值可能全部为 0,计算出的积分值和误差估计也随之变成 0。上限稍小时,采样值可能仍非零,但小到让算法误以为已达到精度要求。
输出中的 absolute error < 0 是根据采样结果得到的误差估计,不能理解为真实误差等于零。采样漏掉峰值时,这个估计本身也不可靠。
为什么使用 Inf 能正确计算?
Inf 会触发无限区间的处理方式。R 会先将无限区间映射到有限区间,再进行数值积分。R 官方文档
对于本例的 [0, Inf),底层变换可写为:
[ x=\frac{1-t}{t},\qquad 0<t<1, ]
对应的积分为:
[ \int_0^\infty f(x),dx =\int_0^1 f!\left(\frac{1-t}{t}\right)\frac{1}{t^2},dt. ]
这一变换及其雅可比因子可见 QUADPACK 的 dqk15i 源码。本例中,峰值位置 x = 2 对应 t = 1/3,不会因为人为选择巨大的上限而被挤到区间端点附近,采样时更容易捕捉到峰值。
无限区间算法也有适用限制;对于本例这个快速衰减的函数,上述变换更合适。
如何可靠地计算?
目标是无限区间积分时,直接使用 Inf
f <- function(x) x^2 * exp(-x)
res <- integrate(
f,
lower = 0,
upper = Inf,
rel.tol = 1e-10,
abs.tol = 1e-10
)
res$value
res$abs.error
res$message
本例的解析值为 2,可以用来独立检查数值结果。这里给出的容差是精度要求,具体误差估计以实际运行结果为准。
目标是有限区间积分时,控制尾部误差
本例可以直接推导出:
[ \int_B^\infty x^2e^{-x},dx=e^{-B}(B^2+2B+2), ]
因此:
[ \int_0^B x^2e^{-x},dx=2-e^{-B}(B^2+2B+2). ]
原问题中的几个大有限上限对应的积分真值都非常接近 2。
如果仍希望通过数值积分计算,可以先积分到覆盖主要贡献区域的位置,再计算剩余尾部。例如,取 cut = 50 时:
[ \int_{50}^{\infty}x^2e^{-x},dx =2602e^{-50}\approx 5.02\times10^{-19}. ]
对于 B >= 50,从 50 到 B 的贡献不会超过这个值。以下代码利用解析尾积分补上了这段贡献:
f <- function(x) x^2 * exp(-x)
B <- 1000000
cut <- 50
core <- integrate(
f, 0, cut,
rel.tol = 1e-10,
abs.tol = 1e-10
)
tail_from <- function(x) {
exp(-x) * (x^2 + 2 * x + 2)
}
value <- core$value + tail_from(cut) - tail_from(B)
value
这里的尾积分公式专用于当前函数。换成其他函数时,应重新分析尾部衰减,不能直接沿用 cut = 50。总误差还包括主体区间的求积误差和浮点舍入误差。
调整 subdivisions 或容差有用吗?
subdivisions 是允许产生的子区间数量上限,不要求算法一定细分到这个数量。如果初始采样已经让算法判断误差足够小,单纯增大它通常无法解决遗漏峰值的问题。R 官方参数说明
减小容差有时能促使算法继续细分,但如果采样值和估计误差都已经是 0,收紧容差也无法恢复丢失的信息。更可靠的做法是先确定函数的主要贡献区域,再选择无限区间算法、合理分段或有误差依据的截断位置。
备注:内容仅供参考。
