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

Correct way to find nth root using pow() in c++

本文关键字:方法 c++ pow      更新时间:2023-10-16

我必须求出最大等于10^18的数的n次方根,其中n等于10^4
我知道使用pow()我们可以用

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

但这是给错误的答案在线编程法官,但在所有的情况下,我已经采取了,它是给出正确的结果。对于给定的约束,这种方法是否有问题
注:这里的n次方根是指其n次幂小于或等于给定数的最大整数,即x^n <= number的最大'x'。
根据答案,我知道这种方法是错误的,那么我应该怎么做呢?

你可以直接使用

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

考虑到n的高值,大多数答案将是1。

更新:

在OP注释之后,这种方法确实是有缺陷的,因为在大多数情况下,1/n没有精确的浮点表示,并且1/n次幂的底数可以减去1。

而四舍五入不是较好的解决方法,它会使根多掉1。

另一个问题是10^18以内的值不能用双精度精确表示,而64位整型可以。

我的建议:

1)在(隐式)强制转换为double之前截断number的11位低位,以避免被FP单位舍入(不确定这是否有用)。

2)利用pow函数得到n次根的劣估计,设r

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

4)在n次幂合适的情况下,解是r+1而不是r

在计算1/n时,FP单元仍有可能四舍五入,导致结果略大。我怀疑这个"太大"在最终结果中是否会像一个单位那么大,但这应该检查一下。

我想我终于明白你的问题了。你所要做的就是将一个值(比如X)提高到一个数字的倒数,比如n(也就是求出n/n),然后四舍五入。如果你把这个答案提高到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≤yn 的最大整数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;
}

我使用我编写的这个例程。这是我见过的最快的。它还可以处理最多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));
}

虽然不总是完美的,但它确实是最接近的。

您可以尝试在C中使用unsigned获取nth_root:

// return a number that, when multiplied by itself nth times, makes N.
unsigned nth_root(const unsigned n, const unsigned nth) {
    unsigned a = n, c, d, r = nth ? n + (n > 1) : n == 1 ;
    for (; a < r; c = a + (nth - 1) * r, a = c / nth)
        for (r = a, a = n, d = nth - 1; d && (a /= r); --d);
    return r;
}

它不包含<math.h>,输出示例:

  24 == (int) pow(15625, 1.0/3)
  25 == nth_root(15625, 3)
   0 == nth_root(0, 0)
   1 == nth_root(1, 0)
   4 == nth_root(4096, 6)
  13 == nth_root(18446744073709551614, 17) // 64-bit 20 digits
  11 == nth_root(340282366920938463463374607431768211454, 37) // 128-bit 39 digits

默认的猜测是变量a,设置为n