web-dev-qa-db-fra.com

Comment faites-vous * entier * exponentiation en C #?

La fonction Math.Pow() intégrée dans .NET pose une base double sur un exposant double et renvoie un résultat double.

Quelle est la meilleure façon de faire la même chose avec des nombres entiers?

Ajout: Il semble que l'on puisse simplement transtyper Math.Pow() result en (int), mais cela produira-t-il toujours le nombre correct et aucune erreur d'arrondi?

42
Roman Starkov

Un assez rapide pourrait être quelque chose comme ça:

int IntPow(int x, uint pow)
{
    int ret = 1;
    while ( pow != 0 )
    {
        if ( (pow & 1) == 1 )
            ret *= x;
        x *= x;
        pow >>= 1;
    }
    return ret;
}

Notez que cela ne permet pas les pouvoirs négatifs. Je vous laisse ça comme un exercice. :)

Ajoutée: Oh oui, presque oublié - ajoutez également un contrôle de débordement/dépassement de capacité, ou vous risquez d’être surpris par de mauvaises surprises.

39
Vilx-

LINQ quelqu'un?

public static int Pow(this int bas, int exp)
{
    return Enumerable
          .Repeat(bas, exp)
          .Aggregate(1, (a, b) => a * b);
}

utilisation en extension:

var threeToThePowerOfNine = 3.Pow(9);
38
3dGrabber

En utilisant les maths dans le lien de blog de John Cook,

    public static long IntPower(int x, short power)
    {
        if (power == 0) return 1;
        if (power == 1) return x;
        // ----------------------
        int n = 15;
        while ((power <<= 1) >= 0) n--;

        long tmp = x;
        while (--n > 0)
            tmp = tmp * tmp * 
                 (((power <<= 1) < 0)? x : 1);
        return tmp;
    }           

pour répondre à l'objection selon laquelle le code ne fonctionnera pas si vous changez le type de pouvoir, eh bien ... en laissant de côté le fait que quiconque change de code ne comprend pas et l'utilise ensuite sans test .....
mais pour résoudre le problème, cette version protège les insensés de cette erreur ... (mais pas d’une myriade d’autres qu’ils pourraient commettre) REMARQUE: non testé.

    public static long IntPower(int x, short power)
    {
        if (power == 0) return 1;
        if (power == 1) return x;
        // ----------------------
        int n = 
            power.GetType() == typeof(short)? 15:
            power.GetType() == typeof(int)? 31:
            power.GetType() == typeof(long)? 63: 0;  

        long tmp = x;
        while (--n > 0)
            tmp = tmp * tmp * 
                 (((power <<= 1) < 0)? x : 1);
        return tmp;
    }

Essayez aussi cet équivalent récursif (plus lent bien sûr):

    public static long IntPower(long x, int power)
    {
        return (power == 0) ? x :
            ((power & 0x1) == 0 ? x : 1) *
                IntPower(x, power >> 1);
    }
19
Charles Bretana

lolz, que diriez-vous:

public static long IntPow(long a, long b)
{
  long result = 1;
  for (long i = 0; i < b; i++)
    result *= a;
  return result;
}
9
mini-me

Voici un blog post qui explique le moyen le plus rapide d’élever des entiers en pouvoirs d’entier. Comme l'un des commentaires le fait remarquer, certaines de ces astuces sont intégrées aux puces.

7
John D. Cook

Utilisez la version double, vérifiez les débordements (max int ou max long) et convertissez en int ou long?

4
bh213

Deux de plus...

    public static int FastPower(int x, int pow)
    {
        switch (pow)
        {
            case 0: return 1;
            case 1: return x;
            case 2: return x * x;
            case 3: return x * x * x;
            case 4: return x * x * x * x;
            case 5: return x * x * x * x * x;
            case 6: return x * x * x * x * x * x;
            case 7: return x * x * x * x * x * x * x;
            case 8: return x * x * x * x * x * x * x * x;
            case 9: return x * x * x * x * x * x * x * x * x;
            case 10: return x * x * x * x * x * x * x * x * x * x; 
            case 11: return x * x * x * x * x * x * x * x * x * x * x; 
            // up to 32 can be added 
            default: // Vilx's solution is used for default
                int ret = 1;
                while (pow != 0)
                {
                    if ((pow & 1) == 1)
                        ret *= x;
                    x *= x;
                    pow >>= 1;
                }
                return ret;
        }
    }

    public static int SimplePower(int x, int pow)
    {
        return (int)Math.Pow(x, pow);
    }

J'ai fait des tests de performance rapides 


  • mini-moi: 32 ms 

  • Sunsetquest (Fast): 37 ms

  • Vilx: 46 ms 

  • Charles Bretana (aka Cook's): 166 ms

  • Sunsetquest (simple): 469 ms 

  • 3dGrabber (version Linq): 868 ms

(Notes de test: Intel i7 2e génération, .net 4, version finale, version finale, bases différentes 1M, exp de 0 à 10 uniquement)

Conclusion: le mini-moi est le meilleur en performance et en simplicité

des tests de précision très minimes ont été effectués

3
Sunsetquest

Ma solution préférée à ce problème est une solution classique de division et de conquête récursive. En fait, il est plus rapide que de multiplier n fois, car il réduit le nombre de multiplications de moitié à chaque fois. 

public static int Power(int x, int n)
{
  // Basis
  if (n == 0)
    return 1;
  else if (n == 1)
    return x;

  // Induction
  else if (n % 2 == 1)
    return x * Power(x*x, n/2);
  return Power(x*x, n/2);
}

Remarque: ceci ne vérifie pas le débordement ni le n négatif. 

2
Evan Moran

Je jette le résultat en int, comme ceci:

double exp = 3.0;
int result = (int)Math.Pow(2.0, exp);

Dans ce cas, il n'y a pas d'erreur d'arrondi car la base et l'exposant sont des entiers . Le résultat sera également un entier.

1
Claudio M

Pour une courte ligne rapide.

int pow(int i, int exp) => (exp == 0) ? 1 : i * pow(i, exp-1);

Il n'y a pas d'exposant négatif ni de contrôle de débordement.

0
Ralph