【问题标题】:Algorithm to find nth root of a number找到数字的第n个根的算法
【发布时间】:2013-12-22 13:40:16
【问题描述】:

我正在寻找一种有效的算法来找到数字的第 n 个根。答案必须是整数。我发现牛顿法和二分法是流行的方法。有没有高效简单的整数输出方法?

【问题讨论】:

  • 带整数表示没有小数位?
  • @Ishmeet 是的答案应该是整数
  • 那么,您是否希望对结果进行四舍五入,如果是,那么如何?或者你想只有当它是整数时才得到答案,否则是错误(或其他)?
  • @hyde 结果必须四舍五入
  • 为什么要关闭这个问题!

标签: c algorithm math


【解决方案1】:
#include <math.h>
inline int root(int input, int n)
{
  return round(pow(input, 1./n));
}

这几乎适用于整个整数范围(因为 IEEE754 8 字节 doubles 可以准确地表示整个 32 位 int 范围,这是几乎每个系统上使用的表示和大小) .而且我怀疑任何基于整数的算法在非古代硬件上都更快。包括ARM。嵌入式控制器(微波炉类型)可能没有浮点硬件。但是问题的那一部分没有详细说明。

【讨论】:

  • @Madura 我敢肯定,因为我不使用上个千年的编译器。
  • @user567879 数学运算:提高到 1/n 次方与取 n 次方相同。这是根据定义。在 C(++) 中,1/n 是整数除法,而在 1./n1.double 文字,因此 n 被转换为双精度,然后除以 1。
  • 我更喜欢写1.0 / n。当他们认为这意味着什么时,它会节省很多阅读代码的人几个“大脑周期”......
  • @MaduraAnushanga:为什么要编译没有-O2-O3?很明显,如果您在未启用优化的情况下进行编译,则不会执行优化。
  • 伟大的基准测试就在那里。正如 Scooter 曾经说过的:“从统计上讲,这些是 Pandora 上最安全的汽车。让我给你一些数字……五!二十三!八百零六!”
【解决方案2】:

我知道这个帖子可能已经死了,但我没有看到任何我喜欢的答案,这让我很烦恼......

int root(int a, int n) {
    int v = 1, bit, tp, t;
    if (n == 0) return 0; //error: zeroth root is indeterminate!
    if (n == 1) return a;
    tp = iPow(v,n);
    while (tp < a) {    // first power of two such that v**n >= a
        v <<= 1;
        tp = iPow(v,n);
    }
    if (tp == a) return v;  // answer is a power of two
    v >>= 1;
    bit = v >> 1;
    tp = iPow(v, n);    // v is highest power of two such that v**n < a
    while (a > tp) {
        v += bit;       // add bit to value
        t = iPow(v, n);
        if (t > a) v -= bit;    // did we add too much?
        else tp = t;
        if ( (bit >>= 1) == 0) break;
    }
    return v;   // closest integer such that v**n <= a
}
// used by root function...
int iPow(int a, int e) {
    int r = 1;
    if (e == 0) return r;
    while (e != 0) {
        if ((e & 1) == 1) r *= a;
        e >>= 1;
        a *= a;
    }
    return r;
}

这种方法也适用于任意精度的定点数学,以防您想要计算诸如 sqrt(2) 到小数点后 100 位...

【讨论】:

  • 这是移位第n个根算法吗?
  • 大整数失败 - 我尝试获取 80 位整数(“1234567890”的 8 个连续副本的字符串)的第 111 个根,即 5,但从该例程中得到 1。 (我目前的例程需要 12 分钟才能完成,所以我正在寻找一个更快的 :)
【解决方案3】:

我质疑你在谈到C programs 时使用“algorithm”。程序和算法不一样(算法是数学的;C 程序应该实现某种算法)。

但在当前的处理器上(例如最近的 x86-64 笔记本电脑或台式机),FPU 的表现相当不错。我猜想(但没有进行基准测试)计算第 n 个根的快速方法可能是,

 inline unsigned root(unsigned x, unsigned n) {
   switch (n) {
     case 0: return 1;
     case 1: return x;
     case 2: return (unsigned)sqrt((double)x);
     case 3: return (unsigned)cbrt((double)x);
     default: return (unsigned) pow (x, 1.0/n);
   }
 }

(我做了一个转换,因为许多处理器都有硬件来计算sqrt,而有些处理器有硬件来计算cbrt......,所以你应该在相关时更喜欢这些......)。

我不确定负数的 n 次根一般是否有意义。所以我的root 函数需要一些unsigned x 并返回一些unsigned 数字。

【讨论】:

  • question你的questioning这个术语算法当谈到任何程序...?
  • 即使有符号值没有意义,如果你想编写没有“风险”有符号-无符号转换的干净代码,它们也会很麻烦,除非 所有 值确实是无符号的,他们很少。在这种情况下,例如考虑cbrt(-8)...
  • 这里使用双精度浮点函数pow 是似是而非的,并且由于浮点舍入错误,有时保证会给你错误的结果 .例如,(unsigned)pow(4096, 1.0/6)3,但 4096 的第 6 个根实际上是 4。这是因为 pow 返回 3.9999999999999996 而不是 4.0,这是因为 1.0/60.1666666666666667 而不是完全1/6。
【解决方案4】:

这是一个高效的 C 通用实现,使用“移位第 n 根算法”的简化版本来计算 x 的第 n 个根的底:

uint64_t iroot(const uint64_t x, const unsigned n)
{
  if ((x == 0) || (n == 0)) return 0;
  if (n == 1) return x;

  uint64_t r = 1;
  for (int s = ((ilog2(x) / n) * n) - n; s >= 0; s -= n)
  {
    r <<= 1;
    r |= (ipow(r|1, n) <= (x >> s));
  }

  return r;
}

需要这个函数来计算xn次幂(使用平方取幂的方法):

uint64_t ipow(uint64_t x, unsigned n)
{
  if (x <= 1) return x;

  uint64_t y = 1;
  for (; n != 0; n >>= 1, x *= x)
    if (n & 1)
      y *= x;
  return y;
}

这个函数计算 x 的以 2 为底的对数的底数:

int ilog2(uint64_t x)
{
  #if __has_builtin(__builtin_clzll)
    return 63 - ((x != 0) * (int)__builtin_clzll(x)) - ((x == 0) * 64);
  #else
    int y = -(x == 0);
    for (unsigned k = 64 / 2; k != 0; k /= 2)
      if ((x >> k) != 0)
        { x >>= k; y += k; }
    return y;
  #endif
}

注意:这假设您的编译器理解 GCC 的 __has_builtin 测试并且您的编译器的 uint64_t 类型与 unsigned long long 的大小相同。

【讨论】:

    【解决方案5】:

    使用吠陀数学获得最快的第 n 根算法 参考http://www.slideshare.net/jadhavvitthal1989/vjs-root-algorithm-final

    同样的算法可以扩展到计算代数方程的根。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-11-27
      • 2012-07-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-02-04
      • 2012-02-08
      • 2011-05-11
      相关资源
      最近更新 更多