Как вы выполняете * integer * exponentiation в С#?
Встроенная функция Math.Pow()
в .NET повышает базу double
до экспоненты double
и возвращает результат double
.
Какой лучший способ сделать то же самое с целыми числами?
Добавлено: Кажется, что можно просто передать результат Math.Pow()
в (int), но будет ли это всегда производить правильное число и ошибки округления?
Ответы
Ответ 1
Довольно быстро может быть что-то вроде этого:
int IntPow(int x, uint pow)
{
int ret = 1;
while ( pow != 0 )
{
if ( (pow & 1) == 1 )
ret *= x;
x *= x;
pow >>= 1;
}
return ret;
}
Обратите внимание, что это не дает отрицательных полномочий. Я оставлю это как упражнение для вас.:)
Добавлено: О да, почти забыл - также добавьте проверку переполнения/переполнения или вы можете столкнуться с несколькими неприятными сюрпризами в будущем.
Ответ 2
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);
Ответ 3
Используя математику в блоге Джона Кука,
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);
}
Ответ 4
Здесь сообщение в блоге, которое объясняет самый быстрый способ поднять целые числа до целых степеней. Как отмечается в одном из комментариев, некоторые из этих трюков встроены в чипы.
Ответ 5
lolz, как насчет:
public static long IntPow(long a, long b)
{
long result = 1;
for (long i = 0; i < b; i++)
result *= a;
return result;
}
Ответ 6
Использовать двойную версию, проверить переполнение (с максимальным значением int или max long) и применить к int или long?
Ответ 7
Мое любимое решение этой проблемы - классическое разделение и завоевание рекурсивного решения. Это на самом деле быстрее, чем умножение на 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.
Ответ 8
Еще два...
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 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);
}
Я сделал несколько быстрых тестов производительности
-
mini-me: 32 мс
-
Sunsetquest (быстрый): 37 мс
-
Vilx: 46 мс
-
Чарльз Бретана (он же Кук): 166 мс
-
Sunsetquest (простой): 469 мс
-
3dGrabber (версия Linq): 868 мс
(тестовые заметки: intel i7 2nd gen,.net 4, сборка релиза, запуск релиза, 1M разные базы, exp только от 0 до 10)
Заключение: mini-me является лучшим как в производительности, так и в простоте
выполнено очень минимальное тестирование точности
Ответ 9
Я передал результат в int, например:
double exp = 3.0;
int result = (int)Math.Pow(2.0, exp);
В этом случае ошибок округления нет, так как base и expotent являются целыми.
Результат также будет целым.
Ответ 10
Для короткой быстрой однострочной линии.
int pow(int i, int exp) => (exp == 0) ? 1 : i * pow(i, exp-1);
Нет отрицательных показателей или проверки переполнения.