boost的erf函数背后的算法是否有任何详细信息?模块的文档不是很精确。我发现的只是几种方法混合在一起。对我来说,它看起来像是 Abramowitz 和 Stegun 的变体。
- 哪些方法是混合的?
- 方法是如何混合的?
- erf 函数(恒定时间)的复杂度是多少?
塞巴斯蒂安
boost的erf函数背后的算法是否有任何详细信息?模块的文档不是很精确。我发现的只是几种方法混合在一起。对我来说,它看起来像是 Abramowitz 和 Stegun 的变体。
塞巴斯蒂安
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;