.NET 4.0は、任意の大きさの整数にSystem.Numerics.BigInteger
型を提供します。 BigInteger
の平方根(または妥当な近似値-整数平方根など)を計算する必要があります。ホイールを再実装する必要がないように、誰かがこれのための素晴らしい拡張方法を持っていますか?
BigIntegerが完全な平方でないかどうかを確認してください a Java BigInteger。ここでは、拡張メソッドとしてC#に変換されます。
public static BigInteger Sqrt(this BigInteger n)
{
if (n == 0) return 0;
if (n > 0)
{
int bitLength = Convert.ToInt32(Math.Ceiling(BigInteger.Log(n, 2)));
BigInteger root = BigInteger.One << (bitLength / 2);
while (!isSqrt(n, root))
{
root += n / root;
root /= 2;
}
return root;
}
throw new ArithmeticException("NaN");
}
private static Boolean isSqrt(BigInteger n, BigInteger root)
{
BigInteger lowerBound = root*root;
BigInteger upperBound = (root + 1)*(root + 1);
return (n >= lowerBound && n < upperBound);
}
非公式のテストでは、これは小さな整数の場合、Math.Sqrtよりも約75倍遅いことが示されています。 VSプロファイラーは、ホットスポットとしてisSqrtの乗算を指します。
ニュートン法がbignumの平方根を計算するための最良の方法であるかどうかはわかりません。これは、bignumに対して遅い除算が含まれるためです。加算とシフトのみを使用するCORDICメソッドを使用できます(符号なしintについてはここに表示されています)
static uint isqrt(uint x)
{
int b=15; // this is the next bit we try
uint r=0; // r will contain the result
uint r2=0; // here we maintain r squared
while(b>=0)
{
uint sr2=r2;
uint sr=r;
// compute (r+(1<<b))**2, we have r**2 already.
r2+=(uint)((r<<(1+b))+(1<<(b+b)));
r+=(uint)(1<<b);
if (r2>x)
{
r=sr;
r2=sr2;
}
b--;
}
return r;
}
「ダイクストラ平方根」と呼ばれる、加算とシフトのみを使用する同様の方法があります。これは、たとえばここで説明されています。
平方根を任意の精度で計算する最も簡単で実行可能な方法は、おそらく ニュートン法
短い答え:(ただし、注意してください。詳細については、以下を参照してください)
_Math.Pow(Math.E, BigInteger.Log(pd) / 2)
_
ここで、pd
は、平方根演算を実行するBigInteger
を表します。
長い答えと説明:
この問題を理解するもう1つの方法は、平方根と対数がどのように機能するかを理解することです。
方程式_5^x = 25
_がある場合、x
を解くには、ログを使用する必要があります。この例では、自然対数を使用します(他のベースのログも可能ですが、自然対数が簡単な方法です)。
_5^x = 25
_
書き直し、私たちは持っています:
_x(ln 5) = ln 25
_
Xを分離するために、
_x = ln 25 / ln 5
_
この結果は_x = 2
_になります。しかし、x(x = 2、5 ^ 2)はすでにわかっているので、わからないことを変更して新しい方程式を書き、新しい未知数を解きましょう。 xを平方根演算の結果とします。これは私たちに与えます
_2 = ln 25 / ln x
_
Xを分離するように書き直すと、
_ln x = (ln 25) / 2
_
ログを削除するために、自然対数の特別なIDと特別な番号eを使用します。具体的には、_e^ln x = x
_。方程式を書き直すと、
_e^ln x = e^((ln 25) / 2)
_
左側を単純化すると、
_x = e^((ln 25) / 2)
_
ここで、xは25の平方根になります。このアイデアを任意の根または数に拡張することもでき、xのy番目の根の一般式はe^((ln x) / y)
になります。
これを具体的にC#、BigIntegers、そしてこの質問に具体的に適用するために、式を実装するだけです。 警告:計算は正しいですが、有限の制限があります。この方法では、(操作する数の大きさに応じて)未知の範囲が広い近隣にのみ移動します。おそらくこれが、Microsoftがそのようなメソッドを実装しなかった理由です。
_// A sample generated public key modulus
var pd = BigInteger.Parse("101017638707436133903821306341466727228541580658758890103412581005475252078199915929932968020619524277851873319243238741901729414629681623307196829081607677830881341203504364437688722228526603134919021724454060938836833023076773093013126674662502999661052433082827512395099052335602854935571690613335742455727");
var sqrt = Math.Pow(Math.E, BigInteger.Log(pd) / 2);
Console.WriteLine(sqrt);
_
注:BigInteger.Log()
メソッドはdoubleを返すため、2つの懸念が生じます。 1)数が不正確であり、2)Log()
がBigInteger
入力に対して処理できるものに上限があります。上限を調べるために、自然対数の正規形、つまり_ln x = y
_を見ることができます。言い換えれば、_e^y = x
_。 double
はBigInteger.Log()
の戻り値の型であるため、最大のBigInteger
がe_double.MaxValue
_に引き上げられました。私のコンピューターでは、それは_e^1.79769313486232E+308
_になります。不正確さは処理されません。 BigDecimal
を実装してBigInteger.Log()
を更新したい人はいますか?
消費者は注意してください、しかしそれはあなたを近所に連れて行きます、そして結果を二乗することは元の入力と同様の数を生成します、非常に多くの桁までそしてRedGreenCodeの答えほど正確ではありません。ハッピー(スクエア)応援! ;)
これを選択した言語と変数タイプに変換できます。これは、1 + 3 + 5 ... + n番目の奇数= n ^ 2を利用するJavaScriptの切り捨てられた平方根(私にとって最も新しい)です。すべての変数は整数であり、加算と減算のみを行います。
var truncSqrt = function(n) {
var oddNumber = 1;
var result = 0;
while (n >= oddNumber) {
n -= oddNumber;
oddNumber += 2;
result++;
}
return result;
};
ほぼ10年になりますが、うまくいけば、これは誰かを助けるでしょう。これが私が使っているものです。遅い分割は使用しません。
//source: http://mjs5.com/2016/01/20/c-biginteger-square-root-function/ Michael Steiner, Jan 2016
public static BigInteger Sqrt(BigInteger number)
{
BigInteger n = 0, p = 0;
if (number == BigInteger.Zero)
{
return BigInteger.Zero;
}
var high = number >> 1;
var low = BigInteger.Zero;
while (high > low + 1)
{
n = (high + low) >> 1;
p = n* n;
if (number < p)
{
high = n;
}
else if (number > p)
{
low = n;
}
else
{
break;
}
}
return number == p? n : low;
}