La forma más rápida de determinar si la raíz cuadrada de un número entero es un número entero
Frecuentes
Visto 287,691 equipos
1513
Estoy buscando la forma más rápida de determinar si un long
el valor es un cuadrado perfecto (es decir, su raíz cuadrada es otro número entero):
- Lo he hecho de la manera más fácil, utilizando el
Math.sqrt()
función, pero me pregunto si hay una manera de hacerlo más rápido restringiéndose al dominio de solo enteros. - Mantener una tabla de búsqueda no es práctico (ya que hay aproximadamente 231.5 enteros cuyo cuadrado es menor que 263).
Aquí está la forma muy simple y directa en que lo estoy haciendo ahora:
public final static boolean isPerfectSquare(long n)
{
if (n < 0)
return false;
long tst = (long)(Math.sqrt(n) + 0.5);
return tst*tst == n;
}
Nota: estoy usando esta función en muchos Proyecto Euler problemas. Por lo tanto, nadie más tendrá que mantener este código. Y este tipo de microoptimización podría marcar la diferencia, ya que parte del desafío es hacer todos los algoritmos en menos de un minuto, y esta función deberá llamarse millones de veces en algunos problemas.
Probé las diferentes soluciones al problema:
- Después de pruebas exhaustivas, descubrí que agregar
0.5
al resultado de Math.sqrt () no es necesario, al menos no en mi máquina. - La raíz cuadrada inversa rápida fue más rápido, pero dio resultados incorrectos para n> = 410881. Sin embargo, como sugiere bobbyshaftoe, podemos usar el truco FISR para n <410881.
- El método de Newton fue un poco más lento que
Math.sqrt()
. Probablemente esto se deba a queMath.sqrt()
usa algo similar al método de Newton, pero implementado en el hardware, por lo que es mucho más rápido que en Java. Además, el método de Newton todavía requería el uso de dobles. - Un método de Newton modificado, que usó algunos trucos para que solo estuvieran involucradas las matemáticas de enteros, requirió algunos trucos para evitar el desbordamiento (quiero que esta función funcione con todos los enteros positivos de 64 bits con signo), y aún era más lento que
Math.sqrt()
. - El corte binario fue aún más lento. Esto tiene sentido porque el corte binario requerirá en promedio 16 pasadas para encontrar la raíz cuadrada de un número de 64 bits.
- Según las pruebas de John, usando
or
sentencias es más rápido en C ++ que usar unswitch
, pero en Java y C # no parece haber diferencia entreor
yswitch
. - También intenté hacer una tabla de búsqueda (como una matriz estática privada de 64 valores booleanos). Entonces, en lugar de cambiar o
or
declaración, solo diríaif(lookup[(int)(n&0x3F)]) { test } else return false;
. Para mi sorpresa, esto fue (solo un poco) más lento. Esto es porque los límites de la matriz se verifican en Java.
0 Respuestas
No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas java math optimization perfect-square or haz tu propia pregunta.
Este es el código Java, donde int == 32 bits y long == 64 bits, y ambos están firmados. - Kip
@Shreevasta: He realizado algunas pruebas con valores grandes (superiores a 2 ^ 53) y su método da algunos falsos positivos. El primero que se encuentra es para n = 9007199326062755, que no es un cuadrado perfecto pero se devuelve como uno. - Kip
Por favor, no lo llames el "truco de John Carmack". No se le ocurrió. - user9282
@mamama - Quizás, pero se le atribuye. Henry Ford no inventó el automóvil, los Wright Bros.no inventaron el avión, y Galleleo no fue el primero en descubrir que la Tierra giraba alrededor del sol ... el mundo está hecho de inventos robados (y amor). - Robert Fraser
Puede obtener un pequeño aumento de velocidad en el 'error rápido' usando algo como
((1<<(n&15))|65004) != 0
, en lugar de tener tres controles separados. - Nabb