Utilizo la siguiente función para calcular la base de registro 2 para enteros:
public static int log2(int n){
if(n <= 0) throw new IllegalArgumentException();
return 31 - Integer.numberOfLeadingZeros(n);
}
¿Tiene un rendimiento óptimo?
¿Alguien sabe la función API J2SE lista para ese propósito?
UPD1 Sorprendentemente para mí, la aritmética de coma flotante parece ser más rápida que la aritmética de enteros.
UPD2 Debido a los comentarios, realizaré una investigación más detallada.
UPD3 Mi función aritmética de enteros es 10 veces más rápida que Math.log (n) /Math.log (2).
java
performance
discrete-mathematics
logarithm
Dispositivo nulo
fuente
fuente
Math.floor(Math.log(n)/Math.log(2))
, ¡así que no está realmente calculando la base de registro 2!Respuestas:
Si está pensando en usar coma flotante para ayudar con la aritmética de enteros, debe tener cuidado.
Por lo general, trato de evitar los cálculos de FP siempre que sea posible.
Las operaciones de punto flotante no son exactas. Nunca se puede saber con certeza qué
(int)(Math.log(65536)/Math.log(2))
evaluará. Por ejemplo,Math.ceil(Math.log(1<<29) / Math.log(2))
es 30 en mi PC donde matemáticamente debería ser exactamente 29. No encontré un valor para x donde(int)(Math.log(x)/Math.log(2))
falla (solo porque solo hay 32 valores "peligrosos"), pero eso no significa que funcionará de la misma manera en cualquier PC.El truco habitual aquí es usar "epsilon" al redondear. Como
(int)(Math.log(x)/Math.log(2)+1e-10)
nunca debería fallar. La elección de este "épsilon" no es una tarea trivial.Más demostración, utilizando una tarea más general: tratar de implementar
int log(int x, int base)
:El código de prueba:
Si usamos la implementación más directa de logaritmo,
esto imprime:
Para deshacerme completamente de los errores, tuve que agregar epsilon, que está entre 1e-11 y 1e-14. ¿Podría haber dicho esto antes de la prueba? Definitivamente no pude.
fuente
strictfp
, ¿no?strictfp
Parece que en realidad se ha vuelto un montón de basura por ser, de hecho, estricto. :-)return ((long)Math.log(x) / (long)Math.log(base));
Qué tal si resolvemos todos los errores?Esta es la función que uso para este cálculo:
Es ligeramente más rápido que Integer.numberOfLeadingZeros () (20-30%) y casi 10 veces más rápido (jdk 1.6 x64) que una implementación basada en Math.log () como esta:
Ambas funciones devuelven los mismos resultados para todos los valores de entrada posibles.
Actualización: el JIT del servidor Java 1.7 puede reemplazar algunas funciones matemáticas estáticas con implementaciones alternativas basadas en intrínsecos de la CPU. Una de esas funciones es Integer.numberOfLeadingZeros (). Entonces, con una VM de servidor 1.7 o más reciente, una implementación como la de la pregunta es en realidad un poco más rápida que la
binlog
anterior. Desafortunadamente, el cliente JIT no parece tener esta optimización.Esta implementación también devuelve los mismos resultados para los 2 ^ 32 valores de entrada posibles que las otras dos implementaciones que publiqué anteriormente.
Estos son los tiempos de ejecución reales en mi PC (Sandy Bridge i7):
JDK 1.7 VM de cliente de 32 bits:
VM de servidor JDK 1.7 x64:
Este es el código de prueba:
fuente
BSR
instrucción de x86 sí32 - numberOfLeadingZeros
, pero no está definida para 0, por lo que un compilador (JIT) tiene que verificar que no sea cero si no puede probar que no es necesario. Se introdujeron las extensiones del conjunto de instrucciones de BMI (Haswell y más recientes)LZCNT
, que se implementa completamentenumberOfLeadingZeros
, en una sola instrucción. Ambos tienen latencia de 3 ciclos, 1 por rendimiento de ciclo. Así que recomiendo absolutamente usarlonumberOfLeadingZeros
, porque eso hace que sea fácil para una buena JVM. (Lo único extrañolzcnt
es que tiene una dependencia falsa del valor anterior del registro que sobrescribe.)Tratar
Math.log(x) / Math.log(2)
fuente
puedes usar la identidad
entonces esto sería aplicable para log2.
simplemente conecte esto al método java Math log10 ...
http://mathforum.org/library/drmath/view/55565.html
fuente
Por qué no:
fuente
Existe la función en las bibliotecas de guayaba:
Entonces sugiero usarlo.
fuente
Para agregar a la respuesta x4u, que le da el piso del registro binario de un número, esta función devuelve el límite máximo del registro binario de un número:
fuente
Algunos casos simplemente funcionaron cuando usé Math.log10:
fuente
agreguemos:
Fuente: https://github.com/pochuan/cs166/blob/master/ps1/rmq/SparseTableRMQ.java
fuente
Para calcular la base de registro 2 de n, se puede usar la siguiente expresión:
fuente