Наиболее эффективный способ реализации целочисленной силовой функции pow (int, int)

Каков наиболее эффективный способ поднять целое число до степени другого целого числа в C?

// 2^3
pow(2,3) == 8

// 5^5
pow(5,5) == 3125

Ответ 1

Экспоненциальное возведение в квадрат.

int ipow(int base, int exp)
{
    int result = 1;
    for (;;)
    {
        if (exp & 1)
            result *= base;
        exp >>= 1;
        if (!exp)
            break;
        base *= base;
    }

    return result;
}

Это стандартный метод для модульного возведения в степень для огромных чисел в асимметричной криптографии.

Ответ 2

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

Например, если вы хотите вычислить x ^ 15, метод возведения в степень по квадрату даст вам:

x^15 = (x^7)*(x^7)*x 
x^7 = (x^3)*(x^3)*x 
x^3 = x*x*x

Это всего 6 умножений.

Оказывается, это можно сделать, используя "просто" 5 умножений с помощью экспансионизации дополнительных цепочек.

n*n = n^2
n^2*n = n^3
n^3*n^3 = n^6
n^6*n^6 = n^12
n^12*n^3 = n^15

Нет эффективных алгоритмов для поиска этой оптимальной последовательности умножений. Из Wikipedia:

Проблема нахождения кратчайшей сложенной цепи не может быть решена путем динамического программирования, поскольку она не удовлетворяет предположению о оптимальной субструктуре. То есть недостаточно разложить мощность на более мелкие мощности, каждая из которых вычисляется минимально, так как цепи присоединения для меньших степеней могут быть связаны (для обмена вычислениями). Например, в кратчайшей цепочке добавления для a¹ выше подзадача для a⁶ должна быть вычислена как (a³) ², поскольку a3 повторно используется (в отличие от, например, a⁶ = a² (a²) ², что также требует трех умножений).

Ответ 3

Если вам нужно поднять 2 до мощности. Самый быстрый способ сделать это - сдвиг бит по мощности.

2 ** 3 == 1 << 3 == 8
2 ** 30 == 1 << 30 == 1073741824 (A Gigabyte)

Ответ 4

Вот метод в Java

private int ipow(int base, int exp)
{
    int result = 1;
    while (exp != 0)
    {
        if ((exp & 1) == 1)
            result *= base;
        exp >>= 1;
        base *= base;
    }

    return result;
}

Ответ 5

int pow( int base, int exponent)

{   // Does not work for negative exponents. (But that would be leaving the range of int) 
    if (exponent == 0) return 1;  // base case;
    int temp = pow(base, exponent/2);
    if (exponent % 2 == 0)
        return temp * temp; 
    else
        return (base * temp * temp);
}

Ответ 6

Чрезвычайно специализированный случай: когда вам нужно сказать, что 2 ^ (- x для y), где x, конечно, отрицательно, а y слишком велико, чтобы делать сдвиг по int. Вы все равно можете сделать 2 ^ x в течение постоянного времени, вставив поплавок.

struct IeeeFloat
{

    unsigned int base : 23;
    unsigned int exponent : 8;
    unsigned int signBit : 1;
};


union IeeeFloatUnion
{
    IeeeFloat brokenOut;
    float f;
};

inline float twoToThe(char exponent)
{
    // notice how the range checking is already done on the exponent var 
    static IeeeFloatUnion u;
    u.f = 2.0;
    // Change the exponent part of the float
    u.brokenOut.exponent += (exponent - 1);
    return (u.f);
}

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

Также существует возможность узнать больше о IEEE float, могут появиться другие особые случаи возведения в степень.

Ответ 7

Если вы хотите получить значение целого числа для 2, поднятого до степени чего-то, всегда лучше использовать опцию shift:

pow(2,5) можно заменить на 1<<5

Это намного эффективнее.

Ответ 8

Также как комментарий к эффективности возведения в степень возведения в квадрат.

Преимущество такого подхода заключается в том, что он работает в log (n) времени. Например, если вы собираетесь вычислить что-то огромное, например x ^ 1048575 (2 ^ 20 - 1), вам нужно пройти через цикл 20 раз, а не 1 миллион +, используя наивный подход.

Кроме того, с точки зрения сложности кода это проще, чем пытаться найти наиболее оптимальную последовательность умножений, предложение a la Pramod.

Edit:

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

Ответ 9

power() для работы Только целые числа

int power(int base, unsigned int exp){

    if (exp == 0)
        return 1;
    int temp = power(base, exp/2);
    if (exp%2 == 0)
        return temp*temp;
    else
        return base*temp*temp;

}

Сложность = O (log (exp))

power() работает для отрицательной exp и базы данных.

float power(float base, int exp) {

    if( exp == 0)
       return 1;
    float temp = power(base, exp/2);       
    if (exp%2 == 0)
        return temp*temp;
    else {
        if(exp > 0)
            return base*temp*temp;
        else
            return (temp*temp)/base; //negative exponent computation 
    }

} 

Сложность = O (log (exp))

Ответ 10

Поздно участнику:

Ниже приведено решение, которое также имеет дело с y < 0 как можно лучше.

  • Он использует результат intmax_t для максимального диапазона. Не существует условий для ответов, которые не соответствуют intmax_t.
  • powjii(0, 0) --> 1, который является общим результатом для этого случая.
  • pow(0,negative), другой результат undefined возвращает INTMAX_MAX

    intmax_t powjii(int x, int y) {
      if (y < 0) {
        switch (x) {
          case 0:
            return INTMAX_MAX;
          case 1:
            return 1;
          case -1:
            return y % 2 ? -1 : 1;
        }
        return 0;
      }
      intmax_t z = 1;
      intmax_t base = x;
      for (;;) {
        if (y % 2) {
          z *= base;
        }
        y /= 2;
        if (y == 0) {
          break; 
        }
        base *= base;
      }
      return z;
    }
    

Этот код использует непрерывный цикл for(;;), чтобы избежать окончательного base *= base общего в других петлевых решениях. Это умножение 1) не требуется и 2) может быть переполнением int*int, которое является UB.

Ответ 11

более общее решение с учетом отрицательной экспоненты

private static int pow(int base, int exponent) {

    int result = 1;
    if (exponent == 0)
        return result; // base case;

    if (exponent < 0)
        return 1 / pow(base, -exponent);
    int temp = pow(base, exponent / 2);
    if (exponent % 2 == 0)
        return temp * temp;
    else
        return (base * temp * temp);
}

Ответ 12

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

public static long pow(long base, long exp){        
    if(exp ==0){
        return 1;
    }
    if(exp ==1){
        return base;
    }

    if(exp % 2 == 0){
        long half = pow(base, exp/2);
        return half * half;
    }else{
        long half = pow(base, (exp -1)/2);
        return base * half * half;
    }       
}

Ответ 13

Я использую рекурсивный, если exp четный, 5 ^ 10 = 25 ^ 5.

int pow(float base,float exp){
   if (exp==0)return 1;
   else if(exp>0&&exp%2==0){
      return pow(base*base,exp/2);
   }else if (exp>0&&exp%2!=0){
      return base*pow(base,exp-1);
   }
}

Ответ 14

Я реализовал алгоритм, который запоминает все вычисленные мощности, а затем использует их при необходимости. Так, например, x ^ 13 равно (x ^ 2) ^ 2 ^ 2 * x ^ 2 ^ 2 * x, где x ^ 2 ^ 2 это взято из таблицы вместо того, чтобы вычислять это снова. Это в основном реализация ответа @Pramod (но в С#). Необходимое количество умножения: Ceil (Log n)

public static int Power(int base, int exp)
{
    int tab[] = new int[exp + 1];
    tab[0] = 1;
    tab[1] = base;
    return Power(base, exp, tab);
}

public static int Power(int base, int exp, int tab[])
    {
         if(exp == 0) return 1;
         if(exp == 1) return base;
         int i = 1;
         while(i < exp/2)
         {  
            if(tab[2 * i] <= 0)
                tab[2 * i] = tab[i] * tab[i];
            i = i << 1;
          }
    if(exp <=  i)
        return tab[i];
     else return tab[i] * Power(base, exp - i, tab);
}

Ответ 15

В дополнение к ответу Элиаса, который приводит к неопределенному поведению при реализации с целыми числами со знаком и к неправильным значениям для высокого ввода при реализации с целыми числами без знака,

Вот модифицированная версия Exponentiation by Squaring, которая также работает со знаковыми целочисленными типами и не дает неправильных значений:

#include <stdint.h>

#define SQRT_INT64_MAX (INT64_C(0xB504F333))

int64_t alx_pow_s64 (int64_t base, uint8_t exp)
{
    int_fast64_t    base_;
    int_fast64_t    result;

    base_   = base;

    if (base_ == 1)
        return  1;
    if (!exp)
        return  1;
    if (!base_)
        return  0;

    result  = 1;
    if (exp & 1)
        result *= base_;
    exp >>= 1;
    while (exp) {
        if (base_ > SQRT_INT64_MAX)
            return  0;
        base_ *= base_;
        if (exp & 1)
            result *= base_;
        exp >>= 1;
    }

    return  result;
}

Соображения для этой функции:

(1 ** N) == 1
(N ** 0) == 1
(0 ** 0) == 1
(0 ** N) == 0

Если произойдет какое-либо переполнение или перенос, return 0;

Я использовал int64_t, но любую ширину (со int64_t или без знака) можно использовать с небольшими изменениями. Однако, если вам нужно использовать целочисленный тип не фиксированной ширины, вам нужно изменить SQRT_INT64_MAX на (int)sqrt(INT_MAX) (в случае использования int) или что-то подобное, что должно быть оптимизировано, но это уродливее, а не константное выражение C. Кроме того, приведение результата sqrt() к типу int не очень хорошо из-за точности с плавающей запятой в случае идеального квадрата, но, как я не знаю ни одной реализации, где INT_MAX -or максимум любого type- равен идеальный квадрат, вы можете жить с этим.

Ответ 16

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

Очевидно, что он работает только для степеней 2.

Mask1 = 1 << (Exponent - 1);
Mask2 = Mask1 - 1;
return Mask1 + Mask2;

Ответ 17

Если вы знаете показатель экспоненты (и это целое число) во время компиляции, вы можете использовать шаблоны для разворачивания цикла. Это можно сделать более эффективным, но я хотел продемонстрировать основной принцип здесь:

#include <iostream>

template<unsigned long N>
unsigned long inline exp_unroll(unsigned base) {
    return base * exp_unroll<N-1>(base);
}

Мы завершаем рекурсию с использованием специализированной специализации:

template<>
unsigned long inline exp_unroll<1>(unsigned base) {
    return base;
}

Показатель должен быть известен во время выполнения,

int main(int argc, char * argv[]) {
    std::cout << argv[1] <<"**5= " << exp_unroll<5>(atoi(argv[1])) << ;std::endl;
}

Ответ 18

Игнорируя особый случай 2, поднятый до мощности, наиболее эффективным способом будет простая итерация.

int pow(int base, int pow) {
  int res = 1;
  for(int i=pow; i<pow; i++)
    res *= base;

  return res;
}

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