Как вы делаете * целочисленное * возведение в степень в C #? - PullRequest
44 голосов
/ 20 декабря 2008

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

Какой лучший способ сделать то же самое с целыми числами?

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

Ответы [ 10 ]

42 голосов
/ 20 декабря 2008

Довольно быстрый может выглядеть примерно так:

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

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

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

41 голосов
/ 09 августа 2012

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);
19 голосов
/ 21 декабря 2008

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

    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);
    }
9 голосов
/ 11 апреля 2012

LOLZ, как насчет:

public static long IntPow(long a, long b)
{
  long result = 1;
  for (long i = 0; i < b; i++)
    result *= a;
  return result;
}
7 голосов
/ 20 декабря 2008

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

4 голосов
/ 20 декабря 2008

Использовать двойную версию, проверить на переполнение (более max int или max long) и привести к int или long?

3 голосов
/ 13 февраля 2014

Еще два ...

    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 мс

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

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

  • 3dGrabber (версия Linq): 868 мс

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

Вывод: mini-me's - лучший по производительности и простоте

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

2 голосов
/ 02 июля 2011

Мое любимое решение этой проблемы - классическое рекурсивное решение «разделяй и властвуй». Это на самом деле быстрее, чем умножение в 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.

1 голос
/ 08 мая 2015

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

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

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

0 голосов
/ 02 сентября 2017

Для короткого быстрого однострочного.

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

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

...