Comment puis-je trouver la racine carrée d'un BigInteger Java?

y a-t-il une bibliothèque qui trouvera la racine carrée d'un BigInteger? Je veux qu'il calculées hors ligne - une seule fois, et pas à l'intérieur d'une boucle. Donc même une solution coûteuse sur le plan informatique est acceptable.

Je ne veux pas trouver d'algorithme et implémenter. Une solution facilement disponible sera parfaite.

47
demandé sur Edward Falk 2010-12-10 13:24:58

18 réponses

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;
    }
}
24
répondu Edward Falk 2013-06-05 18:45:26

Je ne connais pas de solution de bibliothèque pour votre question. Vous devrez importer une solution de bibliothèque externe de quelque part. Ce que je vous donne ci-dessous est moins compliqué que d'obtenir une bibliothèque externe.

vous pouvez créer votre propre solution de bibliothèque externe dans une classe avec deux méthodes statiques comme montré ci-dessous et ajouter cela à votre collection de bibliothèques externes. Les méthodes n'ont pas besoin d'être des méthodes d'instance et donc elles sont statiques et, commodément, vous n'avez pas à demandez à la classe de les utiliser. La norme pour les racines carrées entières est une valeur de plancher (c.-à-d. le plus grand nombre entier inférieur ou égal à la racine carrée), de sorte que vous pouvez avoir besoin seulement la méthode statique, la méthode de plancher, dans la classe ci-dessous pour la valeur de plancher et peut choisir d'ignorer le plafond (c.-à-d. le plus petit nombre entier supérieur ou égal à la racine carrée) version de la méthode. En ce moment, ils sont dans le paquet par défaut, mais vous pouvez ajouter une déclaration de paquet pour les mettre dans n'importe quel paquet que vous trouverez pratique.

les méthodes sont très simples et les itérations convergent vers la réponse entière la plus proche très, très rapide. Ils jettent un ArgumentException illégale si vous essayez de leur donner un argument négatif. Vous pouvez changer l'exception à une autre, mais vous devez vous assurer qu'un argument negatve lance une sorte d'exception ou au moins ne tente pas le calcul. Entier des racines carrées de nombres négatifs n'existent pas puisque nous ne sommes pas dans le domaine des nombres imaginaires.

ces algorithmes proviennent de simples algorithmes itératifs de racine carrée, très connus, qui ont été utilisés dans les calculs manuels pendant des siècles. Il fonctionne en faisant la moyenne d'une surestimation et de sous-estimer à converger vers une meilleure estimation. Cela peut être répété jusqu'à ce que l'estimation soit aussi proche que désiré.

ils sont basés sur y1 = ((x/y0) + y0) / 2 convergeant vers le plus grand entier, yn, où yn * yn <= X.

cela vous donnera un valeur au sol 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 de 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
20
répondu Jim 2015-01-24 00:47:11

Je ne peux pas en vérifier l'exactitude, mais il existe plusieurs solutions maison sur Google. Le meilleur d'entre eux semblait être celui-ci: http://www.merriampark.com/bigsqrt.htm

essaye aussi le projet de maths Apache commons (une fois Apache récupéré de son bombardement après le billet du blog JCP).

5
répondu Martijn Verburg 2015-07-11 22:13:48

comme Jigar indique, l'itération de Newton est à la fois très simple à comprendre et à mettre en œuvre. Je vais laisser les autres décider s'il est le plus efficace algorithme ou pas pour trouver la racine carrée d'un nombre.

avec récursion, il peut être fait en seulement 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);
}

n est le nombre dont nous voulons trouver la racine carrée, et x0 est le numéro de l'appel précédent, qui sera toujours 1 lorsque lancer le premier appel d'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);
}
3
répondu mantono 2017-05-23 10:31:31

j'avais besoin d'avoir la racine carrée pour les BigIntegers pour mettre en œuvre le tamis quadratique. J'ai utilisé certaines des solutions ici, mais la solution absolument la plus rapide et la meilleure jusqu'à présent est de la bibliothèque BigInteger de Google Guava.

Documentation peut être trouvé ici .

2
répondu Johan S 2014-11-12 20:28:31

une approche alternative, qui est assez légère. Du point de vue 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);
}
2
répondu Debosmit Ray 2016-03-23 20:15:39

pour une première supposition j'utiliserais Math.sqrt(bi.doubleValue()) et vous pouvez utiliser les liens déjà suggérés pour rendre la réponse plus précise.

2
répondu Peter Lawrey 2018-07-11 16:00:58

C'est la meilleure (et la plus courte) de travail solution que j'ai trouvé

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 il fonctionne correctement (et semble rapide)

1
répondu Eran Medan 2013-03-23 20:16:39
    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;
}

j'étais juste en train de travailler sur ce problème et j'ai réussi à écrire un logiciel de recherche de racine carrée récursive en Java. Vous pouvez changer le BDtol à tout ce que vous voulez, mais cela fonctionne assez rapidement et m'a donné l'exemple suivant en conséquence:

Original number 1467839114233645767430925372993335637692683931121739087571335401020890062659255388686508254381502201473025

SQRT -- >383123885216472214589586756787577295328224028242477055.000000000

puis pour confirmation 1467839114233645767430925372993335637692683931121739087571335401020890062659255388686508254381502201473025.0000000000000000000000 151940920"

1
répondu Alexander Jansing 2015-10-01 02:07:29

mise à jour (23juillet2018) : cette technique ne s'applique pas aux valeurs plus élevées. Ont posté une technique différente basée sur la recherche binaire ci-dessous.


j'étais en train d'étudier la factorisation et j'ai fini par écrire ceci.

package com.example.so.math;

import java.math.BigInteger;

/**
 * 
 * <p>/q/how-can-i-find-the-square-root-of-a-java-biginteger-73106/":"+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:

Powers of Integers Moduluo - N

Espérons que cette aide!

1
répondu Ravindra HV 2018-07-23 02:55:47

étrange que personne ne l'a mentionné plus tôt, mais dans java 9 Vous avez sqrt dans BigInteger, donc vous pouvez juste l'utiliser comme ça:

BigInteger b = BigInteger.valueOf(64);
BigInteger eight = b.sqrt();

https://docs.oracle.com/javase/9/docs/api/java/math/BigInteger.html#sqrt--

1
répondu LLL 2018-09-13 08:26:30

je vais seulement jusqu'à la partie entière de la racine carrée mais vous pouvez modifier cet algo rugueux pour aller à autant plus de précision que vous voulez:

  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;
}
0
répondu Ustaman Sangat 2012-10-23 04:18:38

le langage C# a une syntaxe similaire à 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 qu'il serait" système.hors.imprimer.)"

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; il s'agit d'une méthode de racine carrée BigInteger.

cela est facilement remédié. Changer la première méthode à la suivante pour donner à la partie récursive de l'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);            
    }
0
répondu kmillen 2014-08-01 19:12:50

Simplifiée Jim réponse et amélioration de la performance.

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;
    }
}
0
répondu Ilya Gazman 2017-05-23 12:26:25

vous pouvez également utiliser la recherche binaire pour trouver la racine carrée de x vous pouvez également le multiplier par exemple 10^10 et trouver un entier comme m par recherche binaire depuis m^2

System.out.println(m.divide(10^5)+"."+m.mod(10^5));
0
répondu kerpoo 2016-03-26 16:41:31

Voici une solution qui n'utilise pas BigInteger.multiplier ou BigInteger.diviser:

    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};
    }
0
répondu Wes 2018-03-29 05:08:40

la réponse que j'ai posté ci-dessus ne fonctionne pas pour les grands nombres (mais il est intéressant de le faire!). Comme telle affichage d'une approche de recherche binaire pour déterminer racine carrée pour l'exactitude.

package com.example.so.squareroot;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;

/**
 * <p>/q/how-can-i-find-the-square-root-of-a-java-biginteger-73106/"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;

    }


}

en ce qui Concerne Ravindra

0
répondu Ravindra HV 2018-07-07 12:50:57

Une seule ligne peut faire le travail je pense.

Math.pow(bigInt.doubleValue(), (1/n));
-2
répondu Masudias 2012-12-02 01:51:36