Как я могу проверить, является ли число идеальным квадратом?
Скорость не вызывает беспокойства, пока что просто работает.
Как я могу проверить, является ли число идеальным квадратом?
Скорость не вызывает беспокойства, пока что просто работает.
Проблема с использованием любого вычисления с плавающей запятой (math.sqrt(x)
или x**0.5
) заключается в том, что вы не можете быть уверены в ее точности (для достаточно больших целых чисел x
это не будет, и может даже переполнение). К счастью (если вы не спешите;-) существует множество простых целочисленных подходов, таких как следующее...:
def is_square(apositiveint):
x = apositiveint // 2
seen = set([x])
while x * x != apositiveint:
x = (x + (apositiveint // x)) // 2
if x in seen: return False
seen.add(x)
return True
for i in range(110, 130):
print i, is_square(i)
Подсказка: она основана на "вавилонском алгоритме" для квадратного корня, см. wikipedia. Он работает для любого положительного числа, для которого у вас достаточно памяти для вычисления, чтобы перейти к завершению; -).
Изменить: посмотрим пример...
x = 12345678987654321234567 ** 2
for i in range(x, x+2):
print i, is_square(i)
это печатает, по желанию (и в разумные сроки тоже; -):
152415789666209426002111556165263283035677489 True
152415789666209426002111556165263283035677490 False
Пожалуйста, прежде чем предлагать решения на основе промежуточных результатов с плавающей запятой, убедитесь, что они работают правильно на этом простом примере - это не , что сложно (вам просто нужно несколько дополнительных проверок в случае, если sqrt вычислено немного), просто берет немного заботы.
И затем попробуйте с x**7
и найдите умный способ обойти проблему, которую вы получите,
OverflowError: long int too large to convert to float
вам нужно будет становиться все более и более умными, поскольку числа продолжают расти, конечно.
Если бы я спешил, я бы использовал gmpy - но тогда я явно предвзято; -.)
>>> import gmpy
>>> gmpy.is_square(x**7)
1
>>> gmpy.is_square(x**7 + 1)
0
Да, я знаю, это так просто, что это похоже на обман (немного то, что я чувствую к Python в целом;-) - никакой умности вообще, просто совершенная прямота и простота (и, в случае gmpy, чистая скорость; -)...
Используйте метод Ньютона, чтобы быстро найти ближайший целочисленный квадратный корень, затем возвести его в квадрат и посмотреть, является ли он вашим числом. Смотри isqrt.
Python ≥ 3.8 имеет math.isqrt
. Если вы используете более старую версию Python, ищите реализацию " def isqrt(n)
" здесь.
import math
def is_square(i: int) -> bool:
return i == math.isqrt(i) ** 2
Поскольку вы никогда не можете зависеть от точных сравнений при работе с вычислениями с плавающей запятой (такими как эти способы вычисления квадратного корня), менее подверженная ошибкам реализация будет
import math
def is_square(integer):
root = math.sqrt(integer)
return integer == int(root + 0.5) ** 2
Представьте, что integer
равно 9
. math.sqrt(9)
может быть 3.0
, но это может быть что-то вроде 2.99999
или 3.00001
, так что квадрат результата сразу не надежен. Зная, что int
принимает значение floor, сначала увеличивая значение float на 0.5
означает, что мы получим искомое значение, если находимся в диапазоне, где float
прежнему имеет достаточно точное разрешение для представления чисел, близких к тому, для которого Мы ищем.
import math
def is_square(n):
sqrt = math.sqrt(n)
return (sqrt - int(sqrt)) == 0
Идеальный квадрат - это число, которое можно выразить как произведение двух равных целых чисел. math.sqrt(number)
возвращает math.sqrt(number)
с float
. int(math.sqrt(number))
результат к int
.
Если квадратный корень является целым числом, например 3, например, то math.sqrt(number) - int(math.sqrt(number))
будет 0, а оператор if
будет False
. Если квадратный корень был действительным числом, таким как 3,2, то он будет True
и выведите "это не идеальный квадрат".
Сбой для большого неквадрата, например 152415789666209426002111556165263283035677490.
Если вам интересно, у меня есть чисто математический ответ на аналогичный вопрос в math stackexchange, "Обнаружение совершенных квадратов быстрее чем путем извлечения квадратного корня" .
Моя собственная реализация isSquare (n) может быть не лучшей, но мне она нравится. В течение нескольких месяцев я изучал математическую теорию, цифровые вычисления и программирование на питоне, сравнивая себя с другими участниками и т.д., Чтобы действительно щелкнуть по этому методу. Мне нравится его простота и эффективность. Я не видел лучше. Расскажите мне, что вы думаете.
def isSquare(n):
## Trivial checks
if type(n) != int: ## integer
return False
if n < 0: ## positivity
return False
if n == 0: ## 0 pass
return True
## Reduction by powers of 4 with bit-logic
while n&3 == 0:
n=n>>2
## Simple bit-logic test. All perfect squares, in binary,
## end in 001, when powers of 4 are factored out.
if n&7 != 1:
return False
if n==1:
return True ## is power of 4, or even power of 2
## Simple modulo equivalency test
c = n%10
if c in {3, 7}:
return False ## Not 1,4,5,6,9 in mod 10
if n % 7 in {3, 5, 6}:
return False ## Not 1,2,4 mod 7
if n % 9 in {2,3,5,6,8}:
return False
if n % 13 in {2,5,6,7,8,11}:
return False
## Other patterns
if c == 5: ## if it ends in a 5
if (n//10)%10 != 2:
return False ## then it must end in 25
if (n//100)%10 not in {0,2,6}:
return False ## and in 025, 225, or 625
if (n//100)%10 == 6:
if (n//1000)%10 not in {0,5}:
return False ## that is, 0625 or 5625
else:
if (n//10)%4 != 0:
return False ## (4k)*10 + (1,9)
## Babylonian Algorithm. Finding the integer square root.
## Root extraction.
s = (len(str(n))-1) // 2
x = (10**s) * 4
A = {x, n}
while x * x != n:
x = (x + (n // x)) >> 1
if x in A:
return False
A.add(x)
return True
Довольно прямо. Сначала он проверяет, что у нас есть целое число, и положительное. В противном случае нет смысла. Он позволяет 0 проскальзывать через True (необходимый или следующий блок - бесконечный цикл).
Следующий блок кода систематически удаляет полномочия 4 в очень быстром под-алгоритме с использованием операций сдвига бит и бит-логики. В конечном счете мы не находим isSquare нашего исходного n, но k < n, который был уменьшен степенями 4, если это возможно. Это уменьшает размер числа, с которым мы работаем, и действительно ускоряет вавилонский метод, но также ускоряет другие проверки.
Третий блок кода выполняет простой логический бит-логический тест. Наименее значимые три цифры в двоичном виде любого идеального квадрата - 001. Всегда. Сохраните для начальных нулей в результате полномочий 4, во всяком случае, которые уже учтены. Если он не прошел тест, вы сразу же узнаете, что это не квадрат. Если он пройдет, вы не можете быть уверены.
Кроме того, если в итоге мы получим значение 1 для тестового значения, тогда изначальный номер теста будет иметь значение 4, включая, возможно, 1 себя.
Как и третий блок, четвертый проверяет значение one-place в десятичном значении с помощью простого оператора модуля и стремится уловить значения, проскальзывающие предыдущий тест. Также модем 7, mod 8, mod 9 и тест мод 13.
Пятый блок кода проверяет некоторые из хорошо известных квадратов. Числам, заканчивающимся на 1 или 9, предшествует кратное четыре. И числа, заканчивающиеся на 5, должны заканчиваться в 5625, 0625, 225 или 025. Я включил других, но понял, что они избыточны или никогда не используются.
Наконец, шестой блок кода очень похож на то, что главный ответчик - Алекс Мартелли - отвечает. В основном находит квадратный корень, используя древний вавилонский алгоритм, но ограничивая его целыми значениями при игнорировании с плавающей запятой. Выполнено как для скорости, так и для увеличения значений измеряемых значений. Я использовал набор вместо списков, потому что он занимает гораздо меньше времени, я использовал бит-сдвиги вместо деления на два, и я решительно выбрал начальное начальное значение гораздо эффективнее.
Кстати, я тестировал Alex Martelli рекомендованный номер теста, а также несколько чисел на несколько порядков больше, например:x=1000199838770766116385386300483414671297203029840113913153824086810909168246772838680374612768821282446322068401699727842499994541063844393713189701844134801239504543830737724442006577672181059194558045164589783791764790043104263404683317158624270845302200548606715007310112016456397357027095564872551184907513312382763025454118825703090010401842892088063527451562032322039937924274426211671442740679624285180817682659081248396873230975882215128049713559849427311798959652681930663843994067353808298002406164092996533923220683447265882968239141724624870704231013642255563984374257471112743917655991279898690480703935007493906644744151022265929975993911186879561257100479593516979735117799410600147341193819147290056586421994333004992422258618475766549646258761885662783430625 ** 2
for i in range(x, x+2):
print(i, isSquare(i))
напечатаны следующие результаты:
1000399717477066534083185452789672211951514938424998708930175541558932213310056978758103599452364409903384901149641614494249195605016959576235097480592396214296565598519295693079257885246632306201885850365687426564365813280963724310434494316592041592681626416195491751015907716210235352495422858432792668507052756279908951163972960239286719854867504108121432187033786444937064356645218196398775923710931242852937602515835035177768967470757847368349565128635934683294155947532322786360581473152034468071184081729335560769488880138928479829695277968766082973795720937033019047838250608170693879209655321034310764422462828792636246742456408134706264621790736361118589122797268261542115823201538743148116654378511916000714911467547209475246784887830649309238110794938892491396597873160778553131774466638923135932135417900066903068192088883207721545109720968467560224268563643820599665232314256575428214983451466488658896488012211237139254674708538347237589290497713613898546363590044902791724541048198769085430459186735166233549186115282574626012296888817453914112423361525305960060329430234696000121420787598967383958525670258016851764034555105019265380321048686563527396844220047826436035333266263375049097675787975100014823583097518824871586828195368306649956481108708929669583308777347960115138098217676704862934389659753628861667169905594181756523762369645897154232744410732552956489694024357481100742138381514396851789639339362228442689184910464071202445106084939268067445115601375050153663645294106475257440167535462278022649865332161044187890625 True
1000399717477066534083185452789672211951514938424998708930175541558932213310056978758103599452364409903384901149641614494249195605016959576235097480592396214296565598519295693079257885246632306201885850365687426564365813280963724310434494316592041592681626416195491751015907716210235352495422858432792668507052756279908951163972960239286719854867504108121432187033786444937064356645218196398775923710931242852937602515835035177768967470757847368349565128635934683294155947532322786360581473152034468071184081729335560769488880138928479829695277968766082973795720937033019047838250608170693879209655321034310764422462828792636246742456408134706264621790736361118589122797268261542115823201538743148116654378511916000714911467547209475246784887830649309238110794938892491396597873160778553131774466638923135932135417900066903068192088883207721545109720968467560224268563643820599665232314256575428214983451466488658896488012211237139254674708538347237589290497713613898546363590044902791724541048198769085430459186735166233549186115282574626012296888817453914112423361525305960060329430234696000121420787598967383958525670258016851764034555105019265380321048686563527396844220047826436035333266263375049097675787975100014823583097518824871586828195368306649956481108708929669583308777347960115138098217676704862934389659753628861667169905594181756523762369645897154232744410732552956489694024357481100742138381514396851789639339362228442689184910464071202445106084939268067445115601375050153663645294106475257440167535462278022649865332161044187890626 False
И это произошло через 0,33 секунды.
По моему мнению, мой алгоритм работает так же, как и Alex Martelli, со всеми его преимуществами, но имеет дополнительное преимущество высокоэффективных простейших тестов, которые экономят много времени, не говоря уже об уменьшении размера тестовых номеров по степеням 4, что повышает скорость, эффективность, точность и размер проверяемых чисел. Вероятно, особенно верно в реализациях, отличных от Python.
Примерно 99% всех целых чисел отклоняются как не-Квадрат до того, как извлечение корня Вавилона даже реализовано, а в 2/3 время, когда вавилонянин отклонил целое число. И хотя эти тесты не ускоряют процесс, который значительно, сокращение всех тестовых чисел до нечетного путем деления всех мощностей 4 действительно ускоряет вавилонский тест.
Я проверил время сравнения. Я тестировал все целые числа от 1 до 10 миллионов последовательно. Используя только вавилонский метод сам по себе (с моей специально подобранной первоначальной догадкой), моя поверхность 3 заняла в среднем 165 секунд (со 100% -ной точностью). Используя только логические тесты в моем алгоритме (за исключением вавилонского), он занял 127 секунд, он отклонил 99% всех целых чисел как не-квадрат, не ошибочно отклонив любые совершенные квадраты. Из тех целых чисел, которые прошли, только 3% были идеальными квадратами (гораздо более высокая плотность). Используя полный алгоритм выше, который использует как логические тесты, так и извлечение корня вавилонов, мы получаем 100% -ную точность и завершение теста всего за 14 секунд. Первые 100 миллионов целых чисел занимают примерно 2 минуты 45 секунд для тестирования.
EDIT: Я смог сократить время. Теперь я могу проверить целые числа от 0 до 100 миллионов за 1 минуту 40 секунд. Много времени тратится впустую, проверяя тип данных и положительность. Устраните первые две проверки, и я сократил эксперимент на минуту. Нужно предположить, что пользователь достаточно умен, чтобы знать, что негативы и поплавки не идеальные квадраты.
Я только что опубликовал небольшую вариацию на некоторых из приведенных выше примеров в другом потоке (Поиск идеальных квадратов) и подумал, что я бы включил небольшую вариацию того, что я здесь написал (используя nsqrt в качестве временной переменной), на случай, если это будет интересно/использовать:
import math
def is_square(n):
if not (isinstance(n, int) and (n >= 0)):
return False
else:
nsqrt = math.sqrt(n)
return nsqrt == math.trunc(nsqrt)
Это неверно для большого квадрата, например 152415789666209426002111556165263283035677490.
Это можно решить, используя модуль decimal
, чтобы получить произвольные квадратные корни и легко проверить "точность":
import math
from decimal import localcontext, Context, Inexact
def is_perfect_square(x):
# If you want to allow negative squares, then set x = abs(x) instead
if x < 0:
return False
# Create localized, default context so flags and traps unset
with localcontext(Context()) as ctx:
# Set a precision sufficient to represent x exactly; `x or 1` avoids
# math domain error for log10 when x is 0
ctx.prec = math.ceil(math.log10(x or 1)) + 1 # Wrap ceil call in int() on Py2
# Compute integer square root; don't even store result, just setting flags
ctx.sqrt(x).to_integral_exact()
# If previous line couldn't represent square root as exact int, sets Inexact flag
return not ctx.flags[Inexact]
Для демонстрации с действительно огромными значениями:
# I just kept mashing the numpad for awhile :-)
>>> base = 100009991439393999999393939398348438492389402490289028439083249803434098349083490340934903498034098390834980349083490384903843908309390282930823940230932490340983098349032098324908324098339779438974879480379380439748093874970843479280329708324970832497804329783429874329873429870234987234978034297804329782349783249873249870234987034298703249780349783497832497823497823497803429780324
>>> sqr = base ** 2
>>> sqr ** 0.5 # Too large to use floating point math
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
OverflowError: int too large to convert to float
>>> is_perfect_power(sqr)
True
>>> is_perfect_power(sqr-1)
False
>>> is_perfect_power(sqr+1)
False
Если вы увеличиваете размер тестируемого значения, это в конечном итоге становится довольно медленным (занимает около секунды для 200 000 бит-квадрата), но для более умеренных чисел (скажем, 20 000 бит) он все же быстрее, чем у человека заметят для отдельных значений (~ 33 мс на моей машине). Но поскольку скорость не была вашей основной задачей, это хороший способ сделать это с помощью стандартных библиотек Python.
Конечно, было бы гораздо быстрее использовать gmpy2
и просто проверить gmpy2.mpz(x).is_square()
, но если сторонние пакеты не являются ваша вещь, это работает хорошо.
Мой ответ:
def is_square(x):
return x**.5 % 1 == 0
Он в основном делает квадратный корень, затем по модулю на 1 обрезает целочисленную часть, а если результат равен 0, возвращает True
противном случае возвращает False
. В этом случае x может быть любым большим числом, но не таким большим, как максимальное число с плавающей запятой, которое может обработать python: 1.7976931348623157e + 308
Это неверно для большого квадрата, например 152415789666209426002111556165263283035677490.
Это мой метод:
def is_square(n) -> bool:
return int(n**0.5)**2 == int(n)
Возьмите квадратный корень из числа. Преобразовать в целое число. Возьми площадь. Если числа равны, то это идеальный квадрат, иначе нет.
Это неправильно для большого квадрата, такого как 152415789666209426002111556165263283035677489.
Вы можете бинарно искать круглый квадратный корень. Квадрат результата, чтобы увидеть, соответствует ли оно исходному значению.
Вам, вероятно, лучше ответить на FogleBirds, хотя будьте осторожны, поскольку арифметика с плавающей запятой является приблизительной, что может отбросить этот подход. В принципе вы могли бы получить ложный положительный результат от большого целого числа, которое представляет собой нечто большее, чем идеальный квадрат, например, из-за потери точности.
Численно это настолько наивное решение, насколько это возможно. Это работает для небольших номеров.
def is_perfect_square(n) -> bool:
return (n ** .5).is_integer()
Очевидно, что это не удается для большого числа, таких как 152415789666209426002111556165263283035677490.
Этот ответ не относится к вашему заявленному вопросу, но к неявному вопросу, который я вижу в опубликованном вами коде, т.е. о том, "как проверить, является ли что-то целое?"
Первый ответ, который вы обычно получите на этот вопрос: "Не надо!". И это правда, что в Python проверка типов обычно не подходит.
Однако для этих редких исключений вместо поиска десятичной точки в строковом представлении числа, нужно использовать функцию isinstance:
>>> isinstance(5,int)
True
>>> isinstance(5.0,int)
False
Конечно, это относится к переменной, а не к значению. Если бы я хотел определить, было ли значение целочисленным, я бы сделал следующее:
>>> x=5.0
>>> round(x) == x
True
Но, как все остальные подробно рассмотрели, в большинстве случаев, не связанных с игрушкой, такого типа вещей есть проблемы с плавающей запятой.
Если вы хотите перебрать диапазон и сделать что-то для каждого числа, которое НЕ является идеальным квадратом, вы можете сделать что-то вроде этого:
def non_squares(upper):
next_square = 0
diff = 1
for i in range(0, upper):
if i == next_square:
next_square += diff
diff += 2
continue
yield i
Если вы хотите сделать что-то для каждого числа, которое является идеальным квадратом, генератор еще проще:
(n * n for n in range(upper))
Я думаю, что это работает и очень просто:
import math
def is_square(num):
sqrt = math.sqrt(num)
return sqrt == int(sqrt)
Это неверно для большого квадрата, например 152415789666209426002111556165263283035677490.
Вы могли бы сделать что-то вроде:
В псевдокоде:
function isSquare(x) = x == floor(sqrt(x) + 0.5)^2
В Python:
import math
def is_square(x):
return x == (math.floor(math.sqrt(x) + 0.5) ** 2)
То есть возьмите число, найдите квадратный корень, округлите его до ближайшего целого числа, возведите в квадрат и проверьте, совпадает ли оно с исходным числом. (floor
и добавление 0.5
сделано для того, чтобы такие случаи, как sqrt(4)
возвращали 1.9999999...
из-за математики с плавающей запятой, как указал Майк Грэм.)
Если вам интересно, когда-то было очень хорошее обсуждение о том, как быстрее всего определить, является ли целочисленный квадратный корень целым числом.
Вышеуказанная функция Python неверна для большого квадрата, такого как 152415789666209426002111556165263283035677489
import math
def is_square(n):
sqrt = math.sqrt(n)
return sqrt == int(sqrt)
Сбой для большого неквадрата, например 152415789666209426002111556165263283035677490.
У меня есть небольшое улучшение исходного решения с использованием вавилонского подхода. Вместо использования набора для хранения каждого ранее сгенерированного приближения сохраняются и проверяются только последние два аппроксимации против текущего приближения. Это экономит огромное количество времени, потраченного впустую, проверяя весь набор предыдущих приближений. Я использую java вместо python и класс BigInteger вместо обычного примитивного целого числа.
BigInteger S = BigInteger.ZERO;
BigInteger x = BigInteger.ZERO;
BigInteger prev1 = BigInteger.ZERO;
BigInteger prev2 = BigInteger.ZERO;
Boolean isInt = null;
x = S.divide(BigInteger.valueOf(2));
while (true) {
x = x.add(preA.divide(x)).divide(BigInteger.valueOf(2));
if (x.pow(2).equals(S)) {
isInt = true;
break;
}
if (prev1.equals(x) || prev2.equals(x)) {
isInt = false;
break;
}
prev2 = prev1;
prev1 = x;
}
Существует очень простой способ сделать это. Найдите количество факторов, количество которых (включая один и сам). Если у него есть нечетное количество факторов, это квадрат.
def getFactors(n):
'''Code for counting factors of n.'''
result = 1 # not forgetting to count the number itself
for i in range(1, n // 2 + 1):
if n % i == 0:
result += 1
return result
Если результат функции нечетный, это квадрат.
EDIT:
Это может быть не лучший метод для компьютерной программы, но это хороший метод для людей.