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


249

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

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

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

3
「効率」と言うときは、何に関連して効率を指定する必要があります。速度?メモリ使用量?コードサイズ?保守性?
アンディレスター

Cにはpow()関数がありませんか?
2009年

16
はい、しかしそれはintではなくfloatまたはdoubleで機能します
Nathan Fellman

1
実際intのs(そしていくつかのhuge-intクラスではない)に固執している場合、ipowへの多くの呼び出しがオーバーフローします。テーブルを事前に計算し、オーバーフローしていないすべての組み合わせを単純なテーブルルックアップに減らす賢い方法があるかどうか疑問に思います。これは一般的な回答のほとんどよりも多くのメモリを必要としますが、速度の点でより効率的です。
エイドリアンマッカーシー、

pow()安全な機能ではありません
EsmaeelE 2017年

回答:


391

二乗によるべき乗。

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

    return result;
}

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


38
「exp」が負でないことのチェックを追加する必要があります。現在、この関数は間違った答えを出すか、永久にループします。(signed intの>> =がゼロ埋め込みまたは符号拡張のどちらを行うかに応じて、Cコンパイラはどちらの動作も選択できます)。
user9876

23
私はこれのより最適化されたバージョンを書きました。ここから自由にダウンロードできます:gist.github.com/3551590 私のマシンでは、約2.5倍高速でした。
orlp 2012

10
@AkhilJain:それは完全に良いCです。Javaでも、それが有効にするために、交換するwhile (exp)if (exp & 1)してwhile (exp != 0)if ((exp & 1) != 0)、それぞれ。
Ilmari Karonen

3
関数はおそらくを持っているunsigned expか、そうでなければ負をexp適切に処理する必要があります。
Craig McQueen

5
@ZinanXing n回乗算すると、乗算が多くなり、速度が遅くなります。この方法は、乗算を効果的に再利用することで乗算を節約します。たとえば、n ^ 8を計算するには、ナイーブメソッドn*n*n*n*n*n*n*nは7つの乗算を使用します。このアルゴリズムは、代わりに計算しm=n*n、次に、o=m*mその後、p=o*oここで、pわずか3回の乗算と、= N ^ 8。指数が大きいと、パフォーマンスの差が大きくなります。
bames53 2015年

68

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

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

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

この最適な乗算のシーケンスを見つける効率的なアルゴリズムはありません。ウィキペディアから:

最短の追加チェーンを見つけるという問題は、最適な部分構造の仮定を満たさないため、動的プログラミングでは解決できません。つまり、電力をより小さな電力に分解するだけでは不十分です。それぞれの電力は最小限に計算されます。これは、より小さな電力の加算チェーンが関連しているためです(計算を共有するため)。たとえば、上記のa¹⁵の最短加算チェーンでは、a⁶が再利用されるため、a⁶の部分問題は(a³)²として計算する必要があります(たとえば、a⁶=a²(a²)²では3回の乗算が必要) )。


4
@JeremySalwen:この回答で述べられているように、2進数のべき乗は一般的に最も最適な方法ではありません。乗算の最小シーケンスを見つけるための現在知られている効率的なアルゴリズムはありません。
Eric Postpischil 2013

2
@EricPostpischil、それはアプリケーションによって異なります。通常、すべての数値に対して機能する一般的なアルゴリズムは必要ありません。Art of Computer Programming、Vol。2:セミノミカルアルゴリズム
Pacerier 2014

3
この正確な問題については、アレクサンダーステパノフとダニエルローズによる「数学から一般的なプログラミングへ」に説明があります。この本は、ソフトウェアの専門家であるIMHOの棚に置いておく必要があります。
Toby Speight、2015年

2
en.wikipedia.org/wiki/…も参照してください。
lhf

これは、32ビット整数のオーバーフローを引き起こさない255未満の整数の累乗があるため、整数用に最適化できます。各intに最適な乗算構造をキャッシュできます。コード+データは、単にすべてのパワーをキャッシュするよりも小さいと思います...
Josiah Yoder

22

2を累乗する必要がある場合。そうするための最速の方法は、パワーによってビットシフトすることです。

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

2 ** 0 == 1になるようにこれを行うエレガントな方法はありますか?
ロブスモールシャー

16
2 ** 0 == 1 << 0 == 1
ジェイク

14

これが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;
}

pow(71045970,41535484)などの多数の場合は機能しません
Anushree Acharjee '10 / 07/15

16
@AnushreeAcharjeeはもちろんありません。このような数値を計算するには、任意の精度の計算が必要になります。
David Etler、2015

大きな数にはBigInteger#modPowまたはBiginteger#powを使用してください。引数のサイズに基づいた適切なアルゴリズムがすでに実装されています
Raman Yelianevich '24

これはJavaの質問ではありません!
カカウエテフリト

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

私の投票でpow(1, -1)はありませんが、負の指数にもかかわらずintの範囲を離れません。これで、誤って機能するようになりましたpow(-1, -1)
MSalters 2015

唯一の負の指数がありますがintの範囲を離れることはありませ-1です。また、baseが1または-1の場合にのみ機能します。したがって、整数以外の累乗につながらないexp <0のペアは2つ(base、exp)のみです。私はmatemateianであり、量指定子が好きですが、この場合、実際には、負の指数は整数の領域を離れると言ってもいいと思います...
bartgol

6

2の整数の値を何かで累乗したい場合は、シフトオプションを使用することをお勧めします。

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

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


6

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 baseで機能する関数。

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


これは、Abhijit Gaikwadおよびchuxの回答とどのように異なりますか?float提示された2番目のコードブロックでのの使用について議論してください(power(2.0, -3)計算方法を示すことを検討してください)。
greybeard 2016年

@greybeardいくつかのコメントを述べました。それはあなたのクエリを解決できるかもしれません
roottraveller

1
GNU Scientific Libraryにはすでに2つ目の機能があります:gnu.org/software/gsl/manual/html_node/Small-integer-powers.html
Cacahuete Frito

@roottraveller negative exp and float base解決策を説明していただけませんか?なぜtempを使用し、expを2で区切り、exp(偶数/奇数)をチェックするのですか?ありがとう!
レフ

6

2 ^(-x to the 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浮動小数点数についてさらに学習する可能性もあります。その他のべき乗の特殊なケースが現れるかもしれません。


気の利いたソリューションですが、非固有ですか?
paxdiablo 2008

IEEE floatはbase x 2 ^ expです。指数値を変更しても、2のべき乗による乗算以外は何も
起こりませ

あなたはすべて正解です。私のソリューションはもともとかなり昔、2のべき乗に対して明示的に記述されていたことを思い出しました。この問題の特別な解決策になるように回答を書き直しました。
Doug T.

まず、引用されているようにコードが壊れており、コンパイルするには編集が必要です。次に、gccを使用してcore2dでコードが壊れています。このダンプを参照してください 多分私は何か間違ったことをした。IEEEの浮動小数点指数が10台あるので、私はしかし、この意志の仕事を考えていません
空き領域が

3
ベース10?ええと、2進数で10を意味する場合を除いて、ベースは2です:)
Drealmer '19

4

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

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

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

編集:

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


2

パーティーに遅れる:

以下は、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)int*intオーバーフローする可能性があり、これはUBです。


powjii(INT_MAX, 63)でUBが発生しbase *= baseます。乗算できること、または符号なしに移動して折り返すことができることを確認することを検討してください。
カカウエテフリト

exp署名した理由はありません。(-1) ** (-N)が有効であるという奇妙な状況のため、コードが複雑になり、abs(base) > 1もが0の負の値になるためexp、符号なしにしてそのコードを保存することをお勧めします。
カカウエテフリト

1
@CacahueteFrito y署名されているように本当に必要ではなく、あなたがコメントした複雑さをもたらすことは真実ですが、OPのリクエストは具体的pow(int, int)でした。したがって、それらの良いコメントはOPの質問に属します。OPはオーバーフローで何をするかを指定していないため、明確に定義された間違った答えはUBよりわずかに優れています。「最も効率的な方法」を考えると、OPがOFを気にしているとは思えません。
chux-モニカを

1

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

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
整数除算の結果は整数になるため、0、1、または-1しか返さないため、負の指数ははるかに効率的です...
jswolf19

pow(i, INT_MIN)無限ループになる可能性があります。
chux-モニカを2015年

1
@chux:ハードディスクをフォーマットできます:整数オーバーフローはUBです。
MSalters 2015

@MSalters pow(i, INT_MIN)は整数オーバーフローではありません。その結果の割り当てはtemp確かにオーバーフローする可能性があり、時間終わりを引き起こす可能性がありますが、一見ランダムな値で解決します。:-)
chux-モニカを2015年

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

Javaの質問ではありません!
カカウエテフリト

0

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

符号付き整数で実装されたときに未定義の動作を引き起こし、符号なし整数で実装されたときに高入力の誤った値を引き起こすエリアスの回答に加えて、

これは、SquaringによるExponentiationの修正バージョンであり、符号付き整数型でも機能し、不正な値を与えません。

#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の定数式です。また、結果のキャストsqrt()には、int理由は完全な方形の場合の浮動小数点precissionの非常に良いではありませんが、私はどのような実装を知らないようINT_MAX-or任意のタイプ-の最大値は完璧な正方形である、あなたは生きることができますそれと。


0

すべての計算されたパワーを記憶し、必要なときにそれらを使用するアルゴリズムを実装しました。したがって、たとえば、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);
}

public?同じ名前の2つの関数?これはCの質問です。
カカウエテフリト

-1

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

明らかに、これは2の累乗でのみ機能します。

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

私はそれを試しました、それは64ビットでは機能しません、シフトしないで戻ることはありません、そしてこの特定のケースでは、私はすべてのビットをXより低く設定しようとしています。
MarcusJ

1 << 64ですか?それはオーバーフローです。最大の整数だけでは以下である:(1 << 64) - 1
ミカエルロイ

1 << 64 == 0、それが理由です。多分あなたの表現はあなたのアプリに最適です。私は、余分な変数などせずに、マクロに入れることができるものを好む #define MASK(e) (((e) >= 64) ? -1 :( (1 << (e)) - 1))ことは、コンパイル時に計算することができますので、
ミカエル・ロイが

はい、オーバーフローとは何か知っています。私がその言葉を使わなかったからといって、不必要に屈することを勧めるわけではありません。私が言ったように、これは私にとってはうまくいき、それを発見するためにそれを共有するために少しの努力を要しました。とても簡単です。
MarcusJ 2017年

私があなたを怒らせてしまったら、すみません。本当にそういうつもりはなかった。
–MichaëlRoy 2017

-1

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

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

1
これは明らかにC ++の質問ではありません。 (c != c++) == 1
カカウエテフリト
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.