Как вы делаете* integer * возведение в степень в C#?

встроенный Math.Pow() функция в .NET вызывает double базовый double экспонента и возвращает double результат.

как лучше всего сделать то же самое с целыми числами?

добавил: кажется, что можно просто бросить Math.Pow() result to (int), но всегда ли это приведет к правильному числу и без ошибок округления?

10 ответов


довольно быстро, может быть что-то вроде этого:

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

обратите внимание, что это не дает негативных сил. Я оставлю это тебе в качестве упражнения. :)

добавлено: О да, почти забыл - также добавьте проверку переполнения / underflow, или вы можете получить несколько неприятных сюрпризов по дороге.


LINQ кто-нибудь?

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

использование в качестве расширения:

var threeToThePowerOfNine = 3.Pow(9);

используя математику в блоге Джона Кука,

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

для решения возражения, что код не будет работать, если вы измените тип питания, хорошо... оставляя в стороне тот момент, что любой, кто меняет код, он не понимает, а затем использует его без тестирования.....
но чтобы решить эту проблему, эта версия защищает глупцов от этой ошибки... (Но не от множества других, которые они могли бы сделать) примечание: не проверено.

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

также попробуйте этот рекурсивный эквивалент (медленнее, конечно):

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

здесь блоге это объясняет самый быстрый способ поднять целые числа до целочисленных степеней. Как указывает один из комментариев, некоторые из этих трюков встроены в чипы.


лолз, как насчет:

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

используйте двойную версию, проверите для переполнения (над максимальным инт или максимальным длинным) и бросьте к инт или длиной?


мое любимое решение этой проблемы-классическое рекурсивное решение divide and conquer. На самом деле это быстрее, чем умножение n раз, поскольку оно уменьшает количество умножений в два раза каждый раз.

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

Примечание: это не проверяет переполнение или отрицательное n.


больше двух...

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

Я провел быстрое тестирование производительности


  • Мини-Я : 32 МС

  • Sunsetquest (быстро): 37 МС

  • Vilx: 46 ms

  • Чарльз Бретана (он же Кук): 166 МС

  • Sunsetquest(простой): 469 МС

  • 3dGrabber (по LINQ версия): 868 ms

(примечания по тестированию: intel i7 2nd gen, .net 4, Сборка выпуска, запуск выпуска, 1M различных баз, exp от 0-10 только)

вывод: mini-me является лучшим как в производительности, так и в простоте

очень минимальное испытание точности было сделано


Я бросил результат в int, например:

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

в этом случае нет ошибок округления, потому что база и Экспонента являются целыми. Результат тоже будет целочисленным.


для короткого быстрого ОДН-вкладыша.

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

нет отрицательных показателей или проверок переполнения.