Como posso verificar se um número é um quadrado perfeito?
Velocidade não é problema, por enquanto, apenas trabalhar.
python
math
perfect-square
Acumenus
fonte
fonte
Respostas:
O problema de confiar em qualquer cálculo de ponto flutuante (
math.sqrt(x)
oux**0.5
) é que você não pode realmente ter certeza de que é exato (para números inteiros suficientemente grandesx
, 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 ; -) ...
fonte
set([x])
={x}
set
ovekill? Babylonian não converge paraint(sqrt(x))
, onde nós apenas temos que verificar seprev != 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 adef isqrt(n)
implementação " " aqui .import math def is_square(i: int) -> bool: return i == math.isqrt(i) ** 2
fonte
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 ser3.0
, mas também poderia ser algo como2.99999
ou3.00001
, portanto, ajustar o resultado de imediato não é confiável. Sabendo queint
leva o valor mínimo, aumentar o valor flutuante0.5
primeiro significa que obteremos o valor que estamos procurando se estivermos em um intervalo em quefloat
ainda tenha uma resolução fina o suficiente para representar números próximos ao que estamos procurando .fonte
if int(root + 0.5) ** 2 == integer:
ifint
age comofloor
para os números que nos interessam.math.sqrt(9)
realmente ser2.99999
? Python'sfloat
mapeia para C'sdouble
, 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 seudouble
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ça2.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.
fonte
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 afloat
.int(math.sqrt(number))
lança o resultado paraint
.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 aif
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.
fonte
if (math.sqrt(number)-int(math.sqrt(number))):
paraa=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 é significativoMinha 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, retornarFalse
. 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.
fonte
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 testargmpy2.mpz(x).is_square()
, mas se você não gosta de pacotes de terceiros , o procedimento acima funciona muito bem.fonte
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.
fonte
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.
fonte
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.
fonte
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.
fonte
fonte
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.
fonte
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))
fonte
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.
fonte
Uma variante da solução @Alex Martelli sem
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.
fonte
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')
fonte
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; }
fonte
import math def is_square(n): sqrt = math.sqrt(n) return sqrt == int(sqrt)
Ele falha para um grande não quadrado, como 152415789666209426002111556165263283035677490.
fonte