Integer квадратный корень в python

Существует ли целочисленный квадратный корень где-то в python или в стандартных библиотеках? Я хочу, чтобы он был точным (т.е. Возвращал целое число) и лаял, если нет решения.

В настоящий момент я свернул свой собственный наивный:

def isqrt(n):
    i = int(math.sqrt(n) + 0.5)
    if i**2 == n:
        return i
    raise ValueError('input was not a perfect square')

Но это уродливо, и я действительно не доверяю ему для больших целых чисел. Я мог бы перебирать квадраты и сдаваться, если бы я превысил ценность, но я предполагаю, что было бы слишком медленно делать что-то подобное. Также я предполагаю, что, вероятно, буду изобретать колесо, что-то вроде этого наверняка существует в python уже...

Ответ 1

Метод Ньютона отлично работает на целых числах:

def isqrt(n):
    x = n
    y = (x + 1) // 2
    while y < x:
        x = y
        y = (x + n // x) // 2
    return x

Это возвращает наибольшее целое число x, для которого x * x не превосходит n. Если вы хотите проверить, является ли результат точно квадратным корнем, просто выполните умножение, чтобы проверить, является ли n идеальным квадратом.

Я обсуждаю этот алгоритм и три других алгоритма вычисления квадратных корней, мой блог.

Ответ 2

Извините за очень поздний ответ; Я просто наткнулся на эту страницу. В случае, если кто-либо посещает эту страницу в будущем, модуль python gmpy2 предназначен для работы с очень большими входами и включает среди прочего целочисленную функцию с квадратным корнем.

Пример:

>>> import gmpy2
>>> gmpy2.isqrt((10**100+1)**2)
mpz(10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001L)
>>> gmpy2.isqrt((10**100+1)**2 - 1)
mpz(10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000L)

Конечно, все будет иметь тег "mpz", но mpz совместимы с int:

>>> gmpy2.mpz(3)*4
mpz(12)

>>> int(gmpy2.mpz(12))
12

См. мой другой ответ для обсуждения этой эффективности метода относительно некоторых других ответов на этот вопрос.

Загрузить: https://code.google.com/p/gmpy/

Ответ 3

Обновление: Python3.8 имеет функцию math.isqrt в стандартной библиотеке!

Я проверил здесь каждую (правильную) функцию как на маленьком (0… 2 22), так и на большом (2 50001) входах. В обоих случаях явными победителями являются gmpy2.isqrt, предложенный mathmandan на первом месте, затем Python 3.8s math.isqrt во втором, за которым следует рецепт ActiveState, связанный с NPE в третьем. Рецепт ActiveState имеет несколько разделов, которые можно заменить на сдвиги, что делает его немного быстрее (но все же отстает от собственных функций):

def isqrt(n):
    if n > 0:
        x = 1 << (n.bit_length() + 1 >> 1)
        while True:
            y = (x + n // x) >> 1
            if y >= x:
                return x
            x = y
    elif n == 0:
        return 0
    else:
        raise ValueError("square root not defined for negative numbers")

Результаты тестов:

(* Поскольку gmpy2.isqrt возвращает объект gmpy2.mpz, который ведет себя в основном, но не совсем как int, вам может потребоваться преобразовать его обратно в int для некоторых целей.)

Ответ 4

Здесь очень простая реализация:

def i_sqrt(n):
    i = n.bit_length() >> 1    # i = floor( (1 + floor(log_2(n))) / 2 )
    m = 1 << i    # m = 2^i
    #
    # Fact: (2^(i + 1))^2 > n, so m has at least as many bits 
    # as the floor of the square root of n.
    #
    # Proof: (2^(i+1))^2 = 2^(2i + 2) >= 2^(floor(log_2(n)) + 2)
    # >= 2^(ceil(log_2(n) + 1) >= 2^(log_2(n) + 1) > 2^(log_2(n)) = n. QED.
    #
    while m*m > n:
        m >>= 1
        i -= 1
    for k in xrange(i-1, -1, -1):
        x = m | (1 << k)
        if x*x <= n:
            m = x
    return m

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

Для достаточно больших значений n (скажем, около 10**6000 или около 20000 бит) это выглядит следующим образом:

Все эти подходы успешны на входах такого размера, но на моей машине эта функция занимает около 1,5 секунд, а @Nibot занимает около 0,9 секунды, @user448810 занимает около 19 секунд, а встроенный метод gmpy2 занимает меньше чем миллисекунда (!). Пример:

>>> import random
>>> import timeit
>>> import gmpy2
>>> r = random.getrandbits
>>> t = timeit.timeit
>>> t('i_sqrt(r(20000))', 'from __main__ import *', number = 5)/5. # This function
1.5102493192883117
>>> t('exact_sqrt(r(20000))', 'from __main__ import *', number = 5)/5. # Nibot
0.8952787937686366
>>> t('isqrt(r(20000))', 'from __main__ import *', number = 5)/5. # user448810
19.326695976676184
>>> t('gmpy2.isqrt(r(20000))', 'from __main__ import *', number = 5)/5. # gmpy2
0.0003599147067689046
>>> all(i_sqrt(n)==isqrt(n)==exact_sqrt(n)[0]==int(gmpy2.isqrt(n)) for n in (r(1500) for i in xrange(1500)))
True

Эта функция может быть легко обобщена, хотя это не совсем так приятно, потому что у меня нет такой же точной первоначальной предпосылки для m:

def i_root(num, root, report_exactness = True):
    i = num.bit_length() / root
    m = 1 << i
    while m ** root < num:
        m <<= 1
        i += 1
    while m ** root > num:
        m >>= 1
        i -= 1
    for k in xrange(i-1, -1, -1):
        x = m | (1 << k)
        if x ** root <= num:
            m = x
    if report_exactness:
        return m, m ** root == num
    return m

Однако обратите внимание, что gmpy2 также имеет метод i_root.

Фактически этот метод может быть адаптирован и применен к любой (неотрицательной, увеличивающейся) функции f, чтобы определить "целое число, обратное к f". Однако, чтобы выбрать эффективное начальное значение m, вы все равно хотите что-то узнать о f.

Редактировать: благодаря @Greggo, указав, что функция i_sqrt может быть переписана, чтобы избежать использования каких-либо умножений. Это дает впечатляющее повышение производительности!

def improved_i_sqrt(n):
    assert n >= 0
    if n == 0:
        return 0
    i = n.bit_length() >> 1    # i = floor( (1 + floor(log_2(n))) / 2 )
    m = 1 << i    # m = 2^i
    #
    # Fact: (2^(i + 1))^2 > n, so m has at least as many bits
    # as the floor of the square root of n.
    #
    # Proof: (2^(i+1))^2 = 2^(2i + 2) >= 2^(floor(log_2(n)) + 2)
    # >= 2^(ceil(log_2(n) + 1) >= 2^(log_2(n) + 1) > 2^(log_2(n)) = n. QED.
    #
    while (m << i) > n: # (m<<i) = m*(2^i) = m*m
        m >>= 1
        i -= 1
    d = n - (m << i) # d = n-m^2
    for k in xrange(i-1, -1, -1):
        j = 1 << k
        new_diff = d - (((m<<1) | j) << k) # n-(m+2^k)^2 = n-m^2-2*m*2^k-2^(2k)
        if new_diff >= 0:
            d = new_diff
            m |= j
    return m

Обратите внимание, что по конструкции бит k th m << 1 не установлен, поэтому побитовым или может использоваться для реализации добавления (m<<1) + (1<<k). В конечном итоге у меня есть (2*m*(2**k) + 2**(2*k)), записанный как (((m<<1) | (1<<k)) << k), поэтому он имеет три смены и один побитовый - или (за которым следует вычитание, чтобы получить new_diff). Может быть, есть еще более эффективный способ получить это? Несмотря на это, это намного лучше, чем умножение m*m! Сравните с выше:

>>> t('improved_i_sqrt(r(20000))', 'from __main__ import *', number = 5)/5.
0.10908999762373242
>>> all(improved_i_sqrt(n) == i_sqrt(n) for n in xrange(10**6))
True

Ответ 5

Долгосрочный алгоритм с квадратным корнем

Оказывается, существует алгоритм вычисления квадратных корней, который вы можете вычислить вручную, что-то вроде длинного разделения. Каждая итерация алгоритма производит ровно одну цифру полученного квадратного корня, потребляя две цифры числа, квадратный корень которого вы ищете. В то время как "длинная рука" версия алгоритма указана в десятичной системе, она работает в любой базе, причем бинарный файл является самым простым для реализации и, возможно, самым быстрым для выполнения (в зависимости от базового представления bignum).

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

На сайте "Dr. Math" есть две хорошие записи, которые объясняют алгоритм:

И вот реализация в Python:

def exact_sqrt(x):
    """Calculate the square root of an arbitrarily large integer. 

    The result of exact_sqrt(x) is a tuple (a, r) such that a**2 + r = x, where
    a is the largest integer such that a**2 <= x, and r is the "remainder".  If
    x is a perfect square, then r will be zero.

    The algorithm used is the "long-hand square root" algorithm, as described at
    http://mathforum.org/library/drmath/view/52656.html

    Tobin Fricke 2014-04-23
    Max Planck Institute for Gravitational Physics
    Hannover, Germany
    """

    N = 0   # Problem so far
    a = 0   # Solution so far

    # We'll process the number two bits at a time, starting at the MSB
    L = x.bit_length()
    L += (L % 2)          # Round up to the next even number

    for i in xrange(L, -1, -1):

        # Get the next group of two bits
        n = (x >> (2*i)) & 0b11

        # Check whether we can reduce the remainder
        if ((N - a*a) << 2) + n >= (a<<2) + 1:
            b = 1
        else:
            b = 0

        a = (a << 1) | b   # Concatenate the next bit of the solution
        N = (N << 2) | n   # Concatenate the next bit of the problem

    return (a, N-a*a)

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

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

Source repo: https://gist.github.com/tobin/11233492

Ответ 6

Один из вариантов - использовать модуль decimal и сделать это в достаточно точных поплавках:

import decimal

def isqrt(n):
    nd = decimal.Decimal(n)
    with decimal.localcontext() as ctx:
        ctx.prec = n.bit_length()
        i = int(nd.sqrt())
    if i**2 != n:
        raise ValueError('input was not a perfect square')
    return i

который, как я думаю, должен работать:

>>> isqrt(1)
1
>>> isqrt(7**14) == 7**7
True
>>> isqrt(11**1000) == 11**500
True
>>> isqrt(11**1000+1)
Traceback (most recent call last):
  File "<ipython-input-121-e80953fb4d8e>", line 1, in <module>
    isqrt(11**1000+1)
  File "<ipython-input-100-dd91f704e2bd>", line 10, in isqrt
    raise ValueError('input was not a perfect square')
ValueError: input was not a perfect square

Ответ 7

Похоже, вы можете проверить вот так:

if int(math.sqrt(n))**2 == n:
    print n, 'is a perfect square'

Update:

Как вы указали выше, при больших значениях n это не работает. Для тех, кто выглядит многообещающим, что является адаптацией примера кода C Мартином Гаем @UKC, июнь 1985 года, для относительно простого вида двоичных чисел с цифрами по цифре, упомянутых в статье Википедии Методы вычисления квадратных корней:

from math import ceil, log

def isqrt(n):
    res = 0
    bit = 4**int(ceil(log(n, 4))) if n else 0  # smallest power of 4 >= the argument
    while bit:
        if n >= res + bit:
            n -= res + bit
            res = (res >> 1) + bit
        else:
            res >>= 1
        bit >>= 2
    return res

if __name__ == '__main__':
    from math import sqrt  # for comparison purposes

    for i in range(17)+[2**53, (10**100+1)**2]:
        is_perfect_sq = isqrt(i)**2 == i
        print '{:21,d}:  math.sqrt={:12,.7G}, isqrt={:10,d} {}'.format(
            i, sqrt(i), isqrt(i), '(perfect square)' if is_perfect_sq else '')

Вывод:

                    0:  math.sqrt=           0, isqrt=         0 (perfect square)
                    1:  math.sqrt=           1, isqrt=         1 (perfect square)
                    2:  math.sqrt=    1.414214, isqrt=         1
                    3:  math.sqrt=    1.732051, isqrt=         1
                    4:  math.sqrt=           2, isqrt=         2 (perfect square)
                    5:  math.sqrt=    2.236068, isqrt=         2
                    6:  math.sqrt=     2.44949, isqrt=         2
                    7:  math.sqrt=    2.645751, isqrt=         2
                    8:  math.sqrt=    2.828427, isqrt=         2
                    9:  math.sqrt=           3, isqrt=         3 (perfect square)
                   10:  math.sqrt=    3.162278, isqrt=         3
                   11:  math.sqrt=    3.316625, isqrt=         3
                   12:  math.sqrt=    3.464102, isqrt=         3
                   13:  math.sqrt=    3.605551, isqrt=         3
                   14:  math.sqrt=    3.741657, isqrt=         3
                   15:  math.sqrt=    3.872983, isqrt=         3
                   16:  math.sqrt=           4, isqrt=         4 (perfect square)
9,007,199,254,740,992:  math.sqrt=9.490627E+07, isqrt=94,906,265
100,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,020,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,001:  math.sqrt=      1E+100, isqrt=10,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,001 (perfect square)

Ответ 8

Ваша функция не работает для больших входов:

In [26]: isqrt((10**100+1)**2)

ValueError: input was not a perfect square

Существует рецепт на сайте ActiveState, который, мы надеемся, будет более надежным, поскольку он использует только целые математические выражения. Он основан на более раннем вопросе StackOverflow: Написание собственной функции квадратного корня

Ответ 9

Я нашел эту ветку несколько дней назад и переписал решение nibot, и сократив количество итераций пополам и выполняя некоторые другие незначительные улучшения производительности, я смог улучшить производительность в 2,4 раза:

def isqrt(n):
    a = 0 # a is the current answer.
    r = 0 # r is the current remainder.
    for s in reversed(range(0, n.bit_length(), 2)): # Shift n by s bits.
        t = n >> s & 3 # t is the two next most significant bits of n.
        r = r << 2 | t # Increase the remainder as if no new bit is set.
        c = a << 2 | 1 # c is an intermediate value used for comparison.
        b = r >= c     # b is the next bit in the remainder.
        if b: 
            r -= c     # b has been set, so reduce the remainder.
        a = a << 1 | b # Update the answer to include b.
    return (a, r)

Вот результаты от timeit:

>>> timeit('isqrt(12345678901234567890)', setup='from __main__ import isqrt')
8.862877120962366

Затем для сравнения я применил наиболее часто используемый алгоритм с квадратным корнем: метод Ньютона. Это определение гораздо более компактно.

def isqrt(n):
    x, y = n, n >> 1
    while x > y:
        x, y = y, (y + n//y) >> 1
    return (x, n - x*x)

Оказывается, что даже оптимизированная версия длинномерных квадратных корней медленнее метода Ньютона, занимая примерно в 1,5 раза.

>>> timeit('isqrt(12345678901234567890)', setup='from __main__ import isqrt')
5.74083631898975

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

Изменить: я исправил ошибку в методе Ньютона выше. На моей машине он работает на ~ 10% быстрее, чем user448810.

Ответ 10

Начиная с Python 3.8, стандартная библиотека представляет функцию isqrt как часть math модуля, который возвращает пол точного квадратного корня из n (math.isqrt(25) # 5 math.isqrt(26) # 5).

В нашем случае это позволяет нам сравнивать квадрат n квадратного корня с n:

from math import isqrt
# n = 25
x if (x := isqrt(n)) ** 2 == n else ValueError('input was not a perfect square')
# 5

Также обратите внимание на удобное использование выражений присваивания (PEP 572) (:= оператор), также введенных в Python 3.8, что позволяет захватывать isqrt в оценке состояния (x := isqrt(n)), чтобы повторно использовать его как возвращенное значение без его повторной оценки или использования промежуточной переменной.

Ответ 11

Поплавки не могут быть точно представлены на компьютерах. Вы можете проверить желаемую настройку близости с небольшим значением в пределах точности плавающих python.

def isqrt(n):
    epsilon = .00000000001
    i = int(n**.5 + 0.5)
    if abs(i**2 - n) < epsilon:
        return i
    raise ValueError('input was not a perfect square')

Ответ 12

Я сравнил различные методы, приведенные здесь, с циклом:

for i in range (1000000): # 700 msec
    r=int(123456781234567**0.5+0.5)
    if r**2==123456781234567:rr=r
    else:rr=-1

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

15241576832799734552675677489**0.5 = 123456781234567.0

Ответ 13

Попробуйте это условие (без дополнительных вычислений):

def isqrt(n):
  i = math.sqrt(n)
  if i != int(i):
    raise ValueError('input was not a perfect square')  
  return i

Если вам нужно вернуть int (не a float с конечным нолем), то дважды назначьте вторую переменную или вычислите int(i).