Нестабильные результаты из факторинговой функции Python
def test_prime(n):
q = True
for p in range(2,n): #Only need to check up to rootn for primes and n/2 for factors
if int(n%p) is 0:
q = False
print(p, 'and', int(n/p), 'are factors of ', n)
if q:
print(n, 'IS a prime number!')
else:
print(n, 'IS NOT a prime number')
Я только начал играть с Python и собираю кусочки, чтобы скоротать время. Я играл с тестированием для простых чисел, и у меня была идея показать факторы для не простых чисел. Функция, которую я собрал выше, кажется, работает достаточно хорошо, за исключением того, что она дает противоречивые результаты.
Например, если я установлю n = 65432, я получу...
2 and 32716 are factors of 65432
4 and 16358 are factors of 65432
8 and 8179 are factors of 65432
8179 and 8 are factors of 65432
16358 and 4 are factors of 65432
32716 and 2 are factors of 65432
65432 IS NOT a prime number
что это то, что я ожидал. Но если я установлю n = 659306, я получу...
2 and 329653 are factors of 659306
71 and 9286 are factors of 659306
142 and 4643 are factors of 659306
4643 and 142 are factors of 659306
9286 and 71 are factors of 659306
659306 IS NOT a prime number
который отличается, потому что он не включает фактор 329653 в самом конце. Это не проблема, так как все факторы отображаются где-то, но меня раздражает, что я не знаю, ПОЧЕМУ это происходит для некоторых чисел!
Просто чтобы показать вам, что я не полный придурок, я решил, что это происходит только с целочисленными значениями длиной более 5 символов. Может кто-нибудь подскажите, пожалуйста, почему выходы разные в этих двух случаях?
2 ответа
Ты хочешь n % p == 0
не n % p is 0
, is
проверяет идентичность, а не равенство, и не каждый 0 совпадает с любым другим 0.
>>> 659306 % 329653
0
>>> (659306 % 329653) == 0
True
>>> (659306 % 329653) is 0
False
>>> id(0)
136748976
>>> id(659306 % 329653)
3070888160
id
там в основном соответствует месту в памяти.
Подумайте об этом так: если у вас есть луни, а у меня луни, то они равны друг другу по значению (1 == 1), но это не тот же объект (моя монета в один доллар не так же, как ваша монета в один доллар.) Мы могли бы использовать одну и ту же монету, но это не обязательно.
[PS: Вы можете использовать n//p
для целочисленного деления вместо int(n/p)
.]
То, что происходит за кулисами, немного сложно. Мои комментарии относятся конкретно к CPython
, Другие реализации, такие как PyPy, Jython, IronPython и т. Д., Будут вести себя по-другому.
Чтобы уменьшить использование памяти и повысить производительность, CPython кэширует диапазон маленьких целых чисел и пытается вернуть ссылку на эти объекты вместо создания другого целочисленного объекта с тем же значением. Когда вы сравниваете числа с is
вы на самом деле проверяете, возвращал ли CPython ссылку на тот же кешированный объект. Но иногда CPython не проверяет, является ли значение одним из кэшированных целых чисел. Как такое могло произойти?
Я объясню CPython 3, так как он немного проще, чем CPython 2. int
Видимый в CPython тип на самом деле называется PyLong
внутренне переводчику. PyLong
хранит целое число в виде массива digits
где каждая цифра находится между 0 и 2**15-1
(32-битные системы) или 0 и 2**30-1
(64-битные системы). Массив увеличивается в размере по мере увеличения чисел; это позволяет эффективно неограниченные целые числа. При расчете %
CPython проверяет, является ли второй аргумент одним digit
долго. Если это так, он вызывает функцию C (divrem1), которая возвращает digit
в результате. Следующий, PyLong_FromLong
вызывается для преобразования значения, которое соответствует длине C (т. е. возвращаемое значение divrem
) в PyLong. PyLong_FromLong
проверяет, находится ли аргумент в диапазоне кэшированных целых чисел, и, если возможно, вернет ссылку на кэшированное целое число.
Если второй аргумент больше одного digit
long вызывается другая функция C (x_divrem). x_divrem
использует алгоритм деления произвольной точности общего назначения для вычисления остатка. поскольку x_divrem
создает PyLong
чтобы сохранить остаток во время вычисления, нет никакого преимущества, полученного, избегая создания другого двойного целого числа; это уже существует. Для вычислений со случайными большими числами остаток редко будет равен одному из кешированных целых чисел, поэтому не стоит тратить время на проверку.
Существуют и другие способы создания дублированных копий кэшированных целых чисел. Я только что проанализировал один из вопроса.
И вот почему вы не используете is
для проверки числового равенства.....