web-dev-qa-db-fra.com

Algorithme pour trouver la nième racine d'un nombre

Je cherche un algorithme efficace pour trouver la nième racine d’un nombre. La réponse doit être un entier. J'ai trouvé que la méthode de newtons et la méthode de bissection sont des méthodes populaires. Existe-t-il des méthodes simples et efficaces pour les sorties entières?

10
user567879
#include <math.h>
inline int root(int input, int n)
{
  return round(pow(input, 1./n));
}

Cela fonctionne pour à peu près toute la plage entière (car IEEE754 8 octets doublename__s peut représenter la totalité de la plage intde 32 bits exactement, qui sont les représentations et les tailles utilisées sur pratiquement tous les systèmes). Et je doute qu'un algorithme basé sur des nombres entiers soit plus rapide sur du matériel non ancien. Y compris ARM. Les contrôleurs intégrés (du type machine à laver à micro-ondes) peuvent ne pas avoir de matériel en virgule flottante. Mais cette partie de la question était sous-spécifiée.

17
rubenvb

Je sais que ce fil est probablement mort, mais je ne vois aucune réponse qui me plaise et qui me dérange ...

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;
}

Cette méthode fonctionnera également avec une mathématique ponctuelle de précision arbitraire si vous souhaitez calculer quelque chose comme sqrt (2) avec 100 décimales ...

11
guest

Je m'interroge sur votre utilisation de " algorithme " lorsque vous parlez de programmes C . Les programmes et les algorithmes ne sont pas identiques (un algorithme est mathématique; un programme C devrait être mettant en œuvre un algorithme).

Mais sur les processeurs actuels (comme dans les ordinateurs portables ou ordinateurs de bureau x86-64 récents), le FPU se porte plutôt bien. Je suppose (mais sans référence) qu’un moyen rapide de calculer la nième racine pourrait être, 

 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);
   }
 }

(J'ai effectué une commutation car de nombreux processeurs ont du matériel pour calculer sqrt et certains ont du matériel pour calculer cbrt ..., vous devriez donc les préférer lorsque cela est pertinent ...).

Je ne suis pas sûr que la énième racine d’un nombre négatif ait un sens en général. Donc, ma fonction root prend un unsigned x et renvoie un nombre unsigned.

5

Voici une implémentation générale efficace en C utilisant une version simplifiée de "l'algorithme de déplacement de la racine" pour calculer le plancher de la racine n th de la racine x :

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;
}

Cette fonction a besoin de cette fonction pour calculer la puissance n ième de x (en utilisant la méthode de l'exponentiation par quadrature):

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;
}

et cette fonction pour calculer le plancher du logarithme en base 2 de x :

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
}

Remarque: Cela suppose que votre compilateur comprend le test __has_builtin de GCC et que le type uint64_t de votre compilateur a la même taille qu'un unsigned long long.

0
Todd Lehman