PAYATHON 2026

为什么 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,收紧容差也无法恢复丢失的信息。更可靠的做法是先确定函数的主要贡献区域,再选择无限区间算法、合理分段或有误差依据的截断位置。

备注:内容仅供参考。