6

boost的erf函数背后的算法是否有任何详细信息?模块的文档不是很精确。我发现的只是几种方法混合在一起。对我来说,它看起来像是 Abramowitz 和 Stegun 的变体。

  • 哪些方法是混合的?
  • 方法是如何混合的?
  • erf 函数(恒定时间)的复杂度是多少?

塞巴斯蒂安

4

1 回答 1

6

Boost Math Toolkit的文档有一长串参考文献,其中包括 Abramowitz 和 Stegun。erf-function接口包含一个策略模板参数,可用于控制数值精度(以及因此它的运行时复杂度)。

#include <boost/math/special_functions/erf.hpp>
namespace boost{ namespace math{

template <class T>
calculated-result-type erf(T z);

template <class T, class Policy>
calculated-result-type erf(T z, const Policy&);

template <class T>
calculated-result-type erfc(T z);

template <class T, class Policy>
calculated-result-type erfc(T z, const Policy&);

}} // namespaces

更新

下面是前面提供的对 erf 函数的引用的“实施”部分的逐字副本:

执行

这些函数的所有版本首先使用通常的反射公式来使它们的参数为正:

erf(-z) = 1 - erf(z);

erfc(-z) = 2 - erfc(z);  // preferred when -z < -0.5

erfc(-z) = 1 + erf(z);   // preferred when -0.5 <= -z < 0

这些函数的通用版本是根据不完整的 gamma 函数实现的。

当有效数字(尾数)大小被识别时(目前用于 53、64 和 113 位实数,加上通过提升为双精度处理的单精度 24 位),然后使用 JM 设计的一系列有理近似值。

对于 z <= 0.5,则使用 erf 的有理近似值,基于 erf 是奇函数的观察结果,因此 erf 使用以下公式计算:

erf(z) = z * (C + R(z*z));

其中有理近似 R(z*z) 针对绝对误差进行了优化:只要它的绝对误差与常数 C 相比足够小,那么在计算 R(z*z) 期间产生的任何舍入误差都会有效从结果中消失。因此,该区域中 erf 和 erfc 的误差非常低:仅在极少数情况下最后一位不正确。

对于 z > 0.5,我们观察到在一个小区间 [a, b) 内:

erfc(z) * exp(z*z) * z ~ c

对于一些常数 c。

因此对于 z > 0.5,我们使用以下方法计算 erfc:

erfc(z) = exp(-z*z) * (C + R(z - B)) / z;

R(z - B) 再次针对绝对误差进行了优化,常数 C 是 erfc(z) * exp(z*z) * z 在范围端点处的平均值。再一次,只要 R(z - B) 中的绝对误差与 c 相比较小,那么 c + R(z - B) 将被正确舍入,结果中的误差将仅取决于 exp 的准确性功能。在实践中,除极少数情况外,错误仅限于结果的最后一位。选择常数 B 使得有理逼近范围的左端为 0。

对于 [a, +∞] 范围内的较大 z,上述近似值修改为:

erfc(z) = exp(-z*z) * (C + R(1 / z)) / z;

有理近似值解释得非常详细。如果您需要更多详细信息,您可以随时查看源代码

于 2012-05-17T11:01:57.337 回答