Python: «long int слишком большой для преобразования в число с плавающей запятой» при проверке (действительно больших) простых чисел

Я попытался реализовать первую is_prime-функцию из этого ответа: https://stackoverflow.com/a/17298131/6208280

# for large numbers, xrange will throw an error.
# OverflowError: Python int too large to convert to C long
# to get over this:

def mrange(start, stop, step):
    while start < stop:
        yield start
        start += step

# benchmarked on an old single-core system with 2GB RAM.

from math import sqrt

def is_prime(num):
    if num == 2:
        return True
    if (num < 2) or (num % 2 == 0):
        return False
    return all(num % i for i in mrange(3, int(sqrt(num)) + 1, 2))

Тем не менее, при тестировании больших чисел у меня возникла небольшая проблема.

Для действительно больших чисел я получаю ошибку переполнения: long int too large to convert to float

Я проверил макс для поплавка:

sys.float_info.max
1.7976931348623157e+308

для is_prime(10**308) все работает нормально ... но например с is_prime(10**309) будет эта ошибка переполнения (из-за макс. числа с плавающей запятой?).

Означает ли это, что 1.7976931348623157e + 308 является пределом для такого типа функции is_prime () или есть какое-либо решение для проверки более высоких чисел с помощью функции is_prime ()?

На мой взгляд, такие решения, как «использовать десятичную дробь», не решат проблему, потому что из-за отсутствия точности функция проверки простых чисел понадобится?

Это означает, что (sys.float_info.max - ϵ) ** 2 - это предел, поскольку вы звоните в sqrt(num). Существуют "алгоритмы извлечения квадратного корня из целых чисел", которые обходят стороной любое использование чисел с плавающей запятой, например, Вот этот.

jedwards 01.05.2018 22:02

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

Mark Dickinson 01.05.2018 22:17
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
2
359
1

Ответы 1

Если единственная причина, по которой вам нужны поплавки, - это возможность выполнять int(sqrt(num)), вы можете найти достаточно эффективную функцию intsqrt и использовать ее вместо нее. См. этот вопрос и сообщение в блоге, связанное с принятым ответом, для некоторых идей.


Или вы можете просто изменить свой код, чтобы он вообще не нуждался в sqrt. Например, вместо диапазона, который заканчивается на int(sqrt(num))+1, вы можете использовать takewhile, который проверяет lambda i: i*i <= num. Или, поскольку у вас уже есть эта функция mrange:

def sqrmrange(start, sqrstop):
    while start*start < sqrstop:
        yield start
        start += step

Или, если вам не нужно, чтобы он был однострочным, вы, вероятно, можете написать что-то менее абстрактное (и, возможно, более эффективное?), Чем любое из них. (Но на самом деле любой intsqrt вполне может быть быстрее, чем лучший из возможных тестов **2, потому что вам нужно выполнять intsqrt только один раз, в начале цикла, а не каждый раз в цикле.)


Или, если вы действительно хотите сохранить такую ​​структуру, вы можете просто использовать decimal. Ты говоришь:

In my mind such solutions as "use decimal" will not really solve the problem, because of the lack of precision a prime number checking function will need?

Но это не так. Использование float означает, что вы по своей сути ограничены 52 битами точности, что, если проблема возникнет задолго до того, как вы дойдете до переполнения. Использование decimal означает, что вы получите столько цифр точности, сколько вам нужно, и даже 30 цифр по умолчанию уже намного больше, чем 52 бита. Например:

>>> float(10**20) == float(10**20)+1
True
>>> Decimal(10**20) == Decimal(10**20)+1
False

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

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

Другие вопросы по теме