квадратный корень из числа больше 10^2000 в Python 3

Я хотел бы вычислить квадратный корень из числа больше 10 ^ 2000 в Python. Если я буду рассматривать это число как обычное целое число, я всегда буду возвращать этот результат:

Traceback (most recent call last):
  File "...", line 3, in <module>
    print( q*(0.5)  )
OverflowError: int too large to convert to float

Как это исправить? Или существует ли возможность, кроме использования Python, для вычисления этого квадратного корня?


person cubeAD    schedule 17.12.2017    source источник
comment
Вы имеете в виду 10^2000 или 10**2000?   -  person RoadRunner    schedule 17.12.2017


Ответы (3)


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

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

import math

_1_50 = 1 << 50  # 2**50 == 1,125,899,906,842,624

def isqrt(x):
    """Return the integer part of the square root of x, even for very
    large integer values."""
    if x < 0:
        raise ValueError('square root not defined for negative numbers')
    if x < _1_50:
        return int(math.sqrt(x))  # use math's sqrt() for small parameters
    n = int(x)
    if n <= 1:
        return n  # handle sqrt(0)==0, sqrt(1)==1
    # Make a high initial estimate of the result (a little lower is slower!!!)
    r = 1 << ((n.bit_length() + 1) >> 1)
    while True:
        newr = (r + n // r) >> 1  # next estimate by Newton-Raphson
        if newr >= r:
            return r
        r = newr
person Rory Daulton    schedule 17.12.2017
comment
гарантированно возвращает правильную целую часть квадратного корня любого положительного целого числа — похоже, что это неверно для моего случая: isqrt(178533196125860586848256)=422531887702 != math.sqrt(178533196125860586848256)=422531887703. Мой калькулятор тоже дает 422531887703. Пробовал с Python 3.5.2 x64 - person Dmitry; 15.08.2019
comment
@Dmitry: Когда я проверяю ваш комментарий, я вижу, что мой isqrt верен, а другие расчеты неверны. Вы можете увидеть, что мой правильный, оценив 422531887702**2 <= 178533196125860586848256 < 422531887703**2 в Python. Это оценивается как True. Однако 422531887703**2 <= 178533196125860586848256 оценивается как False. Если другие методы говорят, что правильный квадратный корень равен 422531887703, то они ошибаются. Пожалуйста, проверьте сами. Если вы обнаружите реальную ошибку в моем коде, я хотел бы знать, поэтому, пожалуйста, сообщите мне. - person Rory Daulton; 16.08.2019
comment
@RoryDaulton спасибо за чек. Да, вы правы — ваша реализация дает точную целую часть, в то время как math.sqrt ведет себя очень странно с большими числами: math.sqrt(178533196125860602616209) == math.sqrt(178533196125860586848256) возвращает True! - person Dmitry; 16.08.2019
comment
Насколько я вижу, if n <= 1 всегда ложно из-за предыдущего if x < _1_50: return int(math.sqrt(x)). - person kyrill; 18.02.2020
comment
Я получаю существенное расхождение с этим: 1234592592962967901296297037045679133590224789902207663928489902170626521926687. Каждый большой метод SQTRT, который IR Try Returs 11111111122222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222226666687.. - person J. Win.; 22.10.2020
comment
@J.Win.: Мой isqrt возвращает число, отличное от того, которое вы показываете. Ваш номер заканчивается на 903, а мой isqrt возвращаемый номер заканчивается на 003. Остальные цифры совпадают. Мое тестирование показывает, что мой результат верен. Пожалуйста, перепроверьте полученные результаты и ваши тесты по результатам. - person Rory Daulton; 24.10.2020

Просто используйте десятичный модуль:

>>> from decimal import *
>>> Decimal(10**2000).sqrt()
Decimal('1.000000000000000000000000000E+1000')
>>> Decimal(10**200000).sqrt()
Decimal('1.000000000000000000000000000E+100000')
>>> Decimal(15**35315).sqrt()
Decimal('6.782765081358674922386659760E+20766')

Вы также можете использовать библиотеку gmpy2.

>>> import gmpy2
>>> n = gmpy2.mpz(99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999982920000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000726067)
>>> gmpy2.get_context().precision=2048
>>> x = gmpy2.sqrt(n)

Полезные ссылки:

  1. Десятичная система счисления — Документация по Python
person mrhallak    schedule 17.12.2017
comment
Оператор ^ обозначает XOR в Python. Вы должны использовать **. Вы можете видеть это и по своим ложным результатам. - person clemens; 17.12.2017
comment
В Java нет оператора возведения в степень. Квадратный корень из 10^2000 никогда не равен 44,83... - person clemens; 17.12.2017
comment
Если вы ищете целочисленный результат и собираетесь использовать gmpy2, самый простой и лучший способ — просто `gmpy2.isqrt(). - person casevh; 17.12.2017

При использовании sqrt из библиотеки math, прежде чем приступить к извлечению квадратного корня, он преобразует значение в число с плавающей запятой.

Если мы вручную попытаемся преобразовать 10**2000 в число с плавающей запятой, это также вызовет ошибку.

>>> float(10**2000)
---------------------------------------------------------------------------
OverflowError                             Traceback (most recent call last)
<ipython-input-14-6ac81f63106d> in <module>
----> 1 math.sqrt(10**2000)

OverflowError: int too large to convert to float

Если бы мы говорили о большом числе, но с квадратом, равным или меньшим 308, Модуль Decimal будет работать следующим образом.

>>> from decimal import Decimal
>>> Decimal(math.sqrt(10**308))
Decimal('10000000000000000369475456880582265409809179829842688451922778552150543659347219597216513109705408327446511753687232667314337003349573404171046192448274432')

Однако, поскольку число в квадрате намного больше, чем 308, в данном случае 2000, нужно было бы сделать следующее.

>>> from decimal import Decimal
>>> Decimal(10**2000).sqrt()
Decimal('1.000000000000000000000000000E+1000')

Давайте посмотрим на результат, если кто-то попытается преобразовать Decimal(10**2000) в число с плавающей запятой.

>>> float(Decimal(10**2000))
inf

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

person Gonçalo Peres 龚燿禄    schedule 21.01.2021