我正在尝试编写一个简短的C ++例程,以针对给定的整数j> i(通常位于0到100之间)和复数z(以| z |为界)计算以下函数F(i,j,z)。 <100),其中L是associated Laguerre Polynomials:
问题是我希望可以在CUDA内核中调用此函数(即,具有__device__
属性)。因此,标准库/ Boost / etc函数是不可能的,除非它们足够简单以至于我自己可以重新实现-这尤其与Boost和C ++ 17中存在的Laguerre多项式有关。无论我是否设法为Laguerre多项式包装任何标准函数,我都仍然具有类似的前置因子来计算形式(z ^ j / j!)。
问题:如何在不引入明显的数值不稳定性的情况下实现这种功能的相对简单的实现?
到目前为止,我的想法是独立计算L及其前置因子。我将通过从0到j-i的第一个循环进行计算并计算(z ^ 1 * z ^ 2/2 * ... * z ^(j-1)/(j-i!)!)。然后,我将计算剩余因子exp(-| z | ^ 2/2)*(j-i)! * sqrt(i!/ j!)(以类似方式或通过CUDA数学中实现的Gamma函数)。然后的想法是找到一种最小的算法来计算相关的Laguerre多项式,除非我设法从一个例子中包装一个实现。 Boost或GNU C ++。
编辑/旁注:对于某些i / j值,F的表达式实际上在数字上会爆炸。在我得到它的源中,它是错误推导的,并且关联的Laguerre多项式的索引应该改为L_i ^(j-i)。但这不会使答案/评论中建议的方法无效。
答案 0 :(得分:2)
我建议为Laguerre多项式的系数找到递归关系:
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)
对于log(sqrt(i!/j!))
部分,我会做(假设j> = i):
log(sqrt(i!/j!))
= 0.5 * (log(i!) - log(j!))
= -0.5 * sum(k==i+1..j, log(k))
我还没有尝试过,所以在这里和那里肯定会有一些小错误。这个答案更多是关于技术,而不是复制粘贴准备好答案
答案 1 :(得分:1)
好吧,你应该做的就是对数
假设自然对数,
q = log(z ^ j / j!)= log(z ^ j)-log(j!)= j * log(z)-log(Gamma(j + 1))
第一项很简单,第二项是标准的C ++函数lgamma(x)(或者您可以使用GSL)。
计算 $('#submit').on('click', function(e) {
e.preventDefault();
if (!$('.name_input').val() || !$('.title_input').val()) {
alert('Please fill in all the required fields');
$('#result').hide();
return false;
} else {
$('#result').show();
$('#form').hide();
//Append desktop email html
$('#DesktopEmailHtml').append(desktopMarkup);
}
});
的值并返回q
您也可以使用此方法折叠指数