¿Cómo puedo comprobar si un número es un cuadrado perfecto?
La velocidad no importa, por ahora, solo funciona.
Respuestas:
El problema de confiar en cualquier cálculo de punto flotante ( math.sqrt(x)
, o x**0.5
) es que no puede estar realmente seguro de que sea exacto (para enteros suficientemente grandes x
, no lo será, e incluso podría desbordarse). Afortunadamente (si no tiene prisa ;-) hay muchos enfoques de enteros puros, como los siguientes ...:
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)
Sugerencia: se basa en el "algoritmo babilónico" para la raíz cuadrada, consulte wikipedia . Se hace trabajo para cualquier número positivo para el que tenga suficiente memoria para el cálculo de proceder a la terminación ;-).
Editar : veamos un ejemplo ...
x = 12345678987654321234567 ** 2
for i in range(x, x+2):
print i, is_square(i)
esto se imprime, como se desee (y también en un tiempo razonable ;-):
152415789666209426002111556165263283035677489 True
152415789666209426002111556165263283035677490 False
Por favor, antes de proponer soluciones basadas en flotante resultados intermedios de punto, asegurarse de que funcionan correctamente en este ejemplo simple - no es que duro (sólo tiene algunas verificaciones adicionales en caso de que la raíz cuadrada calculada es un poco apagado), sólo se necesita una un poco de cuidado.
Y luego prueba con x**7
y encuentre una forma inteligente de solucionar el problema que obtendrá,
OverflowError: long int too large to convert to float
tendrás que ser cada vez más inteligente a medida que los números sigan creciendo, por supuesto.
Si yo estaba en un apuro, por supuesto, que haría uso de gmpy - pero entonces, estoy claramente sesgado ;-).
>>> import gmpy
>>> gmpy.is_square(x**7)
1
>>> gmpy.is_square(x**7 + 1)
0
Sí, lo sé, eso es tan fácil que se siente como hacer trampa (un poco como me siento hacia Python en general ;-) - sin inteligencia en absoluto, solo franqueza y simplicidad perfectas (y, en el caso de gmpy, pura velocidad ; -) ...
set([x])
={x}
set
exceso? ¿No es Babilónico simplemente converger int(sqrt(x))
, donde solo tenemos que verificar si prev != next
?
Use el método de Newton para concentrarse rápidamente en la raíz cuadrada entera más cercana, luego eleve al cuadrado y vea si es su número. Ver isqrt .
Python ≥ 3.8 tiene math.isqrt
. Si usa una versión anterior de Python, busque la def isqrt(n)
implementación " " aquí .
import math
def is_square(i: int) -> bool:
return i == math.isqrt(i) ** 2
Dado que nunca puede depender de comparaciones exactas cuando se trata de cálculos de punto flotante (como estas formas de calcular la raíz cuadrada), una implementación menos propensa a errores sería
import math
def is_square(integer):
root = math.sqrt(integer)
return integer == int(root + 0.5) ** 2
Imagínese lo integer
es 9
. math.sqrt(9)
podría ser 3.0
, pero también podría ser algo como 2.99999
o 3.00001
, por lo que cuadrar el resultado de inmediato no es confiable. Sabiendo que se int
toma el valor mínimo, aumentar el valor flotante 0.5
primero significa que obtendremos el valor que estamos buscando si estamos en un rango en el que float
todavía tiene una resolución lo suficientemente fina como para representar números cercanos al que estamos buscando. .
if int(root + 0.5) ** 2 == integer:
si int
actúa en función floor
de los números que nos interesan.
math.sqrt(9)
realmente puede serlo alguna vez 2.99999
? Python se float
asigna a C double
, pero creo que incluso un tipo FP de 16 bits tiene más precisión que eso, así que tal vez si tuviera un compilador C que usara FP de 8 bits ("minifloats") como double
tipo Supongo que es técnicamente posible, pero me parece poco probable que ese sea el caso en cualquier computadora que ejecute Python en la actualidad.
math.sqrt(9)
vuelva 2.99999
en ningún sistema en particular, pero el resultado real depende del sistema y no se puede esperar que sea exacto.
Si está interesado, tengo una respuesta puramente matemática a una pregunta similar en Math stackexchange, "Detectar cuadrados perfectos más rápido que extrayendo la raíz cuadrada" .
Mi propia implementación de isSquare (n) puede no ser la mejor, pero me gusta. Me tomó varios meses de estudio en teoría matemática, computación digital y programación de Python, comparándome con otros contribuyentes, etc., para realmente hacer clic con este método. Sin embargo, me gusta su simplicidad y eficiencia. No he visto mejor. Dime que piensas.
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
Muy claro. Primero verifica que tengamos un número entero, y además uno positivo. De lo contrario, no tiene sentido. Deja que 0 se deslice como Verdadero (necesario o el siguiente bloque es un bucle infinito).
El siguiente bloque de código elimina sistemáticamente las potencias de 4 en un subalgoritmo muy rápido que utiliza desplazamiento de bits y operaciones lógicas de bits. En última instancia, no estamos encontrando el isSquare de nuestro n original sino de un k <n que se ha reducido en potencias de 4, si es posible. Esto reduce el tamaño del número con el que estamos trabajando y realmente acelera el método babilónico, pero también hace que otras verificaciones también sean más rápidas.
El tercer bloque de código realiza una prueba lógica de bits booleana simple. Los tres dígitos menos significativos, en binario, de cualquier cuadrado perfecto son 001. Siempre. De todos modos, salvo los ceros iniciales resultantes de potencias de 4, que ya se han contabilizado. Si falla la prueba, inmediatamente sabrá que no es un cuadrado. Si pasa, no puede estar seguro.
Además, si terminamos con un 1 para un valor de prueba, entonces el número de prueba era originalmente una potencia de 4, incluido quizás el 1 mismo.
Al igual que el tercer bloque, el cuarto prueba el valor de un lugar en decimal usando un operador de módulo simple y tiende a capturar valores que se escapan de la prueba anterior. También una prueba de mod 7, mod 8, mod 9 y mod 13.
El quinto bloque de código comprueba algunos de los patrones cuadrados perfectos conocidos. Los números que terminan en 1 o 9 están precedidos por un múltiplo de cuatro. Y los números que terminan en 5 deben terminar en 5625, 0625, 225 o 025. Había incluido otros, pero me di cuenta de que eran redundantes o que nunca se usaron.
Por último, el sexto bloque de código se parece mucho a la respuesta del principal respondedor, Alex Martelli. Básicamente, encuentra la raíz cuadrada utilizando el antiguo algoritmo babilónico, pero restringiéndola a valores enteros ignorando el punto flotante. Realizado tanto para la velocidad como para ampliar las magnitudes de los valores que se pueden comprobar. Usé conjuntos en lugar de listas porque toma mucho menos tiempo, usé cambios de bits en lugar de división por dos, y elegí inteligentemente un valor de inicio inicial de manera mucho más eficiente.
Por cierto, probé el número de prueba recomendado por Alex Martelli, así como algunos números muchos órdenes de magnitud más grandes, como:
x=1000199838770766116385386300483414671297203029840113913153824086810909168246772838680374612768821282446322068401699727842499994541063844393713189701844134801239504543830737724442006577672181059194558045164589783791764790043104263404683317158624270845302200548606715007310112016456397357027095564872551184907513312382763025454118825703090010401842892088063527451562032322039937924274426211671442740679624285180817682659081248396873230975882215128049713559849427311798959652681930663843994067353808298002406164092996533923220683447265882968239141724624870704231013642255563984374257471112743917655991279898690480703935007493906644744151022265929975993911186879561257100479593516979735117799410600147341193819147290056586421994333004992422258618475766549646258761885662783430625 ** 2
for i in range(x, x+2):
print(i, isSquare(i))
imprimió los siguientes resultados:
1000399717477066534083185452789672211951514938424998708930175541558932213310056978758103599452364409903384901149641614494249195605016959576235097480592396214296565598519295693079257885246632306201885850365687426564365813280963724310434494316592041592681626416195491751015907716210235352495422858432792668507052756279908951163972960239286719854867504108121432187033786444937064356645218196398775923710931242852937602515835035177768967470757847368349565128635934683294155947532322786360581473152034468071184081729335560769488880138928479829695277968766082973795720937033019047838250608170693879209655321034310764422462828792636246742456408134706264621790736361118589122797268261542115823201538743148116654378511916000714911467547209475246784887830649309238110794938892491396597873160778553131774466638923135932135417900066903068192088883207721545109720968467560224268563643820599665232314256575428214983451466488658896488012211237139254674708538347237589290497713613898546363590044902791724541048198769085430459186735166233549186115282574626012296888817453914112423361525305960060329430234696000121420787598967383958525670258016851764034555105019265380321048686563527396844220047826436035333266263375049097675787975100014823583097518824871586828195368306649956481108708929669583308777347960115138098217676704862934389659753628861667169905594181756523762369645897154232744410732552956489694024357481100742138381514396851789639339362228442689184910464071202445106084939268067445115601375050153663645294106475257440167535462278022649865332161044187890625 True
1000399717477066534083185452789672211951514938424998708930175541558932213310056978758103599452364409903384901149641614494249195605016959576235097480592396214296565598519295693079257885246632306201885850365687426564365813280963724310434494316592041592681626416195491751015907716210235352495422858432792668507052756279908951163972960239286719854867504108121432187033786444937064356645218196398775923710931242852937602515835035177768967470757847368349565128635934683294155947532322786360581473152034468071184081729335560769488880138928479829695277968766082973795720937033019047838250608170693879209655321034310764422462828792636246742456408134706264621790736361118589122797268261542115823201538743148116654378511916000714911467547209475246784887830649309238110794938892491396597873160778553131774466638923135932135417900066903068192088883207721545109720968467560224268563643820599665232314256575428214983451466488658896488012211237139254674708538347237589290497713613898546363590044902791724541048198769085430459186735166233549186115282574626012296888817453914112423361525305960060329430234696000121420787598967383958525670258016851764034555105019265380321048686563527396844220047826436035333266263375049097675787975100014823583097518824871586828195368306649956481108708929669583308777347960115138098217676704862934389659753628861667169905594181756523762369645897154232744410732552956489694024357481100742138381514396851789639339362228442689184910464071202445106084939268067445115601375050153663645294106475257440167535462278022649865332161044187890626 False
Y lo hizo en 0,33 segundos.
En mi opinión, mi algoritmo funciona igual que el de Alex Martelli, con todos los beneficios del mismo, pero tiene el beneficio agregado de rechazos de prueba simple altamente eficientes que ahorran mucho tiempo, sin mencionar la reducción en el tamaño de los números de prueba por potencias de 4, que mejora la velocidad, la eficiencia, la precisión y el tamaño de los números comprobables. Probablemente sea especialmente cierto en implementaciones que no son de Python.
Aproximadamente el 99% de todos los enteros se rechazan como no cuadrados antes de que se implemente la extracción de raíz babilónica, y en 2/3 del tiempo que le tomaría al babilónico rechazar el entero. Y aunque estas pruebas no aceleran el proceso de manera tan significativa, la reducción en todos los números de prueba a un valor impar al dividir todas las potencias de 4 realmente acelera la prueba de Babilonia.
Hice una prueba de comparación de tiempo. Probé todos los números enteros de 1 a 10 millones en sucesión. Usando solo el método babilónico por sí mismo (con mi suposición inicial especialmente diseñada), mi Surface 3 tomó un promedio de 165 segundos (con una precisión del 100%). Usando solo las pruebas lógicas en mi algoritmo (excluyendo el babilónico), tomó 127 segundos, rechazó el 99% de todos los enteros como no cuadrados sin rechazar por error ningún cuadrado perfecto. De los enteros que pasaron, solo el 3% fueron cuadrados perfectos (una densidad mucho mayor). Usando el algoritmo completo anterior que emplea tanto las pruebas lógicas como la extracción de la raíz babilónica, tenemos una precisión del 100% y la finalización de la prueba en solo 14 segundos. Los primeros 100 millones de enteros tardan aproximadamente 2 minutos y 45 segundos en probarse.
EDITAR: He podido reducir aún más el tiempo. Ahora puedo probar los enteros de 0 a 100 millones en 1 minuto y 40 segundos. Se pierde mucho tiempo comprobando el tipo de datos y la positividad. Eliminé los dos primeros controles y reduje el experimento un minuto. Se debe asumir que el usuario es lo suficientemente inteligente como para saber que los negativos y los flotantes no son cuadrados perfectos.
import math
def is_square(n):
sqrt = math.sqrt(n)
return (sqrt - int(sqrt)) == 0
Un cuadrado perfecto es un número que se puede expresar como el producto de dos números enteros iguales. math.sqrt(number)
volver a float
. int(math.sqrt(number))
arroja el resultado a int
.
Si la raíz cuadrada es un número entero, como 3, por ejemplo, entonces math.sqrt(number) - int(math.sqrt(number))
será 0 y el if
enunciado será False
. Si la raíz cuadrada era un número real como 3.2, entonces lo será True
e imprimirá "no es un cuadrado perfecto".
Falla para un gran no cuadrado como 152415789666209426002111556165263283035677490.
if (math.sqrt(number)-int(math.sqrt(number))):
a a=math.sqrt(number)
continuación, otra línea para: if a-int(a):
. Esto se debe a que solo tiene que calcular la raíz cuadrada una vez, lo que en mi opinión para n grande es significativo
Mi respuesta es:
def is_square(x):
return x**.5 % 1 == 0
Básicamente hace una raíz cuadrada, luego módulo por 1 para quitar la parte entera y si el resultado es 0, devuelve de lo True
contrario False
. En este caso, x puede ser cualquier número grande, pero no tan grande como el número flotante máximo que Python puede manejar: 1.7976931348623157e + 308
Es incorrecto para un no cuadrado grande como 152415789666209426002111556165263283035677490.
Esto se puede resolver usando el decimal
módulo para obtener raíces cuadradas de precisión arbitraria y verificaciones fáciles de "exactitud":
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 demostraciones con valores realmente 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
Si aumenta el tamaño del valor que se está probando, esto eventualmente se vuelve bastante lento (toma cerca de un segundo para un cuadrado de 200,000 bits), pero para números más moderados (digamos, 20,000 bits), aún es más rápido de lo que un humano notaría. valores individuales (~ 33 ms en mi máquina). Pero dado que la velocidad no era su principal preocupación, esta es una buena forma de hacerlo con las bibliotecas estándar de Python.
Por supuesto, sería mucho más rápido de usar gmpy2
y simplemente probar gmpy2.mpz(x).is_square()
, pero si los paquetes de terceros no son lo tuyo, lo anterior funciona bastante bien.
Acabo de publicar una ligera variación en algunos de los ejemplos anteriores en otro hilo ( Encontrar cuadrados perfectos ) y pensé que incluiría una ligera variación de lo que publiqué allí (usando nsqrt como variable temporal), en caso de que sea de interés / utilizar:
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)
Es incorrecto para un no cuadrado grande como 152415789666209426002111556165263283035677490.
Este es mi método:
def is_square(n) -> bool:
return int(n**0.5)**2 == int(n)
Saca la raíz cuadrada del número. Convierta a entero. Toma la plaza. Si los números son iguales, entonces es un cuadrado perfecto de lo contrario no.
Es incorrecto para un cuadrado grande como 152415789666209426002111556165263283035677489.
Puede realizar una búsqueda binaria de la raíz cuadrada redondeada. Cuadre el resultado para ver si coincide con el valor original.
Probablemente esté mejor con la respuesta de FogleBirds, aunque tenga cuidado, ya que la aritmética de punto flotante es aproximada, lo que puede desviar este enfoque. En principio, podría obtener un falso positivo de un entero grande que es uno más que un cuadrado perfecto, por ejemplo, debido a la pérdida de precisión.
Si el módulo (resto) que sobra de dividir por la raíz cuadrada es 0, entonces es un cuadrado perfecto.
def is_square(num: int) -> bool:
return num % math.sqrt(num) == 0
Verifiqué esto con una lista de cuadrados perfectos que iban hasta 1000.
Esta respuesta no se refiere a su pregunta planteada, sino a una pregunta implícita que veo en el código que publicó, es decir, "¿cómo verificar si algo es un número entero?"
La primera respuesta que generalmente obtendrá a esa pregunta es "¡No lo haga!" Y es cierto que en Python, la verificación de tipos generalmente no es lo correcto.
Sin embargo, para esas raras excepciones, en lugar de buscar un punto decimal en la representación de cadena del número, lo que se debe hacer es usar la función isinstance :
>>> isinstance(5,int)
True
>>> isinstance(5.0,int)
False
Por supuesto, esto se aplica a la variable más que a un valor. Si quisiera determinar si el valor es un número entero, haría esto:
>>> x=5.0
>>> round(x) == x
True
Pero como todos los demás han cubierto en detalle, hay problemas de punto flotante que deben considerarse en la mayoría de los ejemplos de este tipo de cosas que no son juguetes.
Si desea recorrer un rango y hacer algo para cada número que NO sea un cuadrado perfecto, puede hacer algo como esto:
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
Si desea hacer algo por cada número que ES un cuadrado perfecto, el generador es aún más fácil:
(n * n for n in range(upper))
Creo que esto funciona y es muy simple:
import math
def is_square(num):
sqrt = math.sqrt(num)
return sqrt == int(sqrt)
Es incorrecto para un no cuadrado grande como 152415789666209426002111556165263283035677490.
set
Cuando x in seen
es True
:
x
secuencia de 511, 256, 129, 68, 41, 32, 31 , 31 ;Por lo tanto, basta con detenerse en cuanto la corriente x
sea mayor o igual a la 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)
Equivalencia con el algoritmo original probado para 1 <n <10 ** 7. En el mismo intervalo, esta variante ligeramente más simple es aproximadamente 1,4 veces más 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')
La idea es ejecutar un bucle desde i = 1 hasta el piso (sqrt (n)) y luego verificar si cuadrarlo hace 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)
Falla para un gran no cuadrado como 152415789666209426002111556165263283035677490.