Как найти квадратный корень Java BigInteger? - PullRequest
52 голосов
/ 10 декабря 2010

Есть ли библиотека, которая найдет квадратный корень из BigInteger?Я хочу, чтобы он вычислялся в автономном режиме - только один раз, а не внутри какого-либо цикла.Так что даже вычислительно дорогое решение хорошо.

Я не хочу найти какой-то алгоритм и реализовать.Легкодоступное решение будет идеальным.

Ответы [ 18 ]

32 голосов
/ 29 мая 2013

Просто для удовольствия:

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;
    }
}
20 голосов
/ 15 августа 2012

Я не знаю ни одного библиотечного решения для вашего вопроса. Вам нужно будет импортировать решение внешней библиотеки откуда-то. То, что я дам вам ниже, не так сложно, как получить внешнюю библиотеку.

Вы можете создать собственное решение для внешней библиотеки в классе с помощью двух статических методов, как показано ниже, и добавить его в свою коллекцию внешних библиотек. Методы не обязательно должны быть методами экземпляра, поэтому они являются статическими, и для удобства вам не нужно создавать экземпляр класса для их использования. Норма для целочисленных квадратных корней - это значение пола (т. Е. Наибольшее целое число, меньшее или равное квадратному корню), поэтому вам может понадобиться только один статический метод, метод floor, в классе ниже для значения floor и можно выбрать игнорировать верхний предел (т. е. наименьшее целое число, большее или равное квадратному корню) версии метода. Прямо сейчас они находятся в пакете по умолчанию, но вы можете добавить инструкцию пакета, чтобы поместить их в любой пакет, который вам удобен.

Методы очень просты, и итерации сходятся к самому близкому целочисленному ответу очень, очень быстро. Они выдают исключение IllegalArgumentException, если вы пытаетесь дать им отрицательный аргумент. Вы можете изменить исключение на другое, но вы должны убедиться, что отрицательный аргумент генерирует какое-то исключение или, по крайней мере, не пытается вычислить. Целочисленные квадратные корни отрицательных чисел не существуют, поскольку мы не находимся в области мнимых чисел.

Они получены из очень хорошо известных простых итерационных алгоритмов целочисленного квадратного корня, которые веками использовались в ручных вычислениях. Он работает путем усреднения завышения и недооценки, чтобы приблизиться к лучшей оценке. Это может повторяться до тех пор, пока оценка не станет настолько близкой, насколько это необходимо.

Они основаны на y1 = ((x / y0) + y0) / 2, сходящемся к наибольшему целому числу yn, где yn * yn <= x. </p>

Это даст вам минимальное значение для квадратного корня BigInteger, y, x, где y * y <= x и (y + 1) * (y + 1)> x.

Адаптация может дать вам предельное значение для квадратного корня BigInteger, y, x, где y * y> = x и (y - 1) * (y - 1)

Оба метода были проверены и работают. Они здесь:

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
5 голосов
/ 13 сентября 2018

Странно, что никто не упоминал об этом раньше, но в java 9 у вас есть sqrt в BigInteger, так что вы можете просто использовать его так:

BigInteger nine = BigInteger.valueOf(9);
BigInteger three = nine.sqrt();

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

5 голосов
/ 10 декабря 2010

Я не могу проверить их точность, но при поиске в Google существует несколько решений для дома. Лучшим из них, казалось, был этот: http://www.merriampark.com/bigsqrt.htm

Также попробуйте проект Math Apache commons (как только Apache восстановится после бомбардировки после публикации в блоге JCP).

4 голосов
/ 14 января 2016

Как утверждает Jigar , Итерация Ньютона довольно проста для понимания и реализации.Я оставлю это на усмотрение других, решим, является ли это наиболее эффективным алгоритмом или нет для нахождения квадратного корня числа.

С помощью рекурсии это можно сделать всего за две строки.*

Где n - это число, которое мы хотим найти квадратный корень, а x0 - это число от предыдущего вызова, которое всегда будет равно 1, когда инициируется первый вызовиз другого метода.Поэтому желательно, чтобы вы дополнили это чем-то вроде этого;

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);
}
2 голосов
/ 23 марта 2016

Альтернативный подход, который довольно легкий. Для скорости ответ Мантоно, использующий метод Ньютона, может быть предпочтительным в некоторых случаях.

Вот мой подход ...

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 голосов
/ 10 декабря 2010

Для первоначального предположения я бы использовал Math.sqrt(bi.doubleValue()), а вы можете использовать уже предложенные ссылки, чтобы сделать ответ более точным.

2 голосов
/ 12 ноября 2014

Мне нужно было иметь квадратный корень для BigIntegers для реализации квадратичного сита.Я использовал некоторые из решений здесь, но пока самое быстрое и лучшее решение из библиотеки BigInteger Google Guava.

Документацию можно найти здесь .

1 голос
/ 24 января 2015

Упрощенный Джим ответ и улучшенная производительность.

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;
    }
}
1 голос
/ 06 августа 2017

Обновление (23 июля2018): этот метод не подходит для больших значений. Ниже опубликована другая методика, основанная на бинарном поиске.


Я изучал факторизацию и в итоге написал это.

package com.example.so.math;

import java.math.BigInteger;

/**
 * 
 * <p>/2434917/kak-naiti-kvadratnyi-koren-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;
    }


}

Подход можно визуализировать так:

Powers of Integers Moduluo - N

Надеюсь, это поможет!

...