これはおそらくかなり基本的なことですが、1時間ほど悲しみを節約するために、Javaで特定の正の整数を表すために必要なビット数を計算する方法を誰かに教えてもらえますか?
例えば10進数の11、(1011)を取得します。答えを得る必要がある、4。
最上位ビット以外のすべてのビットを0に設定する方法を考え出して、>>>それを解決できるかどうか考えたので、答えがわかります。しかし...私はできません。
さて、あなたはちょうどゼロになる前にあなたが右にシフトする回数を数えることができます:
int value = 11;
int count = 0;
while (value > 0) {
count++;
value = value >> 1;
}
まあ、答えはかなり簡単です。 int値がある場合:
int log2(int value) {
return Integer.SIZE-Integer.numberOfLeadingZeros(value);
}
同じことがLongにも存在します...
[編集]ここでミリ秒のシェービングが問題である場合、Integer.numberOfLeadingZeros(int)はかなり効率的ですが、それでも15の操作を実行します...適切な量のメモリ(300バイト、静的)を拡張して、1〜8にシェービングできます。整数の範囲に応じた操作。
私のJavaは少し錆びていますが、言語に依存しない答え(「log2」関数と「floor」関数が利用可能な場合)は次のようになります:
numberOfBits = floor(log2(decimalNumber))+1
「decimalNumber」が0より大きいと仮定します。0の場合、必要なのは1ビットだけです。
Integer.toBinaryString(number).length();
良い悲しみ...なぜ反対票を投じるのですか?
public class Main
{
public static void main(final String[] argv)
{
System.out.println(Integer.toBinaryString(0).length());
System.out.println(Integer.toBinaryString(1).length());
System.out.println(Integer.toBinaryString(2).length());
System.out.println(Integer.toBinaryString(3).length());
System.out.println(Integer.toBinaryString(4).length());
System.out.println(Integer.toBinaryString(5).length());
System.out.println(Integer.toBinaryString(6).length());
System.out.println(Integer.toBinaryString(7).length());
System.out.println(Integer.toBinaryString(8).length());
System.out.println(Integer.toBinaryString(9).length());
}
}
出力:
1
1
2
2
3
3
3
3
4
4
以下は、さまざまなソリューションの速度の簡単なテストです。
public class Tester
{
public static void main(final String[] argv)
{
final int size;
final long totalA;
final long totalB;
final long totalC;
final long totalD;
size = 100000000;
totalA = test(new A(), size);
totalB = test(new B(), size);
totalC = test(new C(), size);
totalD = test(new D(), size);
System.out.println();
System.out.println("Total D = " + totalD + " ms");
System.out.println("Total B = " + totalB + " ms");
System.out.println("Total C = " + totalC + " ms");
System.out.println("Total A = " + totalA + " ms");
System.out.println();
System.out.println("Total B = " + (totalB / totalD) + " times slower");
System.out.println("Total C = " + (totalC / totalD) + " times slower");
System.out.println("Total A = " + (totalA / totalD) + " times slower");
}
private static long test(final Testable tester,
final int size)
{
final long start;
final long end;
final long total;
start = System.nanoTime();
tester.test(size);
end = System.nanoTime();
total = end - start;
return (total / 1000000);
}
private static interface Testable
{
void test(int size);
}
private static class A
implements Testable
{
@Override
public void test(final int size)
{
int value;
value = 0;
for(int i = 1; i < size; i++)
{
value += Integer.toBinaryString(i).length();
}
System.out.println("value = " + value);
}
}
private static class B
implements Testable
{
@Override
public void test(final int size)
{
int total;
total = 0;
for(int i = 1; i < size; i++)
{
int value = i;
int count = 0;
while (value > 0)
{
count++;
value >>= 1;
}
total += count;
}
System.out.println("total = " + total);
}
}
private static class C
implements Testable
{
@Override
public void test(final int size)
{
int total;
final double log2;
total = 0;
log2 = Math.log(2);
for(int i = 1; i < size; i++)
{
final double logX;
final double temp;
logX = Math.log(i);
temp = logX / log2;
total += (int)Math.floor(temp) + 1;
}
System.out.println("total = " + total);
}
}
private static class D
implements Testable
{
@Override
public void test(final int size)
{
int total;
total = 0;
for(int i = 1; i < size; i++)
{
total += 32-Integer.numberOfLeadingZeros(i);
}
System.out.println("total = " + total);
}
}
}
私のマシンの出力は:
value = -1729185023
total = -1729185023
total = -1729185023
total = -1729185023
Total D = 118 ms
Total B = 1722 ms
Total C = 4462 ms
Total A = 5704 ms
Total B = 14 times slower
Total C = 37 times slower
Total A = 48 times slower
スピードについて不満を言う人のために... https://en.wikipedia.org/wiki/Program_optimization#Quotes 。
最初にプログラムを読みやすいように記述してから、どこが遅いかを調べ、次に速くします。最適化の前後に、変更をテストします。変更がコードの可読性を低下させるという犠牲を払うのに十分な大きさでなかった場合、変更を気にしないでください。
数値の2つのベースのログを取ると、それを格納するために必要なビット数が報告されます。
ループを避けようとしていて速度を気にしている場合は、次のような方法を使用できます。
int value = ...;
int count = 0;
if( value < 0 ) { value = 0; count = 32; }
if( value >= 0x7FFF ) { value >>= 16; count += 16; }
if( value >= 0x7F ) { value >>= 8; count += 8; }
if( value >= 0x7 ) { value >>= 4; count += 4; }
if( value >= 0x3 ) { value >>= 2; count += 2; }
if( value >= 0x1 ) { value >>= 1; count += 1; }
Javaには符号なし整数がないため、最初のif(value <0)には少し疑問があります。負の数は常に最上位ビットを設定するため、間違いなくそれらを表すにはWord全体が必要です。気になる場合は、その動作を適応させます。
ちなみに、64ビット整数を処理するには、if(value <0)行を次の2つに置き換えます。
if( value < 0 ) { value = 0; count = 64; }
if( value >= 0x7FFFFFFF ) { value >>= 32; count += 32; }
負でない値の場合、おそらく最も直接的な答えは次のとおりです。
Java.math.BigDecimal.valueOf(value).bitLength()
(負の数の場合は、2の補数表記から予想される無限大ではなく、絶対値よりも1短いビット長になります。)
指数2のバイナリ検索は、ビットシフト( トップ投票の回答 )ソリューションよりも高速です。これは、数値が大きい場合(数千桁)、利用可能な最大ビットがわかっている場合に役立ちます。そして、あなたはテーブルを生成したくない:
int minExpVal = 0;
int maxExpVal = 62;
int medExpVal = maxExpVal >> 1;
long medianValue = 0l;
while (maxExpVal - minExpVal > 1) {
medianValue = 1l << medExpVal;
if (value > medianValue) {
minExpVal = medExpVal;
} else {
maxExpVal = medExpVal;
}
medExpVal = (minExpVal + maxExpVal) >> 1;
}
return value == 1l << maxExpVal ? maxExpVal + 1 : maxExpVal;
ただし、先行ゼロを使用するソリューションは、はるかに高速です。
return Long.SIZE - Long.numberOfLeadingZeros(value);
ベンチマーク:
Leading zeros time is: 2 ms
BinarySearch time is: 95 ms
BitShift time is: 135 ms
これはC言語ですが、Javaに簡単に変換できます:
元の値を変更したくない場合は、このようにすることもできます。
_unsigned int value = 11;
unsigned int count = 0;
if(value > 0)
{
for(int i=1;i<value;i*=2) // multiply by two => shift one to left
{
++count;
}
}
_
注:パフォーマンスを向上させるために、コンパイラーが_i*=2
_をビットシフト演算に変換することを考慮してください。
私たちの間の視覚的な思想家のために:
_64 32 16 8 4 2 1
0 0 0 1 0 1 1 -> binary representation of decimal number 'value' = 11 (=1+2+8)
_
右側の_i=1
_から始めます。次に、_i < value
_である限り、2を掛け続けます。その間、左側に何ビット進んだかを追跡します。
したがって、この例では、i
が16に達するとすぐに、値が11より大きいため、停止します。次に、4ビットをカウントします:1 *2 *2 *2 *2 = 16 (=2^4)
。
符号付きの数値に注意。正または負の可能性がある符号付き数値を処理する場合、最初に負の数値に-1を掛ける必要があります。さらに、サインビットをどのように考慮するかを考慮する必要があります。
このようなものはどうですか:
public static int getNumberOfBits(int N) {
int bits = 0;
while(Math.pow(2, bits) <= N){
bits++;
}
return bits;
}
ループを使用しない方法を探しているのはわかっていますが、ビットは数の2の累乗にすぎないため、そうでない場合はかなり前向きです。
これは私のために働きます!
int numberOfBitsRequired(int n)
{
return (int)Math.floor(Math.log(n)/Math.log(2)) + 1;
}
負の数も含めるには、ビットを1つ追加し、それを使用して符号を指定します。
public static int numberOfBitsRequiredSigned(int n)
{
return (int)Math.floor(Math.log(Math.abs(n))/Math.log(2)) + 2;
}
(int) Math.ceil((Math.log(n) / Math.log(2))
もちろん、これは正の整数に対してのみ機能します。