在c ++中使用pow()找到第n个根的正确方法

时间:2014-06-10 16:11:33

标签: c++

我必须找到可以大到10 ^ 18的第n个数字根,其中n大到10 ^ 4。
我知道使用pow()我们可以使用

找到第n个根
x = (long int)(1e-7 + pow(number, 1.0 / n))

但这对在线编程评委给出了错误的答案,但在我所采取的所有案例中,都给出了正确的结果。对于给定的约束,这种方法是否有问题 注意:此处的第n个根表示其n次幂小于或等于给定数字的最大整数,即x ^ n <=数字的最大“x”。 在答案之后,我知道这种方法是错误的,那么我应该怎样做呢?

3 个答案:

答案 0 :(得分:3)

你可以使用

x = (long int)pow(number, 1.0 / n)

鉴于n的价值很高,大多数答案都是1。

更新:

在OP评论之后,这种方法确实存在缺陷,因为在大多数情况下,1 / n没有精确的浮点表示,并且1 / n次幂的最低值可能会偏离1。

并且舍入不是更好的解决方案,它可以使一个超出一个根。

另一个问题是,高达10 ^ 18的值无法使用双精度精确表示,而64位整数则无法表示。

我的建议:

1)在(隐式)强制转换之前截断数字的11个低位比特,以避免FP单位向上舍入(不确定这是否有用)。

2)使用pow函数得到第n个根的低估,让r

3)仅使用整数运算(通过重复平方)计算r+1的n次幂。

4)如果第n次幂适合,解是r+1而不是r

当计算1 / n时,FP单元可能会向上舍入,导致结果略微过大。我怀疑这个“太大”在最终结果中可能会达到一个单位,但是应该检查这个。

答案 1 :(得分:0)

我想我终于理解了你的问题。你想要做的就是将一个值(比如 X )提高到数字的倒数,比如说 n (即找到ⁿ√X̅),然后向下舍入。如果您然后提高 n 次幂的答案,它将永远不会大于原来的 X 。问题是计算机有时会遇到舍入错误。

#include <cmath>

long find_nth_root(double X, int n)
{
    long nth_root = std::trunc(std::pow(X, 1.0 / n));
    // because of rounding error, it's possible that nth_root + 1 is what we actually want; let's check
    if (std::pow(nth_root + 1, n) <= X) {
        return nth_root + 1;
    }
    return nth_root;
}

当然,最初的问题是找到满足等式X≤Yⁿ的最大整数 Y 。这很容易写:

long find_nth_root(double x, int d)
{
    long i = 0;
    for (; std::pow(i + 1, d) <= x; ++i) { }
    return i;
}

这可能比你预期的要快。但是你可以用二进制搜索做得更好:

#include <cmath>

long find_nth_root(double x, int d)
{
    long low = 0, high = 1;
    while (std::pow(high, d) <= x) {
        low = high;
        high *= 2;
    }
    while (low != high - 1) {
        long step = (high - low) / 2;
        long candidate = low + step;

        double value = std::pow(candidate, d);
        if (value == x) {
            return candidate;
        }
        if (value < x) {
            low = candidate;
            continue;
        }
        high = candidate;
    }
    return low;
}

答案 2 :(得分:0)

我使用我编写的例程。这是我在这里看到的最快的。它还可以处理多达64位。顺便说一句,n1是输入数字。

for (n3 = 0; ((mnk) < n1) ; n3+=0.015625, nmrk++) {
   mk += 0.0073125;
   dad += 0.00390625;
   mnk = pow(n1, 1.0/(mk+n3+dad));
   mnk = pow(mnk, (mk+n3+dad));
}

尽管并不总是完美的,但它确实是最接近的。