web-dev-qa-db-ja.com

C#で*整数*べき乗を行うにはどうしますか?

.NETの組み込みMath.Pow()関数は、doubleベースをdouble指数に累乗し、double結果を返します。

整数で同じことをする最良の方法は何ですか?

追加:Math.Pow()の結果を(int)にキャストできるようですが、これは常に正しい数値を生成し、丸めエラーは発生しませんか?

44
Roman Starkov

かなり速いものは次のようなものかもしれません:

int IntPow(int x, uint pow)
{
    int ret = 1;
    while ( pow != 0 )
    {
        if ( (pow & 1) == 1 )
            ret *= x;
        x *= x;
        pow >>= 1;
    }
    return ret;
}

これは負のパワーを許可しないことに注意してください。それは演習として残しておきます。 :)

追加: ああそうです、ほとんど忘れてしまいました-オーバーフロー/アンダーフローのチェックも追加してください。

43
Vilx-

LINQの誰か?

public static int Pow(this int bas, int exp)
{
    return Enumerable
          .Repeat(bas, exp)
          .Aggregate(1, (a, b) => a * b);
}

拡張としての使用:

var threeToThePowerOfNine = 3.Pow(9);
41
3dGrabber

ジョンクックのブログリンクで数学を使用して、

    public static long IntPower(int x, short power)
    {
        if (power == 0) return 1;
        if (power == 1) return x;
        // ----------------------
        int n = 15;
        while ((power <<= 1) >= 0) n--;

        long tmp = x;
        while (--n > 0)
            tmp = tmp * tmp * 
                 (((power <<= 1) < 0)? x : 1);
        return tmp;
    }           

パワーのタイプを変更するとコードが機能しないという異論に対処するには、まあ...コードを変更した人は誰もが理解できず、テストせずにそれを使用するという点はさておき、.
しかし、この問題に対処するために、このバージョンは愚か者をその間違いから守ります...(しかし、彼らがするかもしれない無数の他のものからではありません)注:テストされていません。

    public static long IntPower(int x, short power)
    {
        if (power == 0) return 1;
        if (power == 1) return x;
        // ----------------------
        int n = 
            power.GetType() == typeof(short)? 15:
            power.GetType() == typeof(int)? 31:
            power.GetType() == typeof(long)? 63: 0;  

        long tmp = x;
        while (--n > 0)
            tmp = tmp * tmp * 
                 (((power <<= 1) < 0)? x : 1);
        return tmp;
    }

次の再帰的な同等物も試してください(もちろん遅い):

    public static long IntPower(long x, int power)
    {
        return (power == 0) ? x :
            ((power & 0x1) == 0 ? x : 1) *
                IntPower(x, power >> 1);
    }
19
Charles Bretana

lolz、どうですか:

public static long IntPow(long a, long b)
{
  long result = 1;
  for (long i = 0; i < b; i++)
    result *= a;
  return result;
}
10
mini-me

これが ブログ投稿 で、整数を整数乗する最も速い方法を説明しています。コメントの1つが指摘するように、これらのトリックのいくつかはチップに組み込まれています。

7
John D. Cook

二重バージョンを使用し、オーバーフローをチェックして(max intまたはmax longを超えて)、intまたはlongにキャストしますか?

4
bh213

もう二つ...

    public static int FastPower(int x, int pow)
    {
        switch (pow)
        {
            case 0: return 1;
            case 1: return x;
            case 2: return x * x;
            case 3: return x * x * x;
            case 4: return x * x * x * x;
            case 5: return x * x * x * x * x;
            case 6: return x * x * x * x * x * x;
            case 7: return x * x * x * x * x * x * x;
            case 8: return x * x * x * x * x * x * x * x;
            case 9: return x * x * x * x * x * x * x * x * x;
            case 10: return x * x * x * x * x * x * x * x * x * x; 
            case 11: return x * x * x * x * x * x * x * x * x * x * x; 
            // up to 32 can be added 
            default: // Vilx's solution is used for default
                int ret = 1;
                while (pow != 0)
                {
                    if ((pow & 1) == 1)
                        ret *= x;
                    x *= x;
                    pow >>= 1;
                }
                return ret;
        }
    }

    public static int SimplePower(int x, int pow)
    {
        return (int)Math.Pow(x, pow);
    }

簡単なパフォーマンステストを行いました


  • mini-me:32ミリ秒

  • サンセットクエスト(高速):37 ms

  • Vilx:46ミリ秒

  • チャールズブレタナ(別名クック):166ミリ秒

  • サンセットクエスト(シンプル):469 ms

  • 3dGrabber(Linqバージョン):868 ms

(テストノート:インテルi7第2世代、.net 4、リリースビルド、リリースラン、100万の異なるベース、0〜10のexpのみ)

結論:mini-meはパフォーマンスとシンプルさの両方で最高です

最小限の精度テストが行​​われました

3
Sunsetquest

この問題に対する私のお気に入りの解決策は、古典的な分割統治再帰的解決策です。毎回半分の乗算数を減らすため、実際にはn倍するより高速です。

public static int Power(int x, int n)
{
  // Basis
  if (n == 0)
    return 1;
  else if (n == 1)
    return x;

  // Induction
  else if (n % 2 == 1)
    return x * Power(x*x, n/2);
  return Power(x*x, n/2);
}

注:これはオーバーフローまたは負のnをチェックしません。

2
Evan Moran

次のように、結果をintにキャストします。

double exp = 3.0;
int result = (int)Math.Pow(2.0, exp);

この場合、底と指数は整数であるため、丸め誤差はありません。結果も整数になります。

1
Claudio M

短いクイックワンライナーの場合。

int pow(int i, int exp) => (exp == 0) ? 1 : i * pow(i, exp-1);

負の指数もオーバーフローチェックもありません。

0
Ralph