Como posso verificar se um número é um quadrado perfeito?
Velocidade não é problema, por enquanto, apenas trabalhar.
Respostas:
O problema de confiar em qualquer cálculo de ponto flutuante ( math.sqrt(x)
ou x**0.5
) é que você não pode realmente ter certeza de que é exato (para números inteiros suficientemente grandes x
, não será e pode até mesmo estourar). Felizmente (se não houver pressa ;-), existem muitas abordagens de inteiros puros, como as seguintes ...:
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)
Dica: é baseado no "algoritmo babilônico" para raiz quadrada, consulte a wikipedia . Ele faz o trabalho para qualquer número positivo para o qual você tem memória suficiente para a computação para avançar para a conclusão ;-).
Edit : vamos ver um exemplo ...
x = 12345678987654321234567 ** 2
for i in range(x, x+2):
print i, is_square(i)
isso é impresso, conforme desejado (e em um período de tempo razoável também ;-):
152415789666209426002111556165263283035677489 True
152415789666209426002111556165263283035677490 False
Por favor, antes de propor soluções baseadas em resultados intermediários de ponto flutuante, certifique-se eles funcionam corretamente neste exemplo simples - não é que difícil (você só precisa de algumas verificações extras no caso do sqrt computadorizada é um pouco fora), só tem um pouco de cuidado.
Em seguida, tente x**7
encontrar uma maneira inteligente de contornar o problema que você terá,
OverflowError: long int too large to convert to float
você terá que ficar cada vez mais inteligente à medida que os números continuam crescendo, é claro.
Se eu estivesse com pressa, é claro, usaria o gmpy - mas sou claramente tendencioso ;-).
>>> import gmpy
>>> gmpy.is_square(x**7)
1
>>> gmpy.is_square(x**7 + 1)
0
Sim, eu sei, isso é tão fácil que parece que estou trapaceando (um pouco como eu me sinto em relação ao Python em geral ;-) - nenhuma inteligência, apenas franqueza e simplicidade perfeitas (e, no caso do gmpy, velocidade absoluta ; -) ...
set([x])
={x}
set
ovekill? Babylonian não converge para int(sqrt(x))
, onde nós apenas temos que verificar se prev != next
?
Use o método de Newton para zerar rapidamente a raiz quadrada inteira mais próxima, depois eleve ao quadrado e veja se é o seu número. Veja isqrt .
Python ≥ 3,8 tem math.isqrt
. Se estiver usando uma versão mais antiga do Python, procure a def isqrt(n)
implementação " " aqui .
import math
def is_square(i: int) -> bool:
return i == math.isqrt(i) ** 2
Visto que você nunca pode depender de comparações exatas ao lidar com cálculos de ponto flutuante (como essas formas de calcular a raiz quadrada), uma implementação menos sujeita a erros seria
import math
def is_square(integer):
root = math.sqrt(integer)
return integer == int(root + 0.5) ** 2
Imagine integer
é 9
. math.sqrt(9)
poderia ser 3.0
, mas também poderia ser algo como 2.99999
ou 3.00001
, portanto, ajustar o resultado de imediato não é confiável. Sabendo que int
leva o valor mínimo, aumentar o valor flutuante 0.5
primeiro significa que obteremos o valor que estamos procurando se estivermos em um intervalo em que float
ainda tenha uma resolução fina o suficiente para representar números próximos ao que estamos procurando .
if int(root + 0.5) ** 2 == integer:
if int
age como floor
para os números que nos interessam.
math.sqrt(9)
realmente ser 2.99999
? Python's float
mapeia para C's double
, mas acho que mesmo um tipo de FP de 16 bits tem mais precisão do que isso, talvez se você tivesse um compilador C que usasse FP de 8 bits ("minifloats") como seu double
tipo? Suponho que seja tecnicamente possível, mas me parece improvável que seja o caso em qualquer computador que execute Python hoje.
math.sqrt(9)
isso aconteça 2.99999
em qualquer sistema específico, mas o resultado real depende do sistema e não pode ser esperado que seja exato.
Se você estiver interessado, eu tenho uma resposta de matemática pura para uma pergunta semelhante na troca de pilha de matemática, "Detectando quadrados perfeitos mais rápido do que extraindo a raiz quadrada" .
Minha própria implementação de isSquare (n) pode não ser a melhor, mas eu gosto. Levei vários meses de estudo em teoria matemática, computação digital e programação python, comparando-me com outros colaboradores, etc., para realmente clicar com este método. Eu gosto de sua simplicidade e eficiência. Eu não vi melhor. Me diga o que você acha.
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
Bem direto. Primeiro, ele verifica se temos um número inteiro e positivo. Caso contrário, não há nenhum ponto. Ele deixa 0 passar por True (necessário ou então o próximo bloco é um loop infinito).
O próximo bloco de código remove sistematicamente potências de 4 em um sub-algoritmo muito rápido usando operações de bit shift e lógica de bit. No final das contas, não estamos encontrando o isSquare de nosso n original, mas de um k <n que foi reduzido em potências de 4, se possível. Isso reduz o tamanho do número com o qual estamos trabalhando e realmente acelera o método babilônico, mas também torna outras verificações mais rápidas.
O terceiro bloco de código executa um teste simples de lógica de bits booleana. Os três dígitos menos significativos, em binário, de qualquer quadrado perfeito são 001. Sempre. Exceto pelos zeros à esquerda resultantes de potências de 4, de qualquer maneira, que já foram contabilizados. Se falhar no teste, você saberá imediatamente que não é um quadrado. Se passar, você não pode ter certeza.
Além disso, se terminarmos com 1 para um valor de teste, o número do teste seria originalmente uma potência de 4, incluindo talvez o próprio 1.
Como o terceiro bloco, o quarto testa o valor de uma casa em decimal usando o operador de módulo simples e tende a capturar valores que escapam ao teste anterior. Também um teste de mod 7, mod 8, mod 9 e mod 13.
O quinto bloco de código verifica alguns dos padrões de quadrados perfeitos bem conhecidos. Os números que terminam em 1 ou 9 são precedidos por um múltiplo de quatro. E os números que terminam em 5 devem terminar em 5625, 0625, 225 ou 025. Eu havia incluído outros, mas percebi que eram redundantes ou nunca foram realmente usados.
Por fim, o sexto bloco de código se parece muito com o que respondeu com maior frequência - Alex Martelli - a resposta. Basicamente encontra a raiz quadrada usando o antigo algoritmo babilônico, mas restringindo-a a valores inteiros enquanto ignora o ponto flutuante. Feito para velocidade e extensão das magnitudes dos valores que são testáveis. Usei conjuntos em vez de listas porque leva muito menos tempo, usei deslocamentos de bits em vez de divisão por dois e escolhi inteligentemente um valor inicial de início com muito mais eficiência.
A propósito, testei o número de teste recomendado por Alex Martelli, bem como alguns números com magnitude de ordens maiores, como:
x=1000199838770766116385386300483414671297203029840113913153824086810909168246772838680374612768821282446322068401699727842499994541063844393713189701844134801239504543830737724442006577672181059194558045164589783791764790043104263404683317158624270845302200548606715007310112016456397357027095564872551184907513312382763025454118825703090010401842892088063527451562032322039937924274426211671442740679624285180817682659081248396873230975882215128049713559849427311798959652681930663843994067353808298002406164092996533923220683447265882968239141724624870704231013642255563984374257471112743917655991279898690480703935007493906644744151022265929975993911186879561257100479593516979735117799410600147341193819147290056586421994333004992422258618475766549646258761885662783430625 ** 2
for i in range(x, x+2):
print(i, isSquare(i))
imprimiu os seguintes resultados:
1000399717477066534083185452789672211951514938424998708930175541558932213310056978758103599452364409903384901149641614494249195605016959576235097480592396214296565598519295693079257885246632306201885850365687426564365813280963724310434494316592041592681626416195491751015907716210235352495422858432792668507052756279908951163972960239286719854867504108121432187033786444937064356645218196398775923710931242852937602515835035177768967470757847368349565128635934683294155947532322786360581473152034468071184081729335560769488880138928479829695277968766082973795720937033019047838250608170693879209655321034310764422462828792636246742456408134706264621790736361118589122797268261542115823201538743148116654378511916000714911467547209475246784887830649309238110794938892491396597873160778553131774466638923135932135417900066903068192088883207721545109720968467560224268563643820599665232314256575428214983451466488658896488012211237139254674708538347237589290497713613898546363590044902791724541048198769085430459186735166233549186115282574626012296888817453914112423361525305960060329430234696000121420787598967383958525670258016851764034555105019265380321048686563527396844220047826436035333266263375049097675787975100014823583097518824871586828195368306649956481108708929669583308777347960115138098217676704862934389659753628861667169905594181756523762369645897154232744410732552956489694024357481100742138381514396851789639339362228442689184910464071202445106084939268067445115601375050153663645294106475257440167535462278022649865332161044187890625 True
1000399717477066534083185452789672211951514938424998708930175541558932213310056978758103599452364409903384901149641614494249195605016959576235097480592396214296565598519295693079257885246632306201885850365687426564365813280963724310434494316592041592681626416195491751015907716210235352495422858432792668507052756279908951163972960239286719854867504108121432187033786444937064356645218196398775923710931242852937602515835035177768967470757847368349565128635934683294155947532322786360581473152034468071184081729335560769488880138928479829695277968766082973795720937033019047838250608170693879209655321034310764422462828792636246742456408134706264621790736361118589122797268261542115823201538743148116654378511916000714911467547209475246784887830649309238110794938892491396597873160778553131774466638923135932135417900066903068192088883207721545109720968467560224268563643820599665232314256575428214983451466488658896488012211237139254674708538347237589290497713613898546363590044902791724541048198769085430459186735166233549186115282574626012296888817453914112423361525305960060329430234696000121420787598967383958525670258016851764034555105019265380321048686563527396844220047826436035333266263375049097675787975100014823583097518824871586828195368306649956481108708929669583308777347960115138098217676704862934389659753628861667169905594181756523762369645897154232744410732552956489694024357481100742138381514396851789639339362228442689184910464071202445106084939268067445115601375050153663645294106475257440167535462278022649865332161044187890626 False
E fez isso em 0,33 segundos.
Em minha opinião, meu algoritmo funciona da mesma forma que o de Alex Martelli, com todos os benefícios, mas tem o benefício adicional de rejeições de teste simples altamente eficientes que economizam muito tempo, sem mencionar a redução no tamanho dos números de teste por potências de 4, que melhora a velocidade, eficiência, precisão e o tamanho dos números que podem ser testados. Provavelmente, especialmente verdadeiro em implementações não Python.
Aproximadamente 99% de todos os inteiros são rejeitados como não-Quadrados antes mesmo de a extração da raiz da Babilônia ser implementada, e em 2/3 do tempo que o Babylonian levaria para rejeitar o inteiro. E embora esses testes não acelerem o processo significativamente, a redução em todos os números de teste para ímpar dividindo todas as potências de 4, na verdade acelera o teste babilônico.
Eu fiz um teste de comparação de tempo. Testei todos os inteiros de 1 a 10 milhões em sucessão. Usando apenas o método babilônico por si só (com meu palpite inicial especialmente adaptado), meu Surface 3 levou uma média de 165 segundos (com 100% de precisão). Usando apenas os testes lógicos em meu algoritmo (excluindo o Babylonian), levou 127 segundos, ele rejeitou 99% de todos os inteiros como não-quadrados sem rejeitar por engano quaisquer quadrados perfeitos. Desses inteiros aprovados, apenas 3% eram quadrados perfeitos (uma densidade muito maior). Usando o algoritmo completo acima, que emprega os testes lógicos e a extração de raiz da Babilônia, temos 100% de precisão e conclusão do teste em apenas 14 segundos. Os primeiros 100 milhões de números inteiros levam cerca de 2 minutos e 45 segundos para serem testados.
EDIT: Consegui reduzir ainda mais o tempo. Agora posso testar os inteiros de 0 a 100 milhões em 1 minuto e 40 segundos. Muito tempo é perdido verificando o tipo de dados e a positividade. Elimine as duas primeiras verificações e reduzo o experimento por um minuto. É preciso presumir que o usuário é inteligente o suficiente para saber que negativos e flutuantes não são quadrados perfeitos.
import math
def is_square(n):
sqrt = math.sqrt(n)
return (sqrt - int(sqrt)) == 0
Um quadrado perfeito é um número que pode ser expresso como o produto de dois inteiros iguais. math.sqrt(number)
retornar a float
. int(math.sqrt(number))
lança o resultado para int
.
Se a raiz quadrada for um número inteiro, como 3, por exemplo, então math.sqrt(number) - int(math.sqrt(number))
será 0, e a if
declaração será False
. Se a raiz quadrada for um número real como 3,2, então será True
e imprimirá "não é um quadrado perfeito".
Ele falha para um grande não quadrado, como 152415789666209426002111556165263283035677490.
if (math.sqrt(number)-int(math.sqrt(number))):
para a=math.sqrt(number)
, em seguida, uma outra linha para: if a-int(a):
. Isso ocorre porque ele só precisa calcular a raiz quadrada uma vez, o que imo para n grande é significativo
Minha resposta é:
def is_square(x):
return x**.5 % 1 == 0
Ele basicamente faz uma raiz quadrada, então o módulo por 1 para retirar a parte inteira e se o resultado for 0 retornar, True
caso contrário, retornar False
. Nesse caso, x pode ser qualquer número grande, mas não tão grande quanto o número flutuante máximo que o python pode manipular: 1.7976931348623157e + 308
É incorreto para um grande não quadrado, como 152415789666209426002111556165263283035677490.
Isso pode ser resolvido usando o decimal
módulo para obter raízes quadradas de precisão arbitrária e verificações fáceis de "exatidão":
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]
Para demonstração com valores verdadeiramente enormes:
# 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
Se você aumentar o tamanho do valor que está sendo testado, isso eventualmente se torna bastante lento (leva quase um segundo para um quadrado de 200.000 bits), mas para números mais moderados (digamos, 20.000 bits), ainda é mais rápido do que um humano notaria. valores individuais (~ 33 ms na minha máquina). Mas como a velocidade não era sua principal preocupação, essa é uma boa maneira de fazer isso com as bibliotecas padrão do Python.
Claro, seria muito mais rápido usar gmpy2
e apenas testar gmpy2.mpz(x).is_square()
, mas se você não gosta de pacotes de terceiros , o procedimento acima funciona muito bem.
Acabei de postar uma pequena variação em alguns dos exemplos acima em outro tópico ( Encontrando quadrados perfeitos ) e pensei em incluir uma pequena variação do que postei aqui (usando nsqrt como uma variável temporária), caso seja de interesse / usar:
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)
É incorreto para um grande não quadrado, como 152415789666209426002111556165263283035677490.
Este é o meu método:
def is_square(n) -> bool:
return int(n**0.5)**2 == int(n)
Tire a raiz quadrada do número. Converta para inteiro. Pegue a praça. Se os números forem iguais, então é um quadrado perfeito, caso contrário, não.
É incorreto para um grande quadrado, como 152415789666209426002111556165263283035677489.
Você poderia fazer uma busca binária pela raiz quadrada arredondada. Quadrado o resultado para ver se ele corresponde ao valor original.
Você provavelmente está melhor com a resposta de FogleBirds - mas cuidado, pois a aritmética de ponto flutuante é aproximada, o que pode atrapalhar essa abordagem. Você poderia, em princípio, obter um falso positivo de um inteiro grande que é mais um do que um quadrado perfeito, por exemplo, devido à perda de precisão.
Se o módulo (resto) restante da divisão pela raiz quadrada for 0, então é um quadrado perfeito.
def is_square(num: int) -> bool:
return num % math.sqrt(num) == 0
Eu verifiquei isso em uma lista de quadrados perfeitos que chegava a 1000.
Esta resposta não pertence à sua pergunta declarada, mas a uma pergunta implícita que vejo no código que você postou, ou seja, "como verificar se algo é um número inteiro?"
A primeira resposta que você geralmente obterá a essa pergunta é "Não!" E é verdade que em Python, a verificação de tipos geralmente não é a coisa certa a se fazer.
Para essas raras exceções, no entanto, em vez de procurar um ponto decimal na representação da string do número, a coisa a fazer é usar a função isinstance :
>>> isinstance(5,int)
True
>>> isinstance(5.0,int)
False
Claro que isso se aplica à variável e não a um valor. Se eu quisesse determinar se o valor é um número inteiro, faria o seguinte:
>>> x=5.0
>>> round(x) == x
True
Mas, como todo mundo cobriu em detalhes, há questões de ponto flutuante a serem consideradas na maioria dos exemplos que não são brinquedos desse tipo de coisa.
Se quiser fazer um loop em um intervalo e fazer algo para cada número que NÃO seja um quadrado perfeito, você pode fazer algo assim:
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
Se você quiser fazer algo para cada número que seja um quadrado perfeito, o gerador é ainda mais fácil:
(n * n for n in range(upper))
Acho que funciona e é muito simples:
import math
def is_square(num):
sqrt = math.sqrt(num)
return sqrt == int(sqrt)
É incorreto para um grande não quadrado, como 152415789666209426002111556165263283035677490.
set
Quando x in seen
é True
:
x
sequência de 511, 256, 129, 68, 41, 32, 31 , 31 ;Portanto, basta parar assim que a corrente x
for maior ou igual à anterior:
def is_square(n):
assert n > 1
previous = n
x = n // 2
while x * x != n:
x = (x + (n // x)) // 2
if x >= previous:
return False
previous = x
return True
x = 12345678987654321234567 ** 2
assert not is_square(x-1)
assert is_square(x)
assert not is_square(x+1)
Equivalência com o algoritmo original testado para 1 <n <10 ** 7. No mesmo intervalo, esta variante ligeiramente mais simples é cerca de 1,4 vezes mais rápida.
a=int(input('enter any number'))
flag=0
for i in range(1,a):
if a==i*i:
print(a,'is perfect square number')
flag=1
break
if flag==1:
pass
else:
print(a,'is not perfect square number')
A ideia é executar um loop de i = 1 até floor (sqrt (n)) e verificar se ao quadrado resulta n.
bool isPerfectSquare(int n)
{
for (int i = 1; i * i <= n; i++) {
// If (i * i = n)
if ((n % i == 0) && (n / i == i)) {
return true;
}
}
return false;
}
import math
def is_square(n):
sqrt = math.sqrt(n)
return sqrt == int(sqrt)
Ele falha para um grande não quadrado, como 152415789666209426002111556165263283035677490.