Реализации алгоритмов/Быстрое возведение в степень
Используется представление числа xm:
- .
Реализации
правитьC: Подключение математической библиотеки (для работы со значениями NAN, INF и соответствующими функциями):
#include <math.h>
Тип Real
должен быть задан как:
typedef double Real; /* Вместо double можно подставить другой вещественный тип */
C++: Подключение математической библиотеки (для работы со значениями NAN, INFINITY и сопутствующим функциями):
#include <сmath>
using namespace std;
Каждую нижеприведённую функцию следует предварять строкой:
template <typename Real>
Цикл:
Real fastPower(Real base, int exponent) {
if (isnan(base)) /* Если основание = неопределённость: */
return NAN; /* Неопределённость в любой степени = неопределённость */
if (isinf(base)) { /* Если основание = бесконечность: */
if (!exponent) /* При возведении бесконечности в степень 0 */
return NAN; /* получается неопределённость. */
if (exponent < 0) /* При возведении бесконечности в отрицательную степень получается 0, */
return exponent & 1
? copysign((Real)0, base) /* условно сохраняющий знак основания (+0 или −0) при нечётной степени, */
: (Real)0; /* и беззнаковый при чётной. */
return exponent & 1 /* В положительной степени бесконечность остаётся бесконечностью. */
? base /* В нечётной степени она сохраняет свой знак, */
: INFINITY; /* а в чётной становится строго положительной. */
}
if (base != (Real)0) { /* Сравнение вещественного числа с 0 не вполне корректно, но взято для простоты */
/* Возведение в степень конечного ненулевого основания: */
Real power = (Real)1;
if (exponent < 0) { /* Возведение в отрицательную степень */
exponent = -exponent; /* эквивалентно возведению в такую же положительную степень */
base = (Real)1 / base; /* обратного основанию числа (1 / основание). */
}
while (exponent) { /* Пока текущее значение показателя степени не равно 0: */
if (exponent & 1) /* Если оно нечётно, */
power *= base; /* результат умножается на текущее значение основания. */
exponent >>= 1; /* Показатель степени делится на 2. */
base *= base; /* Основание возводится в квадрат. */
}
return power;
}
/* Возведение в степень нулевого основания: */
if (!exponent)
return NAN; /* 0 в степени 0 = неопределённость */
if (exponent < 0)
return copysign(INFINITY, base); /* 0 в отрицательной степени = бесконечность */
return exponent & 1 ? base : (Real)0; /* 0 в положительной степени = 0 (в нечётной сохраняет знак −, если он есть) */
}
Рекурсия:
static Real _fastPower(Real base, int exponent) {
if (!exponent)
return (Real)1;
if (exponent & 1)
return _fastPower(base, exponent - 1) * base;
base = _fastPower(base, exponent >> 1);
return base * base;
}
Real fastPower(Real base, int exponent) {
if (isnan(base)) /* Если основание = неопределённость: */
return NAN; /* Неопределённость в любой степени = неопределённость */
if (isinf(base)) { /* Если основание = бесконечность: */
if (!exponent) /* При возведении бесконечности в степень 0 */
return NAN; /* получается неопределённость. */
if (exponent < 0) /* При возведении бесконечности в отрицательную степень получается 0, */
return exponent & 1
? copysign((Real)0, base) /* условно сохраняющий знак основания (+0 или −0) при нечётной степени, */
: (Real)0; /* и беззнаковый при чётной. */
return exponent & 1 /* В положительной степени бесконечность остаётся бесконечностью. */
? base /* В нечётной степени она сохраняет свой знак, */
: INFINITY; /* а в чётной становится строго положительной. */
}
if (base != (Real)0) { /* Сравнение вещественного числа с 0 не вполне корректно, но взято для простоты */
if (exponent < 0) {
exponent = -exponent;
base = (Real)1 / base;
}
return _fastPower(base, exponent);
}
/* Возведение в степень нулевого основания: */
if (!exponent)
return NAN; /* 0 в степени 0 = неопределённость */
if (exponent < 0)
return INFINITY; /* 0 в отрицательной степени = бесконечность */
return exponent & 1 ? base : (Real)0; /* 0 в положительной степени = 0 (в нечётной сохраняет знак −, если он есть) */
}
Вместо double
может быть использован другой вещественный, а вместо int
— другой целочисленный тип.
Префикс @
позволяет использовать base
(ключевое слово в C#) как обычный идентификатор (для единообразия с реализациями на других языках).
Цикл:
static double FastPower(double @base, int exponent)
{
if (double.IsNaN(@base)) // Если основание = неопределённость:
return double.NaN; // Неопределённость в любой степени = неопределённость
if (double.IsInfinity(@base)) // Если основание = бесконечность:
{
if (exponent == 0) // При возведении бесконечности в степень 0
return double.NaN; // получается неопределённость.
if (exponent < 0) // При возведении бесконечности в отрицательную степень получается 0,
return (exponent & 1) != 0
? 1 / @base // условно сохраняющий знак основания (+0 или −0) при нечётной степени,
: 0.0; // и беззнаковый при чётной.
return (exponent & 1) != 0 // В положительной степени бесконечность остаётся бесконечностью.
? @base // В нечётной степени она сохраняет свой знак,
: double.PositiveInfinity; // а в чётной становится строго положительной.
}
if (@base != 0.0) // Сравнение вещественного числа с 0 не вполне корректно, но взято для простоты
// Возведение в степень конечного ненулевого основания:
{
double power = 1.0;
if (exponent < 0) // Возведение в отрицательную степень
{
exponent = -exponent; // эквивалентно возведению в такую же положительную степень
@base = 1.0 / @base; // обратного основанию числа (1 / основание).
}
while (exponent > 0) // Пока текущее значение показателя степени не равно 0:
{
if ((exponent & 1) != 0) // Если оно нечётно,
power *= @base; // результат умножается на текущее значение основания.
exponent >>= 1; // Показатель степени делится на 2.
@base *= @base; // Основание возводится в квадрат.
}
return power;
}
// Возведение в степень нулевого основания:
if (exponent == 0)
return double.NaN; // 0 в степени 0 = неопределённость
if (exponent < 0)
return double.PositiveInfinity; // 0 в отрицательной степени = бесконечность
return (exponent & 1) != 0 ? @base : 0.0; // 0 в положительной степени = 0 (в нечётной сохраняет знак −, если он есть)
}
Рекурсия:
private static double _FastPower(double @base, int exponent)
{
if (exponent == 0)
return 1.0;
if ((exponent & 1) != 0)
return _FastPower(@base, exponent - 1) * @base;
@base = _FastPower(@base, exponent >> 1);
return @base * @base;
}
static double FastPower(double @base, int exponent)
{
if (double.IsNaN(@base)) // Если основание = неопределённость:
return double.NaN; // Неопределённость в любой степени = неопределённость
if (double.IsInfinity(@base)) // Если основание = бесконечность:
{
if (exponent == 0) // При возведении бесконечности в степень 0
return double.NaN; // получается неопределённость.
if (exponent < 0) // При возведении бесконечности в отрицательную степень получается 0,
return (exponent & 1) != 0
? 1 / @base // условно сохраняющий знак основания (+0 или −0) при нечётной степени,
: 0.0; // и беззнаковый при чётной.
return (exponent & 1) != 0 // В положительной степени бесконечность остаётся бесконечностью.
? @base // В нечётной степени она сохраняет свой знак,
: double.PositiveInfinity; // а в чётной становится строго положительной.
}
if (@base != 0) // Сравнение вещественного числа с 0 не вполне корректно, но взято для простоты
{
if (exponent < 0) // Возведение в отрицательную степень
{
exponent = -exponent; // эквивалентно возведению в такую же положительную степень
@base = 1.0 / @base; // обратного основанию числа (1 / основание)
}
return _FastPower(@base, exponent);
}
// Возведение в степень нулевого основания:
if (exponent == 0)
return double.NaN; // 0 в степени 0 = неопределённость
if (exponent < 0)
return double.PositiveInfinity; // 0 в отрицательной степени = бесконечность
return (exponent & 1) != 0 ? @base : 0.0; // 0 в положительной степени = 0 (в нечётной сохраняет знак −, если он есть)
}
В нижеприведённых реализациях используются особые вещественные значения Nan
(неопределённость вида 0/0 или 0⁰) и Infinity
(бесконечность) и сопутствующие функции. Их необходимо импортировать из модуля Math:
uses Math;
Цикл:
function FastPower(base: Real; exponent: Integer): Real;
var power: Real;
begin
if IsNaN(base) then { Если основание = неопределённость: }
FastPower := NaN { Неопределённость в любой степени = неопределённость }
else if IsInfinite(base) then { Если основание = бесконечность: }
begin
if exponent = 0 then { При возведении бесконечности в степень 0 }
FastPower := NaN { получается неопределённость. }
else if exponent < 0 then { При возведении бесконечности в отрицательную степень получается 0, }
begin
if Odd(exponent) then
FastPower := 1 / base { условно сохраняющий знак основания (+0 или −0) при нечётной степени, }
else
FastPower := 0 { и беззнаковый при чётной. }
end
else if Odd(exponent) then { В положительной степени бесконечность остаётся бесконечностью. }
FastPower := base { В нечётной степени она сохраняет свой знак, }
else
FastPower := Infinity { а в чётной становится строго положительной. }
end
else if base <> 0 then { Сравнение вещественного числа с 0 не вполне корректно, но взято для простоты }
begin
power := 1;
if exponent < 0 then
begin
exponent := -exponent;
base := 1 / base
end;
while exponent > 0 do
begin
if Odd(exponent) then
power := power * base;
exponent := exponent Shr 1;
base := Sqr(base)
end;
FastPower := power
end
{ Возведение в степень нулевого основания: }
else if exponent = 0 then
FastPower := Nan
else if exponent < 0 then
FastPower := Infinity
else
FastPower := 0
end;
Рекурсия:
function FastPower(base: Real; exponent: Integer): Real;
function _fastPower(base: Real; exponent: Integer): Real;
begin
if exponent = 0 then
_fastPower := 1
else if Odd(exponent) then
_fastPower := _fastPower(base, exponent - 1) * base
else
_fastPower := Sqr(_fastPower(base, exponent Shr 1))
end;
begin
if IsNaN(base) then { Если основание = неопределённость: }
FastPower := NaN { Неопределённость в любой степени = неопределённость }
else if IsInfinite(base) then { Если основание = бесконечность: }
begin
if exponent = 0 then { При возведении бесконечности в степень 0 }
FastPower := NaN { получается неопределённость. }
else if exponent < 0 then { При возведении бесконечности в отрицательную степень получается 0, }
begin
if Odd(exponent) then
FastPower := 1 / base { условно сохраняющий знак основания (+0 или −0) при нечётной степени, }
else
FastPower := 0 { и беззнаковый при чётной. }
end
else if Odd(exponent) then { В положительной степени бесконечность остаётся бесконечностью. }
FastPower := base { В нечётной степени она сохраняет свой знак, }
else
FastPower := Infinity { а в чётной становится строго положительной. }
end
else if base <> 0 then { Сравнение вещественного числа с 0 не вполне корректно, но взято для простоты }
begin
if exponent < 0 then
begin
exponent := -exponent;
base := 1 / base
end;
FastPower := _fastPower(base, exponent)
end
{ Возведение в степень нулевого основания: }
else if exponent = 0 then
FastPower := Nan
else if exponent < 0 then
FastPower := Infinity
else
FastPower := 0
end;
Рекурсивная версия:
def fast_pow(x, y):
if y == 0:
return 1
if y == -1:
return 1. / x
p = fast_pow(x, y // 2)
p *= p
if y % 2:
p *= x
return p
Итеративная версия:
def fast_pow(x, y):
s, v, c = 1, y, x
while (v > 0):
if (v % 2 == 1):
s = s * c
v = v >> 1
c = c * c
return s
Итеративная версия:
function faststep (val, step, mod) {
let mod = parseInt(mod);
let s = 1; let v = step; let c = val;
while (v != 0) {
if (v % 2 == 1) {
s = s * c;
if (mod)
s = s % mod;
}
v = (v - (v % 2)) / 2;
c = c * c;
if (mod)
c = c % mod;
}
return s;
}
Рекурсивная версия:
function fast_pow(x, y) {
if (y == 0)
return 1;
if (y == -1)
return 1. / x;
let p = fast_pow(x, Math.floor(y / 2));
p *= p;
if (y % 2)
p *= x;
return p;
}