Saya menggunakan fungsi berikut untuk menghitung log basis 2 untuk bilangan bulat:
public static int log2(int n){
if(n <= 0) throw new IllegalArgumentException();
return 31 - Integer.numberOfLeadingZeros(n);
}
Apakah memiliki kinerja optimal?
Apakah ada yang tahu fungsi J2SE API siap untuk tujuan itu?
UPD1 Anehnya bagi saya, aritmatika float point tampaknya lebih cepat daripada aritmatika integer.
UPD2 Karena komentar saya akan melakukan penyelidikan lebih rinci.
UPD3 Fungsi aritmatika integer saya 10 kali lebih cepat dari Math.log (n) /Math.log (2).
java
performance
discrete-mathematics
logarithm
Nulldevice
sumber
sumber
Math.floor(Math.log(n)/Math.log(2))
, jadi itu tidak benar-benar menghitung basis log 2!Jawaban:
Jika Anda berpikir untuk menggunakan floating-point untuk membantu integer arithmetics, Anda harus berhati-hati.
Saya biasanya mencoba menghindari perhitungan FP jika memungkinkan.
Operasi titik-mengambang tidak tepat. Anda tidak akan pernah tahu pasti apa yang akan
(int)(Math.log(65536)/Math.log(2))
dievaluasi. Misalnya,Math.ceil(Math.log(1<<29) / Math.log(2))
adalah 30 pada PC saya di mana secara matematis seharusnya 29. Saya tidak menemukan nilai untuk x di mana(int)(Math.log(x)/Math.log(2))
gagal (hanya karena hanya ada 32 nilai "berbahaya"), tetapi itu tidak berarti bahwa ia akan bekerja dengan cara yang sama pada PC apa pun.Trik yang biasa di sini adalah menggunakan "epsilon" saat pembulatan. Seperti
(int)(Math.log(x)/Math.log(2)+1e-10)
seharusnya tidak pernah gagal. Pilihan "epsilon" ini bukanlah tugas sepele.Lebih banyak demonstrasi, menggunakan tugas yang lebih umum - mencoba menerapkan
int log(int x, int base)
:Kode pengujian:
Jika kita menggunakan implementasi logaritma yang paling mudah,
ini mencetak:
Untuk benar-benar menghilangkan kesalahan saya harus menambahkan epsilon yaitu antara 1e-11 dan 1e-14. Bisakah Anda memberi tahu ini sebelum pengujian? Saya pasti tidak bisa.
sumber
strictfp
, bukan?strictfp
tampaknya benar-benar mendapatkan banyak omong kosong karena, pada kenyataannya, ketat. :-)return ((long)Math.log(x) / (long)Math.log(base));
mengatasi semua kesalahan?Ini adalah fungsi yang saya gunakan untuk perhitungan ini:
Ini sedikit lebih cepat daripada Integer.numberOfLeadingZeros () (20-30%) dan hampir 10 kali lebih cepat (jdk 1,6 x64) daripada implementasi berbasis Math.log () seperti ini:
Kedua fungsi mengembalikan hasil yang sama untuk semua nilai input yang mungkin.
Pembaruan: Java 1.7 server JIT dapat menggantikan beberapa fungsi matematika statis dengan implementasi alternatif berdasarkan pada intrinsik CPU. Salah satu fungsi tersebut adalah Integer.numberOfLeadingZeros (). Jadi dengan 1.7 atau lebih baru VM server, implementasi seperti yang ada di pertanyaan sebenarnya sedikit lebih cepat daripada yang di
binlog
atas. Sayangnya, JIT klien tampaknya tidak memiliki pengoptimalan ini.Implementasi ini juga mengembalikan hasil yang sama untuk semua 2 ^ 32 nilai input yang mungkin seperti dua implementasi lainnya yang saya posting di atas.
Berikut adalah runtime aktual di PC saya (Sandy Bridge i7):
JDK 1,7 32 Bit klien VM:
JDK 1,7 x64 server VM:
Ini adalah kode tes:
sumber
BSR
Instruksi x86 memang32 - numberOfLeadingZeros
, tetapi tidak terdefinisi untuk 0, jadi kompiler (JIT) harus memeriksa bukan-nol jika tidak dapat membuktikannya tidak harus. Instruksi set BMI ekstensi (Haswell dan yang lebih baru) diperkenalkanLZCNT
, yang sepenuhnya mengimplementasikan dengannumberOfLeadingZeros
tepat, dalam satu instruksi. Keduanya 3 siklus latensi, 1 per siklus throughput. Jadi saya benar-benar merekomendasikan menggunakannumberOfLeadingZeros
, karena itu membuatnya mudah untuk JVM yang baik. (Satu hal yang anehlzcnt
adalah bahwa ia memiliki ketergantungan salah pada nilai lama register yang ditimpa.)Mencoba
Math.log(x) / Math.log(2)
sumber
Anda dapat menggunakan identitas
jadi ini akan berlaku untuk log2.
cukup tancapkan ini ke metode java Math log10 ....
http://mathforum.org/library/drmath/view/55565.html
sumber
Kenapa tidak:
sumber
Ada fungsi di perpustakaan jambu:
Jadi saya sarankan untuk menggunakannya.
sumber
Untuk menambahkan jawaban x4u, yang memberi Anda dasar log biner suatu angka, fungsi ini mengembalikan langit-langit log biner angka:
sumber
Beberapa kasus baru berfungsi ketika saya menggunakan Math.log10:
sumber
mari tambahkan:
Sumber: https://github.com/pochuan/cs166/blob/master/ps1/rmq/SparseTableRMQ.java
sumber
Untuk menghitung log basis 2 dari n, ekspresi berikut dapat digunakan:
sumber