Comment trouver la racine carrée d'un BigInteger Java?
Existe-t-il une bibliothèque qui trouvera la racine carrée d'un BigInteger? Je le veux calculé hors ligne - une seule fois, et pas dans une boucle. Donc, même une solution coûteuse en informatique est acceptable.
Je ne veux pas trouver un algorithme et l'implémenter. Une solution facilement disponible sera parfaite.
Juste pour le fun:
public static BigInteger sqrt(BigInteger x) {
BigInteger div = BigInteger.ZERO.setBit(x.bitLength()/2);
BigInteger div2 = div;
// Loop until we hit the same value twice in a row, or wind
// up alternating.
for(;;) {
BigInteger y = div.add(x.divide(div)).shiftRight(1);
if (y.equals(div) || y.equals(div2))
return y;
div2 = div;
div = y;
}
}
Je ne connais aucune solution de bibliothèque pour votre question. Vous devrez importer une solution de bibliothèque externe à partir de quelque part. Ce que je vous donne ci-dessous est moins compliqué que d'avoir une bibliothèque externe.
Vous pouvez créer votre propre solution de bibliothèque externe dans une classe avec deux méthodes statiques, comme indiqué ci-dessous, et l'ajouter à votre collection de bibliothèques externes. Les méthodes ne doivent pas nécessairement être des méthodes d'instance. Elles sont donc statiques et, de manière pratique, vous n'avez pas besoin d'instance de la classe pour les utiliser. La norme pour les racines carrées entières est une valeur plancher (c'est-à-dire le plus grand nombre entier inférieur ou égal à la racine carrée). Vous pouvez donc avoir besoin d'une seule méthode statique, la méthode floor, dans la classe ci-dessous pour la valeur plancher et pouvez choisir ignorer la version de la méthode de plafond (c'est-à-dire le plus petit entier supérieur ou égal à la racine carrée). Pour le moment, ils sont dans le paquet par défaut, mais vous pouvez ajouter une instruction de paquet pour les mettre dans le paquet qui vous convient.
Les méthodes sont simples et les itérations convergent vers la réponse entière la plus proche, très, très vite. Ils lèvent une exception IllegalArgumentException si vous essayez de leur donner un argument négatif. Vous pouvez remplacer l'exception par une autre, mais vous devez vous assurer qu'un argument négatve génère une sorte d'exception ou du moins ne tente pas le calcul. Les racines carrées entières des nombres négatifs n'existent pas puisque nous ne sommes pas dans le domaine des nombres imaginaires.
Celles-ci proviennent d'algorithmes de racine carrée entiers simples et bien connus qui sont utilisés dans les calculs manuels depuis des siècles. Cela fonctionne en faisant la moyenne d'une surestimation et d'une sous-estimation pour converger vers une meilleure estimation. Cela peut être répété jusqu'à ce que l'estimation soit aussi proche que vous le souhaitez.
Ils sont basés sur y1 = ((x/y0) + y0)/2 convergeant vers le plus grand entier, yn, où yn * yn <= x.
Cela vous donnera une valeur plancher pour une racine carrée BigInteger, y, de x où Y * y <= x et (y + 1) * (y + 1)> x.
Une adaptation peut vous donner une valeur plafond pour la racine carrée BigInteger, y, de x où Y * y> = x et (y - 1) * (y - 1) <x
Les deux méthodes ont été testées et fonctionnent. Ils sont ici:
import Java.math.BigInteger;
public class BigIntSqRoot {
public static BigInteger bigIntSqRootFloor(BigInteger x)
throws IllegalArgumentException {
if (x.compareTo(BigInteger.ZERO) < 0) {
throw new IllegalArgumentException("Negative argument.");
}
// square roots of 0 and 1 are trivial and
// y == 0 will cause a divide-by-zero exception
if (x .equals(BigInteger.ZERO) || x.equals(BigInteger.ONE)) {
return x;
} // end if
BigInteger two = BigInteger.valueOf(2L);
BigInteger y;
// starting with y = x / 2 avoids magnitude issues with x squared
for (y = x.divide(two);
y.compareTo(x.divide(y)) > 0;
y = ((x.divide(y)).add(y)).divide(two));
return y;
} // end bigIntSqRootFloor
public static BigInteger bigIntSqRootCeil(BigInteger x)
throws IllegalArgumentException {
if (x.compareTo(BigInteger.ZERO) < 0) {
throw new IllegalArgumentException("Negative argument.");
}
// square roots of 0 and 1 are trivial and
// y == 0 will cause a divide-by-zero exception
if (x == BigInteger.ZERO || x == BigInteger.ONE) {
return x;
} // end if
BigInteger two = BigInteger.valueOf(2L);
BigInteger y;
// starting with y = x / 2 avoids magnitude issues with x squared
for (y = x.divide(two);
y.compareTo(x.divide(y)) > 0;
y = ((x.divide(y)).add(y)).divide(two));
if (x.compareTo(y.multiply(y)) == 0) {
return y;
} else {
return y.add(BigInteger.ONE);
}
} // end bigIntSqRootCeil
} // end class bigIntSqRoot
Je ne peux pas vérifier leur exactitude, mais il existe plusieurs solutions maison lorsque vous recherchez Google. Le meilleur d'entre eux semblait être celui-ci: http://www.merriampark.com/bigsqrt.htm
Essayez également le projet mathématique Apache commons (une fois qu'Apache se sera remis de son bombardement après la publication du blog JCP).
Comme Jigar déclare, l'itération de Newton est à la fois assez simple à comprendre et à mettre en œuvre. Je laisserai à d'autres le soin de décider s'il s'agit ou non de l'algorithme le plus efficace pour trouver la racine carrée d'un nombre.
Avec la récursivité, cela peut être fait en à peu près deux lignes.
private static BigInteger newtonIteration(BigInteger n, BigInteger x0)
{
final BigInteger x1 = n.divide(x0).add(x0).shiftRight(1);
return x0.equals(x1)||x0.equals(x1.subtract(BigInteger.ONE)) ? x0 : newtonIteration(n, x1);
}
Où n est le numéro dont nous voulons trouver la racine carrée et x0 est le numéro de l'appel précédent, qui sera toujours égal à 1 lors du premier appel depuis une autre méthode. Donc, de préférence, vous le compléterez avec quelque chose comme ça aussi;
public static BigInteger sqrt(final BigInteger number)
{
if(number.signum() == -1)
throw new ArithmeticException("We can only calculate the square root of positive numbers.");
return newtonIteration(number, BigInteger.ONE);
}
Étrange que personne ne l’ait mentionné plus tôt, mais en Java 9, vous avez sqrt dans BigInteger, vous pouvez donc l’utiliser comme ça:
BigInteger nine = BigInteger.valueOf(9);
BigInteger three = nine.sqrt();
https://docs.Oracle.com/javase/9/docs/api/Java/math/BigInteger.html#sqrt--
J'avais besoin de la racine carrée de BigIntegers pour la mise en œuvre du tamis quadratique. J'ai utilisé certaines des solutions ici, mais la solution la plus rapide et la meilleure à ce jour provient de la bibliothèque BigInteger de Google Guava.
La documentation peut être trouvée ici .
Pour une première hypothèse, j'utiliserais Math.sqrt(bi.doubleValue())
et vous pourrez utiliser les liens déjà suggérés pour rendre la réponse plus précise.
Une approche alternative, qui est assez légère. Pour ce qui est de la vitesse, la réponse de Mantono, qui utilise la méthode de Newton, pourrait être préférable dans certains cas.
Voici mon approche ...
public static BigInteger sqrt(BigInteger n) {
BigInteger a = BigInteger.ONE;
BigInteger b = n.shiftRight(1).add(new BigInteger("2")); // (n >> 1) + 2 (ensure 0 doesn't show up)
while (b.compareTo(a) >= 0) {
BigInteger mid = a.add(b).shiftRight(1); // (a+b) >> 1
if (mid.multiply(mid).compareTo(n) > 0)
b = mid.subtract(BigInteger.ONE);
else
a = mid.add(BigInteger.ONE);
}
return a.subtract(BigInteger.ONE);
}
Réponse simplifiée Jim et amélioration des performances.
public class BigIntSqRoot {
private static BigInteger two = BigInteger.valueOf(2L);
public static BigInteger bigIntSqRootFloor(BigInteger x)
throws IllegalArgumentException {
if (checkTrivial(x)) {
return x;
}
if (x.bitLength() < 64) { // Can be cast to long
double sqrt = Math.sqrt(x.longValue());
return BigInteger.valueOf(Math.round(sqrt));
}
// starting with y = x / 2 avoids magnitude issues with x squared
BigInteger y = x.divide(two);
BigInteger value = x.divide(y);
while (y.compareTo(value) > 0) {
y = value.add(y).divide(two);
value = x.divide(y);
}
return y;
}
public static BigInteger bigIntSqRootCeil(BigInteger x)
throws IllegalArgumentException {
BigInteger y = bigIntSqRootFloor(x);
if (x.compareTo(y.multiply(y)) == 0) {
return y;
}
return y.add(BigInteger.ONE);
}
private static boolean checkTrivial(BigInteger x) {
if (x == null) {
throw new NullPointerException("x can't be null");
}
if (x.compareTo(BigInteger.ZERO) < 0) {
throw new IllegalArgumentException("Negative argument.");
}
// square roots of 0 and 1 are trivial and
// y == 0 will cause a divide-by-zero exception
if (x.equals(BigInteger.ZERO) || x.equals(BigInteger.ONE)) {
return true;
} // end if
return false;
}
}
BigDecimal BDtwo = new BigDecimal("2");
BigDecimal BDtol = new BigDecimal(".000000001");
private BigDecimal bigIntSQRT(BigDecimal lNew, BigDecimal lOld, BigDecimal n) {
lNew = lOld.add(n.divide(lOld, 9, BigDecimal.ROUND_FLOOR)).divide(BDtwo, 9, BigDecimal.ROUND_FLOOR);
if (lOld.subtract(lNew).abs().compareTo(BDtol) == 1) {
lNew = bigIntSQRT(lNew, lNew, n);
}
return lNew;
}
Je travaillais juste sur ce problème et ai écrit avec succès un Finder racine carrée récursive en Java. Vous pouvez remplacer le BDtol par ce que vous voulez, mais cela s'exécute assez rapidement et donne l'exemple suivant:
Numéro d'origine 146783911423364576743092537299333563769268393112173908757133540102089006265925538850825438150202201473025
SQRT -> 383123885216472214589586756787577295328224028242477055.000000000
Puis pour confirmation 1467839114233645767430925372993335637692683931121739087571335401020890062659255388508254381502022014730250000000000000000
Mise à jour (23juillet2018): Cette technique ne semble pas fonctionner pour des valeurs plus grandes. Ont posté une technique différente basée sur la recherche binaire ci-dessous.
Je cherchais dans la factorisation et ai fini par écrire ceci.
package com.example.so.math;
import Java.math.BigInteger;
/**
*
* <p>https://stackoverflow.com/questions/4407839/how-can-i-find-the-square-root-of-a-Java-biginteger</p>
* @author Ravindra
* @since 06August2017
*
*/
public class BigIntegerSquareRoot {
public static void main(String[] args) {
int[] values = {5,11,25,31,36,42,49,64,100,121};
for (int i : values) {
BigInteger result = handleSquareRoot(BigInteger.valueOf(i));
System.out.println(i+":"+result);
}
}
private static BigInteger handleSquareRoot(BigInteger modulus) {
int MAX_LOOP_COUNT = 100; // arbitrary for now.. but needs to be proportional to sqrt(modulus)
BigInteger result = null;
if( modulus.equals(BigInteger.ONE) ) {
result = BigInteger.ONE;
return result;
}
for(int i=2;i<MAX_LOOP_COUNT && i<modulus.intValue();i++) { // base-values can be list of primes...
//System.out.println("i"+i);
BigInteger bigIntegerBaseTemp = BigInteger.valueOf(i);
BigInteger bigIntegerRemainderTemp = bigIntegerBaseTemp.modPow(modulus, modulus);
BigInteger bigIntegerRemainderSubtractedByBase = bigIntegerRemainderTemp.subtract(bigIntegerBaseTemp);
BigInteger bigIntegerRemainderSubtractedByBaseFinal = bigIntegerRemainderSubtractedByBase;
BigInteger resultTemp = null;
if(bigIntegerRemainderSubtractedByBase.signum() == -1 || bigIntegerRemainderSubtractedByBase.signum() == 1) {
bigIntegerRemainderSubtractedByBaseFinal = bigIntegerRemainderSubtractedByBase.add(modulus);
resultTemp = bigIntegerRemainderSubtractedByBaseFinal.gcd(modulus);
} else if(bigIntegerRemainderSubtractedByBase.signum() == 0) {
resultTemp = bigIntegerBaseTemp.gcd(modulus);
}
if( resultTemp.multiply(resultTemp).equals(modulus) ) {
System.out.println("Found square root for modulus :"+modulus);
result = resultTemp;
break;
}
}
return result;
}
}
L'approche peut être visualisée comme ceci:
J'espère que cela t'aides!
C'est la meilleure (et la plus courte) solution de travail que j'ai trouvée
http://faruk.akgul.org/blog/javas-missing-algorithm-biginteger-sqrt/
Voici le code:
public static BigInteger sqrt(BigInteger n) {
BigInteger a = BigInteger.ONE;
BigInteger b = new BigInteger(n.shiftRight(5).add(new BigInteger("8")).toString());
while(b.compareTo(a) >= 0) {
BigInteger mid = new BigInteger(a.add(b).shiftRight(1).toString());
if(mid.multiply(mid).compareTo(n) > 0) b = mid.subtract(BigInteger.ONE);
else a = mid.add(BigInteger.ONE);
}
return a.subtract(BigInteger.ONE);
}
Je l'ai testé et cela fonctionne correctement (et semble rapide)
Voici une solution qui n'utilise ni BigInteger.multiply ni BigInteger.divide:
private static final BigInteger ZERO = BigInteger.ZERO;
private static final BigInteger ONE = BigInteger.ONE;
private static final BigInteger TWO = BigInteger.valueOf(2);
private static final BigInteger THREE = BigInteger.valueOf(3);
/**
* This method computes sqrt(n) in O(n.bitLength()) time,
* and computes it exactly. By "exactly", I mean it returns
* not only the (floor of the) square root s, but also the
* remainder r, such that r >= 0, n = s^2 + r, and
* n < (s + 1)^2.
*
* @param n The argument n, as described above.
*
* @return An array of two values, where the first element
* of the array is s and the second is r, as
* described above.
*
* @throws IllegalArgumentException if n is not nonnegative.
*/
public static BigInteger[] sqrt(BigInteger n) {
if (n == null || n.signum() < 0) {
throw new IllegalArgumentException();
}
int bl = n.bitLength();
if ((bl & 1) != 0) {
++ bl;
}
BigInteger s = ZERO;
BigInteger r = ZERO;
while (bl >= 2) {
s = s.shiftLeft(1);
BigInteger crumb = n.testBit(-- bl)
? (n.testBit(-- bl) ? THREE : TWO)
: (n.testBit(-- bl) ? ONE : ZERO);
r = r.shiftLeft(2).add(crumb);
BigInteger d = s.shiftLeft(1);
if (d.compareTo(r) < 0) {
s = s.add(ONE);
r = r.subtract(d).subtract(ONE);
}
}
assert r.signum() >= 0;
assert n.equals(s.multiply(s).add(r));
assert n.compareTo(s.add(ONE).multiply(s.add(ONE))) < 0;
return new BigInteger[] {s, r};
}
Je ne vais que jusqu'à la partie entière de la racine carrée, mais vous pouvez modifier cet algorithme rugueux pour qu'il soit plus précis:
public static void main(String args[]) {
BigInteger N = new BigInteger(
"17976931348623159077293051907890247336179769789423065727343008115"
+ "77326758055056206869853794492129829595855013875371640157101398586"
+ "47833778606925583497541085196591615128057575940752635007475935288"
+ "71082364994994077189561705436114947486504671101510156394068052754"
+ "0071584560878577663743040086340742855278549092581");
System.out.println(N.toString(10).length());
String sqrt = "";
BigInteger divisor = BigInteger.ZERO;
BigInteger toDivide = BigInteger.ZERO;
String Nstr = N.toString(10);
if (Nstr.length() % 2 == 1)
Nstr = "0" + Nstr;
for (int digitCount = 0; digitCount < Nstr.length(); digitCount += 2) {
toDivide = toDivide.multiply(BigInteger.TEN).multiply(
BigInteger.TEN);
toDivide = toDivide.add(new BigInteger(Nstr.substring(digitCount,
digitCount + 2)));
String div = divisor.toString(10);
divisor = divisor.add(new BigInteger(
div.substring(div.length() - 1)));
int into = tryMax(divisor, toDivide);
divisor = divisor.multiply(BigInteger.TEN).add(
BigInteger.valueOf(into));
toDivide = toDivide.subtract(divisor.multiply(BigInteger
.valueOf(into)));
sqrt = sqrt + into;
}
System.out.println(String.format("Sqrt(%s) = %s", N, sqrt));
}
private static int tryMax(final BigInteger divisor,
final BigInteger toDivide) {
for (int i = 9; i > 0; i--) {
BigInteger div = divisor.multiply(BigInteger.TEN).add(
BigInteger.valueOf(i));
if (div.multiply(BigInteger.valueOf(i)).compareTo(toDivide) <= 0)
return i;
}
return 0;
}
La réponse que j'ai affichée ci-dessus ne fonctionne pas pour les grands nombres (mais curieusement!). En tant que tel, nous affichons une approche de recherche binaire pour déterminer l’exactitude de la racine carrée.
package com.example.so.squareroot;
import Java.math.BigInteger;
import Java.util.ArrayList;
import Java.util.List;
/**
* <p>https://stackoverflow.com/questions/4407839/how-can-i-find-the-square-root-of-a-Java-biginteger</p>
* <p> Determine square-root of a number or its closest whole number (binary-search-approach) </p>
* @author Ravindra
* @since 07-July-2018
*
*/
public class BigIntegerSquareRootV2 {
public static void main(String[] args) {
List<BigInteger> listOfSquares = new ArrayList<BigInteger>();
listOfSquares.add(BigInteger.valueOf(5).multiply(BigInteger.valueOf(5)).pow(2));
listOfSquares.add(BigInteger.valueOf(11).multiply(BigInteger.valueOf(11)).pow(2));
listOfSquares.add(BigInteger.valueOf(15485863).multiply(BigInteger.valueOf(10000019)).pow(2));
listOfSquares.add(BigInteger.valueOf(533000401).multiply(BigInteger.valueOf(982451653)).pow(2));
listOfSquares.add(BigInteger.valueOf(11).multiply(BigInteger.valueOf(23)));
listOfSquares.add(BigInteger.valueOf(11).multiply(BigInteger.valueOf(23)).pow(2));
for (BigInteger bigIntegerNumber : listOfSquares) {
BigInteger squareRoot = calculateSquareRoot(bigIntegerNumber);
System.out.println("Result :"+bigIntegerNumber+":"+squareRoot);
}
System.out.println("*********************************************************************");
for (BigInteger bigIntegerNumber : listOfSquares) {
BigInteger squareRoot = determineClosestWholeNumberSquareRoot(bigIntegerNumber);
System.out.println("Result :"+bigIntegerNumber+":"+squareRoot);
}
}
/*
Result :625:25
Result :14641:121
Result :23981286414105556927200571609:154858924231397
Result :274206311533451346298141971207799609:523647125012112853
Result :253:null
Result :64009:253
*/
public static BigInteger calculateSquareRoot(BigInteger number) {
/*
* Can be optimized by passing a bean to store the comparison result and avoid having to re-calculate.
*/
BigInteger squareRootResult = determineClosestWholeNumberSquareRoot(number);
if( squareRootResult.pow(2).equals(number)) {
return squareRootResult;
}
return null;
}
/*
Result :625:25
Result :14641:121
Result :23981286414105556927200571609:154858924231397
Result :274206311533451346298141971207799609:523647125012112853
Result :253:15
Result :64009:253
*/
private static BigInteger determineClosestWholeNumberSquareRoot(BigInteger number) {
BigInteger result = null;
if(number.equals(BigInteger.ONE)) {
return BigInteger.ONE;
} else if( number.equals(BigInteger.valueOf(2)) ) {
return BigInteger.ONE;
} else if( number.equals(BigInteger.valueOf(3)) ) {
return BigInteger.ONE;
} else if( number.equals(BigInteger.valueOf(4)) ) {
return BigInteger.valueOf(2);
}
BigInteger tempBaseLow = BigInteger.valueOf(2);
BigInteger tempBaseHigh = number.shiftRight(1); // divide by 2
int loopCount = 11;
while(true) {
if( tempBaseHigh.subtract(tempBaseLow).compareTo(BigInteger.valueOf(loopCount)) == -1 ) { // for lower numbers use for-loop
//System.out.println("Breaking out of while-loop.."); // uncomment-for-debugging
break;
}
BigInteger tempBaseMid = tempBaseHigh.subtract(tempBaseLow).shiftRight(1).add(tempBaseLow); // effectively mid = [(high-low)/2]+low
BigInteger tempBaseMidSquared = tempBaseMid.pow(2);
int comparisonResultTemp = tempBaseMidSquared.compareTo(number);
if(comparisonResultTemp == -1) { // move mid towards higher number
tempBaseLow = tempBaseMid;
} else if( comparisonResultTemp == 0 ) { // number is a square ! return the same !
return tempBaseMid;
} else { // move mid towards lower number
tempBaseHigh = tempBaseMid;
}
}
BigInteger tempBasePrevious = tempBaseLow;
BigInteger tempBaseCurrent = tempBaseLow;
for(int i=0;i<(loopCount+1);i++) {
BigInteger tempBaseSquared = tempBaseCurrent.pow(2);
//System.out.println("Squared :"+tempBaseSquared); // uncomment-for-debugging
int comparisonResultTempTwo = tempBaseSquared.compareTo(number);
if( comparisonResultTempTwo == -1 ) { // move current to previous and increment current...
tempBasePrevious = tempBaseCurrent;
tempBaseCurrent = tempBaseCurrent.add(BigInteger.ONE);
} else if( comparisonResultTempTwo == 0 ) { // is an exact match!
tempBasePrevious = tempBaseCurrent;
break;
} else { // we've identified the point of deviation.. break..
//System.out.println("breaking out of for-loop for square root..."); // uncomment-for-debugging
break;
}
}
result = tempBasePrevious;
//System.out.println("Returning :"+result); // uncomment-for-debugging
return result;
}
}
Cordialement Ravindra
La syntaxe du langage C # est similaire à celle de Java. J'ai écrit cette solution récursive.
static BigInteger fsqrt(BigInteger n)
{
string sn = n.ToString();
return guess(n, BigInteger.Parse(sn.Substring(0, sn.Length >> 1)), 0);
}
static BigInteger guess(BigInteger n, BigInteger g, BigInteger last)
{
if (last >= g - 1 && last <= g + 1) return g;
else return guess(n, (g + (n / g)) >> 1, g);
}
Appelez ce code comme ceci (en Java, je suppose que ce serait "System.out.print").
Console.WriteLine(fsqrt(BigInteger.Parse("783648276815623658365871365876257862874628734627835648726")));
Et la réponse est: 27993718524262253829858552106
Avertissement: je comprends que cette méthode ne fonctionne pas pour les nombres inférieurs à 10; c'est une méthode de racine carrée BigInteger.
Ceci est facilement corrigé. Modifiez la première méthode comme suit pour donner à la partie récursive un peu d’espace pour respirer.
static BigInteger fsqrt(BigInteger n)
{
if (n > 999)
{
string sn = n.ToString();
return guess(n, BigInteger.Parse(sn.Substring(0, sn.Length >> 1)), 0);
}
else return guess(n, n >> 1, 0);
}