有效地为立方根提供牛顿迭代

时间:2011-09-18 18:30:31

标签: algorithm math

如何以有效的方式找到数字的立方根? 我认为可以使用Newton-Raphson方法,但我不知道如何以编程方式猜测初始解决方案以最小化迭代次数。

4 个答案:

答案 0 :(得分:11)

这是一个看似复杂的问题。 Here对一些可能的方法进行了很好的调查。

答案 1 :(得分:7)

鉴于"链接腐烂"超过接受的答案,我将提供一个更加独立的答案,重点是快速获得适合超线性迭代的初始猜测。

"调查"由metamerist(Wayback link)为各种起始值/迭代组合提供了一些时序比较(包括Newton和Halley方法)。它的引用是W. Kahan,"计算真实立方根和#34;以及K. Turkowski,"计算多维数据集根"的作品。

metamarist用这个片段更新了W. Kahan的DEC-VAX时代比特摆弄技术,该片段采用32位整数"并依赖IEEE 754格式进行双精度生成,以5位精度生成初始估计值":

inline double cbrt_5d(double d) 
{ 
   const unsigned int B1 = 715094163; 
   double t = 0.0; 
   unsigned int* pt = (unsigned int*) &t; 
   unsigned int* px = (unsigned int*) &d; 
   pt[1]=px[1]/3+B1; 
   return t; 
} 

K. Turkowski的代码通过float fr上的常规二次幂缩放提供稍微更精确("大约6位"),然后对其立方根进行二次近似区间[0.125,1.0]:

/* Compute seed with a quadratic qpproximation */
fr = (-0.46946116F * fr + 1.072302F) * fr + 0.3812513F;/* 0.5<=fr<1 */

随后恢复两个指数(调整为三分之一)。指数/尾数提取和恢复使用math library callsfrexpldexp

与其他立方根&#34;种子&#34;进行比较近似值

要了解这些立方根近似值,我们需要将它们与其他可能的形式进行比较。首先是判断的标准:我们考虑区间[1 / 8,1]的近似值,并且我们使用最佳(最小化最大值)相对误差。

也就是说,如果f(x)x^{1/3}的拟议近似值,我们会发现它的相对误差:

        error_rel = max | f(x)/x^(1/3) - 1 | on [1/8,1]

最简单的近似当然是在区间上使用单个常数,并且在这种情况下通过选择端点上的值的几何平均值f_0(x) = sqrt(2)/2来实现最佳相对误差。这给出了1.27位的相对精度,这是牛顿迭代的一个快速但又脏的起点。

更好的近似将是最好的一次多项式:

 f_1(x) = 0.6042181313*x + 0.4531635984

这给出了4.12位的相对精度,这是一个很大的改进,但是缺少了Kahan和Turkowski各自方法所承诺的5-6位相对精度。但它只是在球场,并且只使用一次乘法(和一次加法)。

最后,如果我们允许自己进行除法而不是乘法怎么办?事实证明,有一个师和两个&#34;添加&#34;我们可以得到最好的线性分数函数:

 f_M(x) = 1.4774329094 - 0.8414323527/(x+0.7387320679) 

给出了7.265位的相对精度。

乍一看,这似乎是一种有吸引力的方法,但旧的经验法则是将FP分割的成本视为三次FP乘法(并且主要忽略加法和减法)。然而,对于当前的FPU设计,这是不现实的。虽然增加/减少乘法的相对成本已降低,但在大多数情况下降低到两倍甚至相等,但除法成本并没有下降,但往往高达乘法成本的7-10倍。因此,我们必须吝啬我们的分工。

答案 2 :(得分:0)

static double cubeRoot(double num) {
    double x = num;

    if(num >= 0) {
        for(int i = 0; i < 10 ; i++) {
            x = ((2 * x * x * x) + num ) / (3 * x * x); 
        }
    } 
    return x;
}

答案 3 :(得分:0)

似乎已经解决了优化问题,但我想在此处发布的cubeRoot()函数中添加一项改进,以便其他人在此页面上找到快速的多维数据集根算法。 / p>

现有算法运行良好,但在0-100范围之外,它会产生不正确的结果。

这是一个修订版,适用于 - / + 1千万亿(1E15)之间的数字。如果您需要使用更大的数字,只需使用更多迭代。

static double cubeRoot( double num ){
    boolean neg = ( num < 0 );
    double x = Math.abs( num );
    for( int i = 0, iterations = 60; i < iterations; i++ ){
        x = ( ( 2 * x * x * x ) + num ) / ( 3 * x * x ); 
    }
    if( neg ){ return 0 - x; }
    return x;
}

关于优化,我猜测原始海报是在询问如何根据任意输入大小预测准确结果的最小迭代次数。但似乎对于大多数一般情况来说,优化的收益并不值得增加复杂性。即使使用上述功能,100次迭代在平均消费者硬件上也只需不到0.2 ms。如果速度至关重要,我会考虑使用预先计算的查找表。但这来自桌面开发人员,而不是嵌入式系统工程师。