Javaの整数の桁数
1. 前書き
このクイックチュートリアルでは、Javaのdifferent ways of getting the number of digits in an Integerについて説明します。
また、これらのさまざまな方法を分析し、どのアルゴリズムが私たちの状況に最も適しているかを判断します。
2. Integerの桁数
ここで説明する方法では、正の整数のみを考慮しています。 負の入力が予想される場合は、これらの方法を使用する前に、まずMath.abs(number)を使用できます。
2.1. Stringベースのソリューション
おそらく、Integerの桁数を取得する最も簡単な方法は、それをStringに変換し、length()メソッドを呼び出すことです。 これにより、数値のString表現の長さが返されます。
int length = String.valueOf(number).length();
But, this may be a sub-optimal approach, as this statement involves memory allocation for a String, for each evaluation. JVMは、最初に数値を解析し、その数字を個別のStringにコピーし、さまざまな操作(一時コピーの保持、Unicode変換の処理など)を実行する必要があります。
評価する数値が数個しかない場合は、このソリューションを明確に使用できます。このアプローチと他のアプローチの違いは、大きな数値であっても無視できるためです。
2.2. 対数アプローチ
10進数で表される数値の場合、基数10でログを取得して切り上げると、その数値の桁数が取得されます。
int length = (int) (Math.log10(number) + 1);
任意の数のlog100は定義されていないことに注意してください。 したがって、値0の入力が予想される場合は、それもチェックできます。
The logarithmic approach is significantly faster than String based approach as it doesn’t have to go through the process of any data conversion.追加のオブジェクトの初期化やループを行わずに、単純で単純な計算を行うだけです。
2.3. 繰り返し乗算
この方法では、一時変数(1に初期化)を取得し、数値が大きくなるまで10を継続的に乗算します。 このプロセスでは、length変数も使用して、数値の長さを追跡します。
int length = 0;
long temp = 1;
while (temp <= number) {
length++;
temp *= 10;
}
return length;
このコードでは、行temp *= 10はtemp = (temp << 3) + (temp << 1)を書き込むのと同じです。 通常、乗算演算子は、シフト演算子と比較した場合、一部のプロセッサーでよりコストの高い操作であるため、後者の方が少し効率的です。
2.4. 2の累乗で割る
数値の範囲がわかっている場合は、比較をさらに減らすバリエーションを使用できます。 この方法では、数値を2の累乗で除算します(例: 1、2、4、8など):
この方法では、数値を2の累乗で除算します(例: 1、2、4、8など):
int length = 1;
if (number >= 100000000) {
length += 8;
number /= 100000000;
}
if (number >= 10000) {
length += 4;
number /= 10000;
}
if (number >= 100) {
length += 2;
number /= 100;
}
if (number >= 10) {
length += 1;
}
return length;
任意の数を2の累乗の加算で表すことができるという事実を利用します。 たとえば、15は8 + 4 + 2 + 1として表すことができ、すべて2の累乗です。
15桁の数値の場合、以前のアプローチで15回の比較を行っていましたが、この方法では4回に削減されました。
2.5. 分割統治
ここで説明する他のすべてと比較すると、これはおそらくthe bulkiest approachですが、変換、乗算、加算、またはオブジェクトの初期化を実行していないため、言うまでもなくthis one is the fastestです。
答えは、3つまたは4つの単純なifステートメントで得られます。
if (number < 100000) {
if (number < 100) {
if (number < 10) {
return 1;
} else {
return 2;
}
} else {
if (number < 1000) {
return 3;
} else {
if (number < 10000) {
return 4;
} else {
return 5;
}
}
}
} else {
if (number < 10000000) {
if (number < 1000000) {
return 6;
} else {
return 7;
}
} else {
if (number < 100000000) {
return 8;
} else {
if (number < 1000000000) {
return 9;
} else {
return 10;
}
}
}
}
前のアプローチと同様に、数値の範囲がわかっている場合にのみ、このメソッドを使用できます。
3. ベンチマーク
考えられる解決策を十分に理解したので、Java Microbenchmark Harness (JMH)を使用してすべてのメソッドの簡単なベンチマークを実行してみましょう。
次の表は、各操作の平均処理時間(ナノ秒単位)を示しています。
Benchmark Mode Cnt Score Error Units
Benchmarking.stringBasedSolution avgt 200 32.736 ± 0.589 ns/op
Benchmarking.logarithmicApproach avgt 200 26.123 ± 0.064 ns/op
Benchmarking.repeatedMultiplication avgt 200 7.494 ± 0.207 ns/op
Benchmarking.dividingWithPowersOf2 avgt 200 1.264 ± 0.030 ns/op
Benchmarking.divideAndConquer avgt 200 0.956 ± 0.011 ns/op
最も単純なStringベースのソリューションは、データ変換と新しいオブジェクトの初期化を必要とする唯一のソリューションであるため、最もコストのかかる操作でもあります。
対数アプローチは、データ変換を必要としないため、以前のソリューションと比較して大幅に効率的です。 また、単一行のソリューションであるため、String-ベースのアプローチの優れた代替手段となる可能性があります。
繰り返し乗算には、数の長さに比例した単純な乗算が含まれます。たとえば、数値の長さが15桁の場合、このメソッドには15の乗算が含まれます。
ただし、次の方法では、すべての数値を2の累乗で表すことができるという事実を利用し(BCDと同様のアプローチ)、4分割演算に減らすため、前者よりもさらに効率的です。
最後に、推測できるように、the most efficient algorithm is the verbose Divide and Conquer implementation –これは3つまたは4つの単純なifステートメントで答えを提供します。 分析する必要がある数の大きなデータセットがある場合に使用できます。
4. 結論
この短い記事では、Integerの桁数を見つけるいくつかの方法の概要を説明し、各アプローチの効率を比較しました。
そして、いつものように、完全なコードover on GitHubを見つけることができます。