Большие целые числа, квадратный корень, Java и C: что делает эта строка?

Я провел некоторое исследование в поисках относительно быстрого алгоритма квадратного корня, который работает с большими целыми числами. Я нашел здесь пару рутин. Первый (ниже) был написан на C...

int isqrt(int n)
{
  int b = 0;

  while(n >= 0)
  {
    n = n - b;
    b = b + 1;
    n = n - b;
  }

  return b - 1;
}

... которые я нашел здесь: эффективный алгоритм целочисленного квадратного корня для ARM Thumb2

Я реализовал эту процедуру из-за ее простоты и эффективного использования буферного пространства. Однако, как это просто, производительность выходит за рамки неприемлемого. Он работает и дает правильный ответ, когда возвращает только b, а не b - 1.

Итак, в поисках лучшего алгоритма я наткнулся на следующий алгоритм, написанный на Java...

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

... который я нашел по этому вопросу: Как найти квадратный корень из Java BigInteger?

Я с радостью признаю, что я плохо разбираюсь в Java, поэтому вопрос, который у меня есть, заключается в том, что делает строка BigInteger y = div.add(x.divide(div)).shiftRight(1);?

Насколько я знаю, у меня есть возможное преобразование цикла в приведенный ниже фрагмент кода C, но я недостаточно знаю Java, чтобы быть уверенным.

while (1)
{
    x /= div;
    div += x;
    y = x >> 1;

    if (y == div || y == div2) return(y);
    div2 = div;
    div = y;
}

Это правильно?


person Daniel Rudy    schedule 19.07.2016    source источник
comment
BigInteger y = div.add(x.divide(div)).shiftRight(1); эквивалентен псевдокоду y = (div + x / div) / 2.   -  person Rudy Velthuis    schedule 19.07.2016


Ответы (1)


Да, это правильно.

Давайте переведем!

BigInteger y = div.add(x.divide(div)).shiftRight(1);
//becomes
long int y = (div + (x / div)) >> 1;
//this also applies to Java (you don't 'need' BigInteger or its fancy methods)

а в остальном довольно прямолинейно. Вы просто сравниваете, чтобы увидеть, равно ли y div или div2, и если это не так, перетасуйте значения и повторите попытку.

person Yidna    schedule 19.07.2016
comment
Это сработало. Спасибо. Глядя на представление C, это похоже на ньютоновский метод вычисления квадратного корня. Используя этот метод, он увеличился примерно с 92 с до 113 мкс. Большое улучшение. - person Daniel Rudy; 19.07.2016
comment
Я почти уверен, что является методом Ньютона. Вероятно, будет быстрее, если вы начнете с хорошей догадки вместо 1/div. В моем вычислении BigInteger sqrt() я сдвигаю div вправо на половину его битового размера в качестве первого приближения. Конечно, это имеет больше смысла с огромным BigInteger, чем с простым int. - person Rudy Velthuis; 19.07.2016