I think the best take, using only "built-in" integer arithmetic, is:
def issquare(n): return math.isqrt(n)**2==n
(Squares x**2 are a priori more efficiently computed than products x*x...)
According to my timings, this is (at least up to ~ 10^8) faster than sympy.numtheory.primetest.is_square
.
(I use a different name to make it easier to compare the two.)
The latter is first using some modular checks that should speed it up considerably, but it has so much conversion and testing overhead (int
, as_int
, squares become n-th powers with n=2, integers are converted from "small" to multiprecision integers and back, ...) that all the advantage is lost. After a lot of tests, it roughly does the above, using ntheory.nthroot, which is again an overkill: designed for any n-th root, the square root is just one special case, and noting is optimized for this case. Some subroutines there even do very weird floating point arithmetics including multiplication with 1.0000000001 and similar horrors... I once got the following horrible error message: (The original output has the full path "C:\Users\Username\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.9_qbz5n2kfra8p0\LocalCache\local-packages\Python39\site-packages\"
instead of each "..." below...)
File "...\sympy\ntheory\primetest.py", line 112, in is_square
return integer_nthroot(n, 2)[1]
File "...\sympy\core\power.py", line 86, in integer_nthroot
return _integer_nthroot_python(y, n)
File "...\sympy\core\power.py", line 94, in _integer_nthroot_python
x, rem = mpmath_sqrtrem(y)
File "...\mpmath\libmp\libintmath.py", line 284, in sqrtrem_python
y = isqrt_small_python(x)
File "...\mpmath\libmp\libintmath.py", line 217, in isqrt_small_python
r = int(x**0.5 * 1.00000000000001) + 1
KeyboardInterrupt
This gives a good idea of the abyss into which sympy
's is_square
hopelessly drowns...
Documentation: see is_square
in Sympy's Ntheory Class Reference
P.S.: FWIW, regarding other answers suggesting to use round()
etc, let me just mention here that ceil(sqrt(n))**2 < n
(!!), for example when n is the 26th and 27th primorial -- not extremely large numbers! So, clearly, use of math.sqrt
is not adequate.