【发布时间】:2017-05-08 05:26:31
【问题描述】:
我一直在用 C++ 开发一个项目,作为项目的一部分,我需要计算实数值的谐波数。对于超过 40 的值,我有一个非常准确的公式,并且使用 Kahan 和我可以准确地获得整数的谐波数,但我不知道如何准确计算像 HarmonicNumber(1.5) 这样的值。我该怎么做?
注意:如果有人有用于计算 Digamma 函数的快速 C++ 代码,我也可以使用它,因为 Digamma 函数很容易转换为 HarmonicNumber 函数。
编辑:我已经编写了以下代码,尽管我希望有更快、更简单的代码。
编辑 2:对于双精度值,我需要一个小于 10^-15 的相对误差,对于像“long double”这样的 80 位扩展精度值,我需要一个小于 10^-18 的相对误差
long double HarmonicNumber(long double n)
{
//Absolute error is smaller than or equal to 2^-61, or 4.33681e-19, for n<5000
//For n>5000, all but the last two bits are correct.
constexpr long double m1 = 1.0L / 24;
constexpr long double m2 = -7.0L / 960;
constexpr long double m3 = 31.0L / 8064;
constexpr long double m4 = -127.0L / 30720;
constexpr long double m5 = 511.0L / 67584;
constexpr long double m6 = -1414477.0L / 67092480;
constexpr long double m7 = 8191.0L / 98304;
constexpr long double EulerGamma = 0.5772156649015328606065120900824024310421L;
long double v = n + 0.5L;
long double v2 = 1.0L / (v * v);
//Uses asymptotic expansion with progressively more terms.
//Fewer terms are needed for larger inputs.
if(n >= 10000.L) return m1*v2 + log(v) + EulerGamma;
if(n >= 450.00L) return (m2*v2 + m1)*v2 + log(v) + EulerGamma;
if(n >= 110.00L) return ((m3*v2 + m2)*v2 + m1)*v2 + log(v) + EulerGamma;
if(n >= 42.000L) return (((m4*v2 + m3)*v2 + m2)*v2 + m1)*v2 + log(v) + EulerGamma;
if(n >= 24.000L) return ((((m5*v2 + m4)*v2 + m3)*v2 + m2)*v2 + m1)*v2 + log(v) + EulerGamma;
if(n >= 17.000L) return (((((m6*v2 + m5)*v2 + m4)*v2 + m3)*v2 + m2)*v2 + m1)*v2 + log(v) + EulerGamma;
if(n >= 13.000L) return ((((((m7*v2 + m6)*v2 + m5)*v2 + m4)*v2 + m3)*v2 + m2)*v2 + m1)*v2 + log(v) + EulerGamma;
if(n >= 6.0L)
{
//Calculates HarmonicNumber(n+7) and then subtracts fraction to find HarmonicNumber(n)
v = n + 7.5L;
v2 = 1.0L / (v * v);
auto base = ((((((m7*v2 + m6)*v2 + m5)*v2 + m4)*v2 + m3)*v2 + m2)*v2 + m1)*v2 + log(v) + EulerGamma;
long double n2 = n + 4.0L;
auto n2sq = n2*n2;
auto n2sh = n2sq - 7.0L;
auto shft = 1.0L / n2 + (2*n2*n2sh*(3.0L*n2sq-7.0L))/(n2sq*n2sh*n2sh-36.0L);
return base - shft;
}
else
{
//Calculates HarmonicNumber(n+14) and then subtracts fraction to find HarmonicNumber(n)
v = n + 14.5L;
v2 = 1.0L / (v * v);
auto base = ((((((m7*v2 + m6)*v2 + m5)*v2 + m4)*v2 + m3)*v2 + m2)*v2 + m1)*v2 + log(v) + EulerGamma;
long double n2 = n + 4.0L;
auto n2sq = n2*n2;
auto n2sh = n2sq - 7.0L;
auto shft = 1.0L / n2 + (2*n2*n2sh*(3.0L*n2sq-7.0L))/(n2sq*n2sh*n2sh-36.0L);
n2 = n + 11.0L;
n2sq = n2*n2;
n2sh = n2sq - 7.0L;
shft += 1.0L / n2 + (2*n2*n2sh*(3.0L*n2sq-7.0L))/(n2sq*n2sh*n2sh-36.0L);
return base - shft;
}
}`
【问题讨论】:
-
您能准确地说出您的计算需要多精确吗?如果精度允许,您可以通过预先计算查找表并使用它们周围的近似值来强制执行它。
-
我希望它尽可能准确 - 最好至少是双精度。我看着将一堆多项式插值拼接在一起,但这很麻烦
-
您可以在问题中添加精度要求吗?没有明确的要求很难回答
-
我希望 80 位“长双精度”的相对误差小于 10^-18,对于大于 1 的输入的常规双精度小于 10^-15
-
然后将其添加到问题。重要的是,轻描淡写
标签: c++ math optimization numeric