快速准确地实现不完整伽玛函数的双精度


10

最新的实现双精度特殊功能的方式是什么?我需要以下积分: 对于和,可以用较低的不完全伽马函数来表示。这是我的Fortran和C实现:=012

Fm(t)=01u2metu2du=γ(m+12,t)2tm+12
m=0,1,2,...t>0

https://gist.github.com/3764427

它使用级数展开,将各项相加直到达到给定的精度,然后使用递归关系有效地获得较低值。我对其进行了很好的测试,获得了我需要的所有参数值的1e-15精度,有关详细信息,请参见Fortran版本的注释。m

有没有更好的方法来实现它?这是gfortran中的gamma函数实现:

https://github.com/mirrors/gcc/blob/master/libgfortran/intrinsics/c99_functions.c#L1781

它使用有理函数逼近而不是总结一些我正在做的无穷级数。我认为这是一种更好的方法,因为应该获得统一的精度。是否有某种规范的方法来处理这些问题,还是必须为每种特殊功能找出一种特殊的算法?

更新1

根据评论,这是使用SLATEC的实现:

https://gist.github.com/3767621

它从我自己的函数中复制值,大致在1e-15精度级别上。但是,我注意到一个问题,对于t = 1e-6和m = 50,项等于1e-303,对于更高的“ m”,它只是开始给出错误的答案。我的函数没有这个问题,因为我直接对使用了一系列扩展/递归关系。这是一个正确值的示例: ˚Ftm+12Fm

F100(1e-6)=4.97511945200351715E-003

但是我无法使用SLATEC来实现这一点,因为分母会爆炸。如您所见,的实际值很小。Fm

更新2

为避免上述问题,可以使用该函数dgamit(Tricomi的不完全Gamma函数),然后F(m, t) = dgamit(m+0.5_dp, t) * gamma(m+0.5_dp) / 2,因此不再存在问题,但是不幸的是,。然而,这可能是足够高的,我的目的。172 tgamma(m+0.5_dp)m172m


2
为什么要编写自己的函数?GSL,cephes和SLATEC都实现了它。
Geoff Oxberry 2012年

我已经更新了为什么不使用SLATEC的问题。
昂德里杰·塞蒂克

@OndřejČertík您已经发现了一个错误!提出了您的问题!
阿里

Ali ---这不是SLATEC中的错误,但实际上,我实际上需要将除以以获取。因此,适用于的数值方法可能不适用于。t m + 1γ(z,x) FmtγzxFmttm+12Fm(t)γ(z,x)Fm(t)
昂德里杰·塞蒂克

@OndřejČertík好,抱歉,我的错误,在发表评论之前我没有检查您的代码。
阿里

Answers:


9

在英国化学家塞缪尔·弗朗西斯·博伊斯(Samuel Francis Boys)于1950年代初提出使用该积分之后,该积分也被称为“男孩”函数。几年前,我需要以尽可能快但又准确的双精度来计算此函数。我设法在整个输入域中实现了的相对误差。1015

通常,对于小参数和大参数使用不同的近似值是有利的,其中“大”和“小”之间的最佳切换最好通过实验确定,并且通常是的函数。对于我的代码,我将“小”自变量定义为满足。一个+ 1 1mam+112

对于大参数,我计算

Fm(a)=12γ(m+12,a)×p×p,  p=a12(m+12)

此操作顺序避免了过早的下溢。由于这里我们只需要半整数阶的较低不完全伽马函数,而不是完全通用的较低不完全伽马函数,因此从性能角度来看,这是有利的

γ(m+12,a)=Γ(m+12)Γ(m+12,a)

根据此答案使用列表值,并根据 此答案计算,同时谨慎地避免出现此问题通过使用融合的乘法加法运算进行减法消除的方法。一个潜在的进一步优化是观察,对于足够大的,至内给定的浮点精度。Γ+1Γ(m+12)一个γ+1Γ(m+12,a)aγ(m+12,a)=Γ(m+12)

对于小参数,我从下面的较低不完整伽玛函数开始进行级数展开

A. Erdelyi,W。Magnus,F。Oberhettinger和FG Tricomi,“更高的超越功能,第2卷”。纽约,纽约:麦格劳·希尔1953

并对其进行了修改,以计算出Boys函数如下(当项对于给定的精度足够小时,将其截断):Fm(a)

Fm(a)=121m+12exp(a)(1+n=1an(1+m+12)× ... ×(n+m+12))

对于Boys函数的低阶,还有一些有趣且潜在重要的特殊情况,特别是。首先,我们有,其中是在2008年的Fortran作为元素函数提供的误差函数和在C / C ++作为标准库函数和。m=0,1,2,3F0(a)=π4aerf(a)erfERFerferff

为了在时进行快速计算,我对小参数使用了自定义的minimax多项式逼近,例如和前向递归,对于大的,后者中减法消除的问题通过使用融合的乘加运算得以缓解。< 2 1m=1,2,3 Fma=1a<212Fm(a)=12a((2m1)Fm1(a)exp(a))

在必须为给定跨多个阶数计算函数值的情况下,人们可能想直接为最大值计算函数值,即如上所述,然后使用数值稳定的向后递归进行计算所有其他函数值。m m F m 1 = 1ammFm1=12m1(2a Fm(a)+exp(a))


感谢@njuffa的出色回答。如果您为该开源代码编写代码,我认为这对很多人来说将非常有用。
昂德里杰·塞蒂克

1
当前,可从NVIDIA开发人员网站免费下载所描述算法的CUDA实现(要求免费注册为CUDA开发人员,通常需要在一个工作日内批准)。该代码受BSD许可,该许可应与几乎任何类型的项目兼容。
njuffa


4

我会看一看Abramowicz&Stegun的书,或者NIST几年前发布的较新版本,我相信它可以在线获得。他们还讨论了以稳定方式实现事物的方法。


我在使用它时:dlmf.nist.gov/8,但这可能是另一种资源。《数字食谱》的第5章也有一些有趣的信息,但仅适用于一个变量的功能。
昂德里杰·塞蒂克

我认为您不会发现比他们2001年的参考文献更新的东西。SLATEC将比那更早。
Geoff Oxberry 2012年

1

它似乎不是最新技术,但Netlib的SLATEC提供了“ 1400个通用数学和统计例程”。不完整的Gamma可在此处特殊功能下使用。

实现这样的功能既费时又容易出错,因此除非绝对必要,否则我不会自己做。SLATEC已经存在了很长一段时间,并且至少基于下载次数,它已经被广泛使用,因此我希望实现能够成熟。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.