How can I find the Square Root of a Java BigInteger?

后端 未结 19 996
[愿得一人]
[愿得一人] 2020-11-28 08:10

Is there a library that will find the square root of a BigInteger? I want it computed offline - only once, and not inside any loop. So even computationally expensive solutio

相关标签:
19条回答
  • 2020-11-28 08:23

    An alternative approach, which is quite light. Speed-wise, Mantono's answer, that uses the Newton method, might be preferable for certain cases.

    Here's my approach...

    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);
    }
    
    0 讨论(0)
  • 2020-11-28 08:25

    I am only going as far as the integer part of the square root but you can modify this rough algo to go to as much more precision as you want:

      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 讨论(0)
  • 2020-11-28 08:27

    Update (23July2018) : This technique does not apper to work for larger values. Have posted a different technique based on binary-search below.


    I was looking into factorization and ended up writing this.

    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;
        }
    
    
    }
    

    The approach can be visualized like this :

    Hope this helps!

    0 讨论(0)
  • 2020-11-28 08:28

    For an initial guess I would use Math.sqrt(bi.doubleValue()) and you can use the links already suggested to make the answer more accurate.

    0 讨论(0)
  • 2020-11-28 08:28

    The answer I posted above doesn't work for large numbers (but interestingly so!). As such posting a binary-search approach for determining square root for correctness.

    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;
    
        }
    
    
    }
    

    Regards Ravindra

    0 讨论(0)
  • 2020-11-28 08:32

    A single line can do the job I think.

    Math.pow(bigInt.doubleValue(), (1/n));
    
    0 讨论(0)
提交回复
热议问题