数值计算阶乘和多项式的组合 我尝试编写一个短的C++例程来计算给定整数J>i(通常它们位于0和100)和复数Z(由z<100)所限定的下列函数f(i,j,z),其中L是:

数值计算阶乘和多项式的组合 我尝试编写一个短的C++例程来计算给定整数J>i(通常它们位于0和100)和复数Z(由z<100)所限定的下列函数f(i,j,z),其中L是:,c++,algorithm,numerical-methods,factorial,polynomials,C++,Algorithm,Numerical Methods,Factorial,Polynomials,问题是我希望这个函数可以在CUDA内核中调用(即使用\uuuu device\uuu属性)。因此,标准库/Boost/etc函数是不可能的,除非它们足够简单,可以自己重新实现——这尤其与Boost和C++17中存在的拉盖尔多项式有关。不管我是否为拉盖尔多项式包装了任何标准函数,我仍然有一个类似的预因子来计算(z^j/j!) 问题:如何在不引入显著数值不稳定性的情况下实现这样一个函数的相对简单的实现 到目前为止,我的想法是独立计算L及其前置因子。预因子I将首先从0循环到j-I并计算(z^1*z^2

问题是我希望这个函数可以在CUDA内核中调用(即使用
\uuuu device\uuu
属性)。因此,标准库/Boost/etc函数是不可能的,除非它们足够简单,可以自己重新实现——这尤其与Boost和C++17中存在的拉盖尔多项式有关。不管我是否为拉盖尔多项式包装了任何标准函数,我仍然有一个类似的预因子来计算(z^j/j!)

问题:如何在不引入显著数值不稳定性的情况下实现这样一个函数的相对简单的实现

到目前为止,我的想法是独立计算L及其前置因子。预因子I将首先从0循环到j-I并计算(z^1*z^2/2*…*z^(j-1)/(j-I)!)。然后,我将计算剩余因子exp(-z^2/2)*(j-I)!*sqrt(i!/j!)(以类似的方式,或者通过Gamma函数,该函数在CUDA math中实现)。然后,这个想法是找到一个最小的算法来计算相关的Laguerre多项式,除非我设法包装一个实现从例如Boost或GNU C++。
编辑/旁注:对于i/j的某些值,F的表达式实际上以数字形式展开。它在我得到它的源代码中推导错误,相关拉盖尔多项式的指数应该是L_I^(j-I)。这并不会使答案/评论中建议的方法无效。

好吧,你应该做的是将其对数化

假设自然对数

q=log(z^j/j!)=log(z^j)-log(j!)=j*log(z)-log(γ(j+1))

第一项是简单的,第二项是标准C++函数LGAMM(X)(或者可以使用GSL)。 计算

q的值
并返回
cexp(q)


您也可以使用此方法折叠指数

我建议为拉盖尔多项式的系数找到一个递推关系:

C(k+1) = g(k)C(k)
g(k) = C(k+1) / C(k)
g(k) = -z * (j - k) / ((j - i + k + 1) * (k + 1)) //Verify this yourself :)
这允许您在计算多项式时避免大多数阶乘

在那之后,我将遵循Severin的想法,用对数进行计算 为了不使双浮点范围过载:

log(F) = log(sqrt(i!/j!)) - |z|^2 + (j-i) * log(-z) + log(L(|z|^2))
log(L) = log((2*j - i)!) + log(sum) // where the summation is computed using the recurrence relation above
并利用以下事实:

log(a!) = sum(k=1..a, log(k))
而且:

log(z) = log(|z|) + I * arg(z) for complex z
log(-z) = log(|z|) + I * arg(-z)
log(-z) = log(|z|) - I * arg(z)
对于
日志(sqrt(i!/j!))
第一部分(假设j>=i):


我还没有试过,所以肯定会有一些小错误。这个答案更多的是关于这项技术,而不是一个复制粘贴就绪的答案

看起来你可以从一个大数字库中获益。搜索互联网或[softwarereqs.se]。你的障碍是指数和因子。这些往往会超过C++语言的能力,非常快。你能简化还是简化?这个问题应该在StackOverflow数学社区的数学部分提问。太好了,谢谢!我只需要小心处理log(z),因为它是一个复数。@fromGiants是的,当然。而
lgama()
将近似值放在gputh上应该很容易找到。CUDA标准数学库包括
lgama()
类型
double
float
  log(sqrt(i!/j!))
= 0.5 * (log(i!) - log(j!))
= -0.5 * sum(k==i+1..j, log(k))