J'utilise la fonction suivante pour calculer le journal de base 2 pour les entiers:
public static int log2(int n){
if(n <= 0) throw new IllegalArgumentException();
return 31 - Integer.numberOfLeadingZeros(n);
}
At-il des performances optimales?
Est-ce que quelqu'un connaît la fonction API J2SE prête à cet effet?
UPD1 Étonnamment pour moi, l'arithmétique à virgule flottante semble être plus rapide que l'arithmétique entière.
UPD2 En raison des commentaires, je vais mener une enquête plus détaillée.
UPD3 Ma fonction arithmétique entière est 10 fois plus rapide que Math.log (n) /Math.log (2).
java
performance
discrete-mathematics
logarithm
Nulldevice
la source
la source
Math.floor(Math.log(n)/Math.log(2))
, donc ce n'est pas vraiment calculateur de base de journal 2!Réponses:
Si vous envisagez d'utiliser la virgule flottante pour vous aider avec l'arithmétique entière, vous devez être prudent.
J'essaie généralement d'éviter les calculs de FP autant que possible.
Les opérations en virgule flottante ne sont pas exactes. Vous ne pouvez jamais savoir avec certitude ce qui vaudra
(int)(Math.log(65536)/Math.log(2))
. Par exemple,Math.ceil(Math.log(1<<29) / Math.log(2))
est 30 sur mon PC où mathématiquement il devrait être exactement 29. Je n'ai pas trouvé de valeur pour x où(int)(Math.log(x)/Math.log(2))
échoue (simplement parce qu'il n'y a que 32 valeurs "dangereuses"), mais cela ne signifie pas que cela fonctionnera le de la même manière sur n'importe quel PC.L'astuce habituelle ici est d'utiliser "epsilon" lors de l'arrondi. Comme
(int)(Math.log(x)/Math.log(2)+1e-10)
ne devrait jamais échouer. Le choix de cet "epsilon" n'est pas une tâche anodine.Plus de démonstration, en utilisant une tâche plus générale - essayer de mettre en œuvre
int log(int x, int base)
:Le code de test:
Si nous utilisons l'implémentation la plus simple du logarithme,
ceci imprime:
Pour me débarrasser complètement des erreurs, j'ai dû ajouter epsilon qui se situe entre 1e-11 et 1e-14. Auriez-vous pu le dire avant de tester? Je ne pourrais certainement pas.
la source
strictfp
, non?strictfp
semble avoir eu beaucoup de conneries pour être, en fait, strict. :-)return ((long)Math.log(x) / (long)Math.log(base));
de résoudre toutes les erreurs?C'est la fonction que j'utilise pour ce calcul:
Il est légèrement plus rapide que Integer.numberOfLeadingZeros () (20-30%) et presque 10 fois plus rapide (jdk 1.6 x64) qu'une implémentation basée sur Math.log () comme celle-ci:
Les deux fonctions renvoient les mêmes résultats pour toutes les valeurs d'entrée possibles.
Mise à jour: Le serveur JIT Java 1.7 est capable de remplacer quelques fonctions mathématiques statiques par des implémentations alternatives basées sur les intrinsèques du processeur. L'une de ces fonctions est Integer.numberOfLeadingZeros (). Ainsi, avec une VM de serveur 1.7 ou plus récente, une implémentation comme celle de la question est en fait légèrement plus rapide que celle
binlog
ci - dessus. Malheureusement, le client JIT ne semble pas avoir cette optimisation.Cette implémentation renvoie également les mêmes résultats pour les 2 ^ 32 valeurs d'entrée possibles que les deux autres implémentations que j'ai publiées ci-dessus.
Voici les temps d'exécution réels sur mon PC (Sandy Bridge i7):
VM client JDK 1.7 32 bits:
VM serveur JDK 1.7 x64:
Voici le code de test:
la source
BSR
instruction de x86 le fait32 - numberOfLeadingZeros
, mais non définie pour 0, donc un compilateur (JIT) doit vérifier s'il n'est pas nul s'il ne peut pas prouver qu'il n'est pas obligé de le faire. Les extensions du jeu d'instructions BMI (Haswell et plus récent) ont été introduitesLZCNT
, qui implémentent complètementnumberOfLeadingZeros
exactement, en une seule instruction. Ils ont tous deux une latence de 3 cycles, un débit par cycle. Je recommande donc absolument d'utilisernumberOfLeadingZeros
, car cela facilite la création d'une bonne JVM. (Ce qui est étrange,lzcnt
c'est qu'il a une fausse dépendance sur l'ancienne valeur du registre qu'il écrase.)Essayer
Math.log(x) / Math.log(2)
la source
vous pouvez utiliser l'identité
ce serait donc applicable pour log2.
il suffit de le brancher dans la méthode java Math log10 ....
http://mathforum.org/library/drmath/view/55565.html
la source
Pourquoi pas:
la source
Il y a la fonction dans les bibliothèques de goyaves:
Je suggère donc de l'utiliser.
la source
Pour ajouter à x4u answer, qui vous donne le plancher du log binaire d'un nombre, cette fonction renvoie le ceil du log binaire d'un nombre:
la source
Certains cas ont juste fonctionné lorsque j'ai utilisé Math.log10:
la source
ajoutons:
Source: https://github.com/pochuan/cs166/blob/master/ps1/rmq/SparseTableRMQ.java
la source
Pour calculer la base logarithmique 2 de n, l'expression suivante peut être utilisée:
la source