Eu uso a seguinte função para calcular a base de log 2 para números inteiros:
public static int log2(int n){
if(n <= 0) throw new IllegalArgumentException();
return 31 - Integer.numberOfLeadingZeros(n);
}
Tem desempenho ideal?
Alguém sabe a função pronta da API J2SE para esse fim?
UPD1 Surpreendentemente para mim, a aritmética de ponto flutuante parece ser mais rápida que a aritmética de número inteiro.
UPD2 Devido a comentários, conduzirei uma investigação mais detalhada.
UPD3 Minha função aritmética inteira é 10 vezes mais rápida que Math.log (n) /Math.log (2).
java
performance
discrete-mathematics
logarithm
Nulldevice
fonte
fonte
Math.floor(Math.log(n)/Math.log(2))
, portanto, não é realmente o cálculo da base de log 2!Respostas:
Se você estiver pensando em usar ponto flutuante para ajudar na aritmética de números inteiros, tenha cuidado.
Eu costumo tentar evitar cálculos de FP sempre que possível.
As operações de ponto flutuante não são exatas. Você nunca pode ter certeza do que
(int)(Math.log(65536)/Math.log(2))
avaliará. Por exemplo,Math.ceil(Math.log(1<<29) / Math.log(2))
é 30 no meu PC, onde matematicamente deveria ser exatamente 29. Não encontrei um valor para x em que(int)(Math.log(x)/Math.log(2))
falha (apenas porque existem apenas 32 valores "perigosos"), mas isso não significa que funcionará da mesma maneira. da mesma maneira em qualquer PC.O truque usual aqui é usar "epsilon" ao arredondar. Como
(int)(Math.log(x)/Math.log(2)+1e-10)
nunca deve falhar. A escolha deste "epsilon" não é uma tarefa trivial.Mais demonstração, usando uma tarefa mais geral - tentando implementar
int log(int x, int base)
:O código de teste:
Se usarmos a implementação mais direta do logaritmo,
isto imprime:
Para me livrar completamente dos erros, tive que adicionar o epsilon, que fica entre 1e-11 e 1e-14. Você poderia ter dito isso antes do teste? Eu definitivamente não podia.
fonte
strictfp
, não?strictfp
parece ter realmente recebido muita porcaria por ser, de fato, rigoroso. :-)return ((long)Math.log(x) / (long)Math.log(base));
resolver todos os erros?Esta é a função que eu uso para este cálculo:
É um pouco mais rápido que Integer.numberOfLeadingZeros () (20-30%) e quase 10 vezes mais rápido (jdk 1.6 x64) que uma implementação baseada em Math.log () como esta:
Ambas as funções retornam os mesmos resultados para todos os possíveis valores de entrada.
Atualização: O servidor Java 1.7 JIT é capaz de substituir algumas funções matemáticas estáticas por implementações alternativas baseadas em intrínsecas da CPU. Uma dessas funções é Integer.numberOfLeadingZeros (). Portanto, com uma VM de servidor 1.7 ou mais recente, uma implementação como a da pergunta é realmente um pouco mais rápida que a
binlog
anterior. Infelizmente, o JIT do cliente não parece ter essa otimização.Essa implementação também retorna os mesmos resultados para todos os 2 ^ 32 possíveis valores de entrada que as outras duas implementações postadas acima.
Aqui estão os tempos de execução reais no meu PC (Sandy Bridge i7):
VM do cliente JDK 1.7 32 bits:
VM do servidor JDK 1.7 x64:
Este é o código de teste:
fonte
BSR
instruções do x86 funcionam32 - numberOfLeadingZeros
, mas não são definidas como 0; portanto, um compilador (JIT) precisa verificar se não é zero se não puder provar que não precisa. As extensões do conjunto de instruções BMI (Haswell e mais recentes) foram introduzidasLZCNT
, que implementam completamentenumberOfLeadingZeros
exatamente, em uma única instrução. Ambos têm latência de 3 ciclos, 1 por taxa de transferência de ciclo. Portanto, eu recomendaria absolutamente o usonumberOfLeadingZeros
, porque isso facilita para uma boa JVM. (A única coisa estranha sobrelzcnt
é que ele tem uma falsa dependência do valor antigo do registo ele substitui.)Experimentar
Math.log(x) / Math.log(2)
fonte
você pode usar a identidade
portanto, isso seria aplicável ao log2.
basta conectar isso ao método java Math log10 ....
http://mathforum.org/library/drmath/view/55565.html
fonte
Por que não:
fonte
Existe a função nas bibliotecas da goiaba:
Então eu sugiro usá-lo.
fonte
Para adicionar à resposta x4u, que fornece o piso do log binário de um número, essa função retorna o teto do log binário de um número:
fonte
Alguns casos funcionaram quando usei o Math.log10:
fonte
vamos adicionar:
Fonte: https://github.com/pochuan/cs166/blob/master/ps1/rmq/SparseTableRMQ.java
fonte
Para calcular a base de log 2 de n, a seguinte expressão pode ser usada:
fonte