.NETの組み込みMath.Pow()
関数は、double
ベースをdouble
指数に累乗し、double
結果を返します。
整数で同じことをする最良の方法は何ですか?
追加:Math.Pow()
の結果を(int)にキャストできるようですが、これは常に正しい数値を生成し、丸めエラーは発生しませんか?
かなり速いものは次のようなものかもしれません:
int IntPow(int x, uint pow)
{
int ret = 1;
while ( pow != 0 )
{
if ( (pow & 1) == 1 )
ret *= x;
x *= x;
pow >>= 1;
}
return ret;
}
これは負のパワーを許可しないことに注意してください。それは演習として残しておきます。 :)
追加: ああそうです、ほとんど忘れてしまいました-オーバーフロー/アンダーフローのチェックも追加してください。
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);
ジョンクックのブログリンクで数学を使用して、
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);
}
lolz、どうですか:
public static long IntPow(long a, long b)
{
long result = 1;
for (long i = 0; i < b; i++)
result *= a;
return result;
}
これが ブログ投稿 で、整数を整数乗する最も速い方法を説明しています。コメントの1つが指摘するように、これらのトリックのいくつかはチップに組み込まれています。
二重バージョンを使用し、オーバーフローをチェックして(max intまたはmax longを超えて)、intまたはlongにキャストしますか?
もう二つ...
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はパフォーマンスとシンプルさの両方で最高です
最小限の精度テストが行われました
この問題に対する私のお気に入りの解決策は、古典的な分割統治再帰的解決策です。毎回半分の乗算数を減らすため、実際には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をチェックしません。
次のように、結果をintにキャストします。
double exp = 3.0;
int result = (int)Math.Pow(2.0, exp);
この場合、底と指数は整数であるため、丸め誤差はありません。結果も整数になります。
短いクイックワンライナーの場合。
int pow(int i, int exp) => (exp == 0) ? 1 : i * pow(i, exp-1);
負の指数もオーバーフローチェックもありません。