web-dev-qa-db-ja.com

整数ベースのべき関数pow(int、int)を実装する最も効率的な方法

Cで整数を別の整数の累乗にする最も効率的な方法は何ですか?

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

// 5^5
pow(5,5) == 3125
231
Doug T.

二乗によるべき乗。

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

    return result;
}

これは、非対称暗号法で膨大な数のモジュラーべき乗を行うための標準的な方法です。

370
Elias Yarrkov

二乗によるべき乗 は最適な方法ではないことに注意してください。すべての指数値に対して機能する一般的な方法としては、おそらく最善の方法ですが、特定の指数値に対しては、必要な乗算が少ない、より良いシーケンスがあるかもしれません。

たとえば、x ^ 15を計算する場合、2乗によるべき乗の方法は次のようになります。

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

これは合計6回の乗算です。

これは、 addition-chain exponentiation を介した「ちょうど」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

この最適な乗算シーケンスを見つけるための効率的なアルゴリズムはありません。 From Wikipedia

最短の加算チェーンを見つける問題は、最適な部分構造の仮定を満たさないため、動的計画法では解決できません。つまり、パワーを小さなパワーに分解するだけでは十分ではありません。小さなパワーの加算チェーンは(計算を共有するために)関連している可能性があるため、それぞれが最小限に計算されます。たとえば、上のa¹⁵の最短加算チェーンでは、a³が再利用されるため、a⁶の副問題は(a³)²として計算する必要があります(たとえば、a⁶=a²(a²)²ではなく、3つの乗算が必要です) )。

63
Pramod

2を累乗する必要がある場合。これを行う最も速い方法は、パワーによるビットシフトです。

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

これが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;
}
14
user1067920
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);
}
7
Chris Cudmore

非常に特殊なケースは、2 ^(-x to 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);
}

Doubleをベースタイプとして使用すると、2の累乗をさらに取得できます。 (この投稿を整理する手助けをしてくれたコメント者に感謝します)。

IEEE floats についてさらに学習する可能性もあります。これは、指数のその他の特殊なケースが現れる可能性があります。

6
Doug T.

2の累乗の整数の値を取得する場合は、shiftオプションを使用することをお勧めします。

pow(2,5)1<<5に置き換えることができます

これははるかに効率的です。

6
aditya

二乗によるべき乗の効率に関するコメントへのフォローアップと同じように。

このアプローチの利点は、log(n)時間で実行されることです。たとえば、x ^ 1048575(2 ^ 20-1)などの巨大なものを計算する場合、単純なアプローチを使用して100万以上ではなく、ループを20回実行するだけで済みます。

また、コードの複雑さの観点からは、最適な乗算シーケンスを見つけようとするよりも簡単です。これは、Pramodの提案です。

編集:

誰かが私にオーバーフローの可能性があるとタグ付けする前に明確にする必要があると思います。このアプローチは、ある種のhugeintライブラリがあることを前提としています。

4
Jason Z

動作する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))

負のexpおよびfloat baseで機能するpower()関数。

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))

4
roottraveller

パーティーに遅れて:

以下は、y < 0もできる限り最善の方法で処理するソリューションです。

  1. 最大範囲のintmax_tの結果を使用します。 intmax_tに収まらない回答に対する規定はありません。
  2. powjii(0, 0) --> 1これは 一般的な結果 この場合です。
  3. 別の未定義の結果であるpow(0,negative)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)UBであるint*intオーバーフローである可能性があります。

2
chux

負の指数を考慮したより一般的なソリューション

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);
}
1
Abhijit Gaikwad

コンパイル時に指数(および整数)がわかっている場合は、テンプレートを使用してループを展開できます。これはより効率的にすることができますが、ここで基本的な原理を示したいと思いました。

#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;
}
0

もう1つの実装(Java)。最も効率的なソリューションではないかもしれませんが、反復回数は指数ソリューションと同じです。

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;
    }       
}
0
Vaibhav Fouzdar

すべての計算されたパワーを記憶し、必要なときにそれらを使用するアルゴリズムを実装しました。したがって、たとえばx ^ 13は(x ^ 2)^ 2 ^ 2 * x ^ 2 ^ 2 * xと等しくなります。ここでx ^ 2 ^ 2は、もう一度計算する代わりにテーブルから取得されます。これは基本的に@Pramod回答の実装です(ただし、C#で)。必要な乗算の数は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);
}
0
rank1

私の場合は少し異なります。権力からマスクを作成しようとしていますが、とにかく見つけた解決策を共有すると思いました。

明らかに、2のべき乗に対してのみ機能します。

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

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);
   }
}
0
kyorilys

Eliasの回答に加えて、符号付き整数で実装すると未定義の動作が発生し、符号なし整数で実装すると高入力の値が不正になる

これは、符号付き整数型でも機能し、不正な値を与えない、二乗法による指数の修正版です。

#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を使用しましたが、ほとんどの幅(符号付きまたは符号なし)をほとんど変更せずに使用できます。ただし、固定幅以外の整数型を使用する必要がある場合は、SQRT_INT64_MAX(int)sqrt(INT_MAX)intを使用する場合)または類似のもので変更する必要がありますが、最適化する必要がありますが、Cではなく、ugいです定数式。また、sqrt()の結果をintにキャストすることは、完全な正方形の場合の浮動小数点精度のためあまり良くありませんが、INT_MAX-または任意の型の最大値-が完全な正方形である実装を知りません、あなたはそれで生きることができます。

0
Cacahuete Frito