Каков наиболее эффективный способ поднять целое число до степени другого целого числа в C?
// 2^3
pow(2,3) == 8
// 5^5
pow(5,5) == 3125
c
algorithm
math
exponentiation
Дуг Т.
источник
источник
int
s (а не какого-то класса large-int), многие вызовы ipow будут переполнены. Это заставляет меня задуматься, есть ли умный способ предварительно рассчитать таблицу и свести все непересекающиеся комбинации к простому поиску в таблице. Это займет больше памяти, чем большинство общих ответов, но, возможно, будет более эффективным с точки зрения скорости.pow()
не безопасная функцияОтветы:
Возведение в степень путем возведения в квадрат.
Это стандартный метод модульного возведения в степень для огромных чисел в асимметричной криптографии.
источник
while (exp)
иif (exp & 1)
наwhile (exp != 0)
иif ((exp & 1) != 0)
соответственно.unsigned exp
, или иначе обрабатывать негативexp
правильно.n*n*n*n*n*n*n*n
использует 7 умножений. Этот алгоритм вместо этого вычисляетm=n*n
, аo=m*m
затемp=o*o
, гдеp
= n ^ 8, только с тремя умножениями. При больших показателях разница в производительности значительна.Обратите внимание, что возведение в квадрат по квадратам - не самый оптимальный метод. Вероятно, это лучший способ, который вы можете использовать в качестве общего метода, который работает для всех значений показателя, но для конкретного значения показателя может быть лучшая последовательность, которая требует меньше умножений.
Например, если вы хотите вычислить x ^ 15, метод возведения в степень путем возведения в квадрат даст вам:
Это всего 6 умножений.
Оказывается, это можно сделать, используя «всего лишь» 5 умножений через возведение в степень сложения .
Не существует эффективных алгоритмов для нахождения этой оптимальной последовательности умножений. Из Википедии :
источник
Если вам нужно поднять 2 до степени. Самый быстрый способ сделать это - сдвинуться с места силой.
источник
2 ** 0 == 1 << 0 == 1
Вот метод в Java
источник
источник
pow(1, -1)
не покидает диапазон int, несмотря на отрицательный показатель. Теперь это работает случайно, как иpow(-1, -1)
.Если вы хотите получить значение целого числа для 2, возведенное в степень чего-либо, всегда лучше использовать параметр shift:
pow(2,5)
можно заменить на1<<5
Это гораздо эффективнее.
источник
power()
функция для работы только для целыхСложность = O (log (exp))
power()
функция для работы с отрицательным опытом и плавающей базой .Сложность = O (log (exp))
источник
float
во втором представленном блоке кода (рассмотрите возможность показаpower(2.0, -3)
вычислений).negative exp and float base
решение? почему мы используем temp, разделяем exp на 2 и проверяем exp (четное / нечетное)? Спасибо!Чрезвычайно специализированный случай, когда вам нужно сказать 2 ^ (- x к y), где x, конечно, отрицателен, а y слишком велик, чтобы делать смещение на int. Вы все еще можете делать 2 ^ х в постоянное время, прикручивая поплавком.
Вы можете получить больше степеней 2, используя удвоение в качестве базового типа. (Большое спасибо комментаторам за помощь в выравнивании этого поста).
Также существует вероятность того, что, узнав больше о плавающих элементах IEEE, могут проявиться и другие особые случаи возведения в степень.
источник
Так же, как продолжение комментариев по эффективности возведения в степень путем возведения в квадрат.
Преимущество такого подхода заключается в том, что он выполняется за время log (n). Например, если вы собирались вычислить что-то огромное, например, x ^ 1048575 (2 ^ 20 - 1), вам нужно всего лишь пройти цикл 20 раз, а не 1 миллион +, используя наивный подход.
Кроме того, с точки зрения сложности кода это проще, чем пытаться найти наиболее оптимальную последовательность умножений, как предложил Прамод.
Редактировать:
Я думаю, мне следует уточнить, прежде чем кто-то пометит меня на предмет потенциального переполнения. Этот подход предполагает, что у вас есть какая-то огромная библиотека.
источник
Поздно на вечеринку:
Ниже приведено решение, которое также работает
y < 0
как можно лучше.intmax_t
для максимальной дальности. Там нет положения для ответов, которые не вписываются вintmax_t
.powjii(0, 0) --> 1
что является общим результатом для этого случая.pow(0,negative)
другой неопределенный результат, возвращаетINTMAX_MAX
Этот код использует цикл навсегда,
for(;;)
чтобы избежать окончательногоbase *= base
общего в других зацикленных решениях. Это умножение 1) не нужно и 2) может бытьint*int
переполнением, которое является UB.источник
powjii(INT_MAX, 63)
вызывает UB вbase *= base
. Подумайте о том, чтобы проверить, что вы можете умножить или перейти к неподписанному и позволить ему обернуться.exp
быть подписанным. Это усложняет код из-за нечетной ситуации, где(-1) ** (-N)
допустимо, и любаяabs(base) > 1
будет0
для отрицательных значенийexp
, поэтому лучше оставить его без знака и сохранить этот код.y
поскольку подписанный документ на самом деле не нужен и приносит сложности, которые вы прокомментировали, тем не менее, запрос OP был конкретнымpow(int, int)
. Таким образом, эти хорошие комментарии относятся к вопросу ОП. Так как OP не указал, что делать при переполнении, четко определенный неправильный ответ лишь немного лучше, чем UB. Учитывая "наиболее эффективный способ", я сомневаюсь, что OP заботится о OF.более общее решение с учетом отрицательной степени
источник
pow(i, INT_MIN)
может быть бесконечный цикл.pow(i, INT_MIN)
не является целочисленным переполнением. Назначение этого результата,temp
безусловно, может быть переполнено, что может привести к концу времени , но я согласен на случайное значение. :-)Еще одна реализация (на Java). Может быть не самое эффективное решение, но число итераций такое же, как у экспоненциального решения.
источник
Я использую рекурсивный, если exp четный, 5 ^ 10 = 25 ^ 5.
источник
В дополнение к ответу Элиаса, который приводит к неопределенному поведению при реализации с целыми числами со знаком и к неправильным значениям для высокого ввода при реализации с целыми числами без знака,
Вот модифицированная версия Exponentiation by Squaring, которая также работает со знаковыми целочисленными типами и не дает неправильных значений:
Соображения для этой функции:
Если произойдет какое-либо переполнение или упаковка,
return 0;
Я использовал
int64_t
, но любая ширина (со знаком или без знака) может быть использована с небольшими изменениями. Однако, если вам необходимо использовать не фиксированную ширину целочисленного типа, то нужно изменитьSQRT_INT64_MAX
путь(int)sqrt(INT_MAX)
(в случае использованияint
) или нечто подобное, которые должны быть оптимизированы, но это уродливее, а не выражение константы С. Кроме того, приведение результатаsqrt()
к anint
не очень хорошо из-за точности с плавающей запятой в случае идеального квадрата, но поскольку я не знаю ни одной реализации, гдеINT_MAX
- или максимум любого типа - является идеальным квадратом, вы можете жить с этим.источник
Я реализовал алгоритм, который запоминает все вычисленные мощности, а затем использует их при необходимости. Так, например, x ^ 13 равно (x ^ 2) ^ 2 ^ 2 * x ^ 2 ^ 2 * x, где x ^ 2 ^ 2 это взято из таблицы вместо того, чтобы вычислять это снова. Это в основном реализация ответа @Pramod (но в C #). Необходимое количество умножения: Ceil (Log n)
источник
public
? 2 функции названы одинаково? Это вопрос C.Мой случай немного отличается, я пытаюсь создать маску из власти, но я решил поделиться решением, которое нашел в любом случае.
Очевидно, это работает только для степеней 2.
источник
#define MASK(e) (((e) >= 64) ? -1 :( (1 << (e)) - 1))
, так, чтобы они могли быть вычислены во время компиляцииЕсли вы знаете показатель (и это целое число) во время компиляции, вы можете использовать шаблоны, чтобы развернуть цикл. Это можно сделать более эффективным, но я хотел продемонстрировать здесь основной принцип:
Мы прекращаем рекурсию, используя специализацию шаблона:
Экспонента должна быть известна во время выполнения,
источник
(c != c++) == 1