Calculer la racine carrée D'un BigInteger (système.Numérique.BigInteger)
.NET 4.0 fournit le type System.Numerics.BigInteger
pour les entiers arbitrairement grands. Je dois calculer la racine carrée (ou une approximation raisonnable -- par exemple, racine carrée entière) d'un BigInteger
. Pour que je n'ai pas besoin de réimplanter la roue, quelqu'un a-t-il une méthode d'extension pour ça?
5 réponses
vérifiez si BigInteger n'est pas un carré parfait a le code pour calculer la racine carrée entière d'un BigInteger Java. Ici, il est traduit en C#, comme une méthode d'extension.
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);
}
un test informel indique QU'il est environ 75 fois plus lent que les mathématiques.Sqrt, pour petits entiers. Le profileur VS indique les multiplications dans isSqrt comme points chauds.
Je ne suis pas sûr si la méthode de Newton est la meilleure façon de calculer des racines carrées de bignum, parce qu'elle implique des divisions qui sont lents pour les bignums. Vous pouvez utiliser une méthode CORDIC, qui n'utilise que l'addition et les changements (montré ici pour les ints non signés)
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;
}
il existe une méthode similaire qui n'utilise que l'addition et les déplacements, appelée 'racine carrée de Dijkstras', expliquée par exemple ici:
la méthode la plus simple pour calculer une racine carrée avec une précision arbitraire est probablement celle de Newton.
vous pouvez le convertir dans la langue et les types de variables de votre choix. Voici un squareroot tronqué en JavaScript (le plus frais pour moi) qui tire profit de 1+3+5...+ nième Impair = n^2. Toutes les variables sont des entiers, et il ajoute et soustrait.
var truncSqrt=function(n){
var oddNumber=1;
var result=0;
while (n>=oddNumber) {
n-=oddNumber;
oddNumber+=2;
result++; }
return result; }`
brève réponse: (mais attention, voir ci-dessous pour plus de détails)
Math.Pow(Math.E, BigInteger.Log(pd) / 2)
où pd
représente le BigInteger
sur lequel vous voulez effectuer l'opération de racine carrée.
longue réponse et explication:
une autre façon de comprendre ce problème est de comprendre comment fonctionnent les racines carrées et les rondins.
Si vous avez le équation 5^x = 25
, pour résoudre pour x
nous devons utiliser des logs. Dans cet exemple, je vais utiliser des rondins naturels (les rondins dans d'autres bases sont également possibles, mais le rondin naturel est le moyen facile).
5^x = 25
Réécriture, nous avons:
x(ln 5) = ln 25
pour isoler x, nous avons
x = ln 25 / ln 5
nous voyons ce résultat dans x = 2
. Mais puisque nous connaissons déjà x (x = 2, en 5^2), changeons ce que nous ne savons pas et écrivons un nouveau équation et résoudre pour le nouvel inconnu. Que x soit le résultat de l'opération de la racine carrée. Cela nous donne
2 = ln 25 / ln x
réécriture pour isoler x, nous avons
ln x = (ln 25) / 2
pour enlever le rondin, nous utilisons maintenant une identité spéciale du rondin naturel et le numéro spécial e . Plus précisément, e^ln x = x
. Réécrire l'équation nous donne maintenant
e^ln x = e^((ln 25) / 2)
simplifier le côté gauche, nous avons
x = e^((ln 25) / 2)
où x sera la racine carrée de 25. Vous pouvez également étendre cette idée à n'importe quelle racine ou nombre, et la formule générale pour la racine yth de x devient e^((ln x) / y)
.
maintenant pour appliquer cela spécifiquement à C#, BigIntegers, et cette question spécifiquement, nous mettons simplement en œuvre la formule. AVERTISSEMENT: Même si le calcul est correct, il y a des limites. Cette méthode ne vous mènera qu'à proximité, avec une grande portée inconnue (selon la taille du nombre sur lequel vous opérez). Peut-être est-ce la raison pour laquelle Microsoft n'a pas mis en œuvre une telle méthode.
// A sample generated public key modulus
var pd = BigInteger.Parse("101017638707436133903821306341466727228541580658758890103412581005475252078199915929932968020619524277851873319243238741901729414629681623307196829081607677830881341203504364437688722228526603134919021724454060938836833023076773093013126674662502999661052433082827512395099052335602854935571690613335742455727");
var sqrt = Math.Pow(Math.E, BigInteger.Log(pd) / 2);
Console.WriteLine(sqrt);
NOTE: la méthode BigInteger.Log()
retourne un double, donc deux problèmes se posent. 1) le nombre est imprécis, et 2) Il y a une limite supérieure sur ce que Log()
peut gérer pour BigInteger
entrées. Pour examiner la limite supérieure, on peut regarder la forme normale pour le logarithme naturel, qui est ln x = y
. En d'autres termes, e^y = x
. Puisque double
est le type de retour de BigInteger.Log()
, il serait logique que le plus grand BigInteger
serait alors e élevé à double.MaxValue
. Sur mon ordinateur, ça ferait e^1.79769313486232E+308
. L'imprécision est pas gérée. Quelqu'un veut implémenter BigDecimal
et mettre à jour BigInteger.Log()
?
attention consommateur, mais il va vous obtenir dans le quartier, et la quadrature du résultat ne produit un nombre semblable à l'entrée originale, jusqu'à autant de chiffres et pas aussi précis que la réponse de RedGreenCode. Heureux (carré) de l'enracinement de l'! ;)