Подтвердить что ты не робот

Вычислить квадратный корень из BigInteger (System.Numerics.BigInteger)

.NET 4.0 предоставляет тип System.Numerics.BigInteger для произвольно больших целых чисел. Мне нужно вычислить квадратный корень (или разумное приближение - например, целочисленный квадратный корень) a BigInteger. Так что мне не нужно переопределять колесо, у кого-нибудь есть хороший метод расширения для этого?

4b9b3361

Ответ 1

Проверьте, не имеет ли BigInteger идеальный квадрат имеет код для вычисления целочисленного квадратного корня Java BigInteger. Здесь он переводится на С# в качестве метода расширения.

    public static BigInteger Sqrt(this BigInteger n)
    {
        if (n == 0) return 0;
        if (n > 0)
        {
            int bitLength = Convert.ToInt32(Math.Ceiling(BigInteger.Log(n, 2)));
            BigInteger root = BigInteger.One << (bitLength / 2);

            while (!isSqrt(n, root))
            {
                root += n / root;
                root /= 2;
            }

            return root;
        }

        throw new ArithmeticException("NaN");
    }

    private static Boolean isSqrt(BigInteger n, BigInteger root)
    {
        BigInteger lowerBound = root*root;
        BigInteger upperBound = (root + 1)*(root + 1);

        return (n >= lowerBound && n < upperBound);
    }

Неофициальное тестирование показывает, что это примерно на 75X медленнее, чем Math.Sqrt, для небольших целых чисел. Профилировщик VS указывает на умножения в isSqrt как горячие точки.

Ответ 2

Я не уверен, что метод Ньютона - лучший способ вычислить квадратные корни бигма, потому что он включает в себя деления, которые медленны для бонусов. Вы можете использовать метод CORDIC, который использует только сложение и сдвиги (показано здесь для unsigned ints)

static uint isqrt(uint x)
{
    int b=15; // this is the next bit we try 
    uint r=0; // r will contain the result
    uint r2=0; // here we maintain r squared
    while(b>=0) 
    {
        uint sr2=r2;
        uint sr=r;
                    // compute (r+(1<<b))**2, we have r**2 already.
        r2+=(uint)((r<<(1+b))+(1<<(b+b)));      
        r+=(uint)(1<<b);
        if (r2>x) 
        {
            r=sr;
            r2=sr2;
        }
        b--;
    }
    return r;
}

Там аналогичный метод, который использует только добавление и сдвиги, называемый "квадрат квадрата дийкстра", здесь объясняется здесь:

Ответ 3

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

Ответ 4

Вы можете преобразовать это в язык и типы переменных по вашему выбору. Вот усеченный квадратный корень в JavaScript (самый свежий для меня), который использует преимущество 1 + 3 + 5... +nth нечетное число = n ^ 2. Все переменные являются целыми числами, и это только добавляет и вычитает.

var truncSqrt = function(n) {
  var oddNumber = 1;
  var result = 0;
  while (n >= oddNumber) {
    n -= oddNumber;
    oddNumber += 2;
    result++;
  }
  return result;
};

Ответ 5

Короткий ответ: (но остерегайтесь, см. ниже для более подробной информации)

Math.Pow(Math.E, BigInteger.Log(pd) / 2)

Где pd представляет BigInteger, на котором вы хотите выполнить операцию с квадратным корнем.

Длинный ответ и объяснение:

Еще один способ понять эту проблему - понять, как работают квадратные корни и журналы.

Если у вас есть уравнение 5^x = 25, для решения для x мы должны использовать журналы. В этом примере я буду использовать естественные журналы (также возможны журналы в других базах, но естественный журнал - это простой способ).

5^x = 25

Переписывая, мы имеем:

x(ln 5) = ln 25

Чтобы выделить x, имеем

x = ln 25 / ln 5

Мы видим, что это приводит к x = 2. Но так как мы уже знаем x (x = 2, в 5 ^ 2), изменим то, что мы не знаем, и напишем новое уравнение и решим для нового неизвестного. Пусть x - результат операции квадратного корня. Это дает нам

2 = ln 25 / ln x

Переписывая для выделения x, имеем

ln x = (ln 25) / 2

Чтобы удалить журнал, мы теперь используем специальное тождество естественного логарифма и специального числа e. В частности, e^ln x = x. Переписывание уравнения теперь дает нам

e^ln x = e^((ln 25) / 2)

Упрощая левую сторону, имеем

x = e^((ln 25) / 2)

где x будет квадратным корнем из 25. Вы также можете распространить эту идею на любой корень или число, а общая формула для y-го корня x становится e^((ln x) / y).

Теперь, чтобы применить это специально к С#, BigIntegers и конкретно к этому вопросу, мы просто реализуем формулу. ПРЕДУПРЕЖДЕНИЕ: Хотя математика верна, существуют конечные пределы. Этот метод приведет вас только по соседству с большим неизвестным диапазоном (в зависимости от того, насколько большой из числа вы работаете). Возможно, именно поэтому Microsoft не применяла такой метод.

// A sample generated public key modulus
var pd = BigInteger.Parse("101017638707436133903821306341466727228541580658758890103412581005475252078199915929932968020619524277851873319243238741901729414629681623307196829081607677830881341203504364437688722228526603134919021724454060938836833023076773093013126674662502999661052433082827512395099052335602854935571690613335742455727");
var sqrt = Math.Pow(Math.E, BigInteger.Log(pd) / 2);

Console.WriteLine(sqrt);

ПРИМЕЧАНИЕ. Метод BigInteger.Log() возвращает double, поэтому возникают две проблемы. 1) Число неточно, и 2) существует верхний предел для того, что Log() может обрабатывать для входов BigInteger. Чтобы проверить верхний предел, мы можем посмотреть обычную форму для естественного логарифма, то есть ln x = y. Другими словами, e^y = x. Так как double является возвращаемым типом BigInteger.Log(), то разумно предположить, что наибольший BigInteger затем будет поднят до double.MaxValue. На моем компьютере это будет e^1.79769313486232E+308. Неточность не обрабатывается. Кто-то хочет реализовать BigDecimal и обновить BigInteger.Log()?

Потребитель, будьте осторожны, но он доставит вас по соседству, и возведение в квадрат результата приведет к числу, близкому к исходному входу, до стольких цифр и не столь точно, как ответ RedGreenCode. Счастливое (квадратное) укоренение!;)