このメソッドよりもintの長さを取得するためのより良い方法はありますか?
int length = String.valueOf(1000).length();
このメソッドよりもintの長さを取得するためのより良い方法はありますか?
int length = String.valueOf(1000).length();
回答:
あなたの文字列ベースのソリューションは完全に問題なく、それについて「きちんとした」ものは何もありません。数学的には、数値には長さがなく、数字もないことを理解する必要があります。長さと数字はどちらも、特定の基数、つまり文字列での数値の物理表現のプロパティです。
対数ベースのソリューションは、文字列ベースのソリューションが内部で行うことと同じことを(一部)行い、長さのみを生成して桁を無視するため、おそらく(わずかに)より高速になります。しかし、私は実際にはそれを意図としてより明確に考えることはしません-そしてそれが最も重要な要素です。
Math.abs()
ただし、追加するとこれは修正されます。
対数はあなたの友達です:
int n = 1000;
int length = (int)(Math.log10(n)+1);
注意:n> 0の場合のみ有効です。
最速のアプローチ:分割統治。
範囲が0〜MAX_INTであるとすると、1〜10桁になります。各入力ごとに最大4つの比較を使用して、分割統治を使用してこの間隔にアプローチできます。最初に、[1..10]を1つの比較で[1..5]と[6..10]に分割し、次に、1つの比較を使用して1つの長さ3と1つの長さ2の間隔に分割する各長さ5の間隔を分割します。長さ2の間隔にはさらに1つの比較(合計3つの比較)が必要です。長さ3の間隔は、長さ1の間隔(ソリューション)と長さ2の間隔に分けることができます。したがって、3つまたは4つの比較が必要です。
除算、浮動小数点演算、高価な対数、整数比較のみ。
コード(長いが高速):
if (n < 100000){
// 5 or less
if (n < 100){
// 1 or 2
if (n < 10)
return 1;
else
return 2;
}else{
// 3 or 4 or 5
if (n < 1000)
return 3;
else{
// 4 or 5
if (n < 10000)
return 4;
else
return 5;
}
}
} else {
// 6 or more
if (n < 10000000) {
// 6 or 7
if (n < 1000000)
return 6;
else
return 7;
} else {
// 8 to 10
if (n < 100000000)
return 8;
else {
// 9 or 10
if (n < 1000000000)
return 9;
else
return 10;
}
}
}
ベンチマーク(JVMウォームアップ後)-ベンチマークがどのように実行されたかを確認するには、以下のコードを参照してください。
完全なコード:
public static void main(String[] args)
throws Exception
{
// validate methods:
for (int i = 0; i < 1000; i++)
if (method1(i) != method2(i))
System.out.println(i);
for (int i = 0; i < 1000; i++)
if (method1(i) != method3(i))
System.out.println(i + " " + method1(i) + " " + method3(i));
for (int i = 333; i < 2000000000; i += 1000)
if (method1(i) != method3(i))
System.out.println(i + " " + method1(i) + " " + method3(i));
for (int i = 0; i < 1000; i++)
if (method1(i) != method4(i))
System.out.println(i + " " + method1(i) + " " + method4(i));
for (int i = 333; i < 2000000000; i += 1000)
if (method1(i) != method4(i))
System.out.println(i + " " + method1(i) + " " + method4(i));
// work-up the JVM - make sure everything will be run in hot-spot mode
allMethod1();
allMethod2();
allMethod3();
allMethod4();
// run benchmark
Chronometer c;
c = new Chronometer(true);
allMethod1();
c.stop();
long baseline = c.getValue();
System.out.println(c);
c = new Chronometer(true);
allMethod2();
c.stop();
System.out.println(c + " = " + StringTools.formatDouble((double)baseline / c.getValue() , "0.00") + " times as fast as baseline");
c = new Chronometer(true);
allMethod3();
c.stop();
System.out.println(c + " = " + StringTools.formatDouble((double)baseline / c.getValue() , "0.00") + " times as fast as baseline");
c = new Chronometer(true);
allMethod4();
c.stop();
System.out.println(c + " = " + StringTools.formatDouble((double)baseline / c.getValue() , "0.00") + " times as fast as baseline");
}
private static int method1(int n)
{
return Integer.toString(n).length();
}
private static int method2(int n)
{
if (n == 0)
return 1;
return (int)(Math.log10(n) + 1);
}
private static int method3(int n)
{
if (n == 0)
return 1;
int l;
for (l = 0 ; n > 0 ;++l)
n /= 10;
return l;
}
private static int method4(int n)
{
if (n < 100000)
{
// 5 or less
if (n < 100)
{
// 1 or 2
if (n < 10)
return 1;
else
return 2;
}
else
{
// 3 or 4 or 5
if (n < 1000)
return 3;
else
{
// 4 or 5
if (n < 10000)
return 4;
else
return 5;
}
}
}
else
{
// 6 or more
if (n < 10000000)
{
// 6 or 7
if (n < 1000000)
return 6;
else
return 7;
}
else
{
// 8 to 10
if (n < 100000000)
return 8;
else
{
// 9 or 10
if (n < 1000000000)
return 9;
else
return 10;
}
}
}
}
private static int allMethod1()
{
int x = 0;
for (int i = 0; i < 1000; i++)
x = method1(i);
for (int i = 1000; i < 100000; i += 10)
x = method1(i);
for (int i = 100000; i < 1000000; i += 100)
x = method1(i);
for (int i = 1000000; i < 2000000000; i += 200)
x = method1(i);
return x;
}
private static int allMethod2()
{
int x = 0;
for (int i = 0; i < 1000; i++)
x = method2(i);
for (int i = 1000; i < 100000; i += 10)
x = method2(i);
for (int i = 100000; i < 1000000; i += 100)
x = method2(i);
for (int i = 1000000; i < 2000000000; i += 200)
x = method2(i);
return x;
}
private static int allMethod3()
{
int x = 0;
for (int i = 0; i < 1000; i++)
x = method3(i);
for (int i = 1000; i < 100000; i += 10)
x = method3(i);
for (int i = 100000; i < 1000000; i += 100)
x = method3(i);
for (int i = 1000000; i < 2000000000; i += 200)
x = method3(i);
return x;
}
private static int allMethod4()
{
int x = 0;
for (int i = 0; i < 1000; i++)
x = method4(i);
for (int i = 1000; i < 100000; i += 10)
x = method4(i);
for (int i = 100000; i < 1000000; i += 100)
x = method4(i);
for (int i = 1000000; i < 2000000000; i += 200)
x = method4(i);
return x;
}
ここでも、ベンチマーク:
編集: ベンチマークを書いた後、Java 6からInteger.toStringにピークを入れました。
final static int [] sizeTable = { 9, 99, 999, 9999, 99999, 999999, 9999999,
99999999, 999999999, Integer.MAX_VALUE };
// Requires positive x
static int stringSize(int x) {
for (int i=0; ; i++)
if (x <= sizeTable[i])
return i+1;
}
私はそれを私の分割統治ソリューションに対してベンチマークしました:
鉱山は、Java 6ソリューションの約4倍の速度です。
n<100000?n<100?n<10?1:2:n<1000?3:n<10000?4:5:n<10000000?n<1000000?6:7:n<100000000?8:n<1000000000?9:10
ベンチマークに関する2つのコメント:Javaは、ジャストインタイムのコンパイルとガベージコレクションなどを備えた複雑な環境であるため、公正な比較を行うために、ベンチマークを実行するときはいつでも、常に(a)2つのテストを囲みます。それらを順番に5回または10回実行するループ内。多くの場合、ループの2番目のパスのランタイムは、最初のものとはかなり異なります。(b)各「アプローチ」の後で、私はSystem.gc()を実行して、ガベージコレクションをトリガーしようとします。そうしないと、最初のアプローチでオブジェクトの束が生成される可能性がありますが、ガベージコレクションを強制するのに十分ではなく、2番目のアプローチではいくつかのオブジェクトが作成され、ヒープが使い果たされ、ガベージコレクションが実行されます。次に、2番目のアプローチは、最初のアプローチで残されたごみを拾うために「課金」されます。とても不公平です!
とはいえ、上記のどちらもこの例では大きな違いはありませんでした。
これらの変更の有無にかかわらず、私はあなたとは非常に異なる結果を得ました。私がこれを実行したとき、はい、toStringアプローチは6400〜6600ミリ秒の実行時間を与えましたが、logアプローチは20,000〜20,400ミリ秒を要しました。ログアプローチは少し高速ではなく、3倍遅くなりました。
2つのアプローチは非常に異なるコストを伴うため、これはまったく衝撃的ではありません。toStringアプローチはクリーンアップする必要のある多くの一時オブジェクトを作成しますが、ログアプローチはより強力な計算を行います。つまり、メモリが少ないマシンでは、toStringに必要なガベージコレクションの回数が増える一方、遅いプロセッサを搭載したマシンでは、ログの余分な計算がさらに面倒になるという違いがあるかもしれません。
私も3番目の方法を試しました。私はこの小さな関数を書きました:
static int numlength(int n)
{
if (n == 0) return 1;
int l;
n=Math.abs(n);
for (l=0;n>0;++l)
n/=10;
return l;
}
これは1600〜1900ミリ秒で実行されました。私のマシンでは、toStringアプローチの1/3未満、ログアプローチの1/10未満です。
数値の範囲が広い場合は、1,000または1,000,000で除算し始めて、ループの回数を減らすことでさらに高速化できます。私はそれで遊んだことがありません。
Javaの使用
int nDigits = Math.floor(Math.log10(Math.abs(the_integer))) + 1;
import java.lang.Math.*;
最初に使用
Cの使用
int nDigits = floor(log10(abs(the_integer))) + 1;
使用する inclue math.h
最初に
the_integer
は無限大になり0
ます。確認してください。
まだコメントを残すことができないので、別の回答として投稿します。
対数ベースのソリューションは、非常に大きな長整数の正しい桁数を計算しません。次に例を示します。
long n = 99999999999999999L;
// correct answer: 17
int numberOfDigits = String.valueOf(n).length();
// incorrect answer: 18
int wrongNumberOfDigits = (int) (Math.log10(n) + 1);
整数の基数10の桁数は1 + truncate(log10(number))なので、次のことができます。
public class Test {
public static void main(String[] args) {
final int number = 1234;
final int digits = 1 + (int)Math.floor(Math.log10(number));
System.out.println(digits);
}
}
編集した私の最後の編集は、記述のコード例を固定ではなくので。
Math.floor
は少し冗長ですよね。キャストするint
と、とにかく丸められます。
マリアンのソリューションは、誰かがそれをコピーして貼り付けたい場合に備えて、長いタイプの数値(最大9,223,372,036,854,775,807)に適応しました。プログラムでこれを書いたのは、10000までの数字の方がはるかに可能性が高かったためです。そのため、特定のブランチを作成しました。とにかく、それは大きな違いはありません。
public static int numberOfDigits (long n) {
// Guessing 4 digit numbers will be more probable.
// They are set in the first branch.
if (n < 10000L) { // from 1 to 4
if (n < 100L) { // 1 or 2
if (n < 10L) {
return 1;
} else {
return 2;
}
} else { // 3 or 4
if (n < 1000L) {
return 3;
} else {
return 4;
}
}
} else { // from 5 a 20 (albeit longs can't have more than 18 or 19)
if (n < 1000000000000L) { // from 5 to 12
if (n < 100000000L) { // from 5 to 8
if (n < 1000000L) { // 5 or 6
if (n < 100000L) {
return 5;
} else {
return 6;
}
} else { // 7 u 8
if (n < 10000000L) {
return 7;
} else {
return 8;
}
}
} else { // from 9 to 12
if (n < 10000000000L) { // 9 or 10
if (n < 1000000000L) {
return 9;
} else {
return 10;
}
} else { // 11 or 12
if (n < 100000000000L) {
return 11;
} else {
return 12;
}
}
}
} else { // from 13 to ... (18 or 20)
if (n < 10000000000000000L) { // from 13 to 16
if (n < 100000000000000L) { // 13 or 14
if (n < 10000000000000L) {
return 13;
} else {
return 14;
}
} else { // 15 or 16
if (n < 1000000000000000L) {
return 15;
} else {
return 16;
}
}
} else { // from 17 to ...¿20?
if (n < 1000000000000000000L) { // 17 or 18
if (n < 100000000000000000L) {
return 17;
} else {
return 18;
}
} else { // 19? Can it be?
// 10000000000000000000L is'nt a valid long.
return 19;
}
}
}
}
}
普通の数学はどうですか?0になるまで10で割ります。
public static int getSize(long number) {
int count = 0;
while (number > 0) {
count += 1;
number = (number / 10);
}
return count;
}
Long.MAX_VALUE
ます。これは、コードの最も複雑なケースでありSystem.nanoTime()
、他のソリューションの最も複雑なケースに対してクロックトライアルを実行するために使用します。++実際には、次の範囲に設定されたランダマイザーで満たされた配列で試してください0
Long.MAX_VALUE
は、「平均的な複雑さ」のテストのためだけ toに ++結果が見つかるかもしれません...非常に衝撃的です。
int,
thisループの場合、最大11回実行されます。あなたの主張の証拠はありますか?
Marian's Solution、現在Ternaryで:
public int len(int n){
return (n<100000)?((n<100)?((n<10)?1:2):(n<1000)?3:((n<10000)?4:5)):((n<10000000)?((n<1000000)?6:7):((n<100000000)?8:((n<1000000000)?9:10)));
}
そのため我々はできます。
好奇心が強い、私はそれをベンチマークしようとした...
import org.junit.Test;
import static org.junit.Assert.*;
public class TestStack1306727 {
@Test
public void bench(){
int number=1000;
int a= String.valueOf(number).length();
int b= 1 + (int)Math.floor(Math.log10(number));
assertEquals(a,b);
int i=0;
int s=0;
long startTime = System.currentTimeMillis();
for(i=0, s=0; i< 100000000; i++){
a= String.valueOf(number).length();
s+=a;
}
long stopTime = System.currentTimeMillis();
long runTime = stopTime - startTime;
System.out.println("Run time 1: " + runTime);
System.out.println("s: "+s);
startTime = System.currentTimeMillis();
for(i=0,s=0; i< 100000000; i++){
b= number==0?1:(1 + (int)Math.floor(Math.log10(Math.abs(number))));
s+=b;
}
stopTime = System.currentTimeMillis();
runTime = stopTime - startTime;
System.out.println("Run time 2: " + runTime);
System.out.println("s: "+s);
assertEquals(a,b);
}
}
結果は次のとおりです。
実行時間1:6765 s:400000000 実行時間2:6000 s:400000000
今、私は私のベンチマークが実際に何かを意味するのかどうか疑問に思っていますが、ベンチマーク自体の複数の実行で一貫した結果(ミリ秒以内の変動)を取得します... :)これを試して最適化するのは役に立たないようです...
編集:ptomliのコメントに続いて、上のコードで「number」を「i」に置き換え、ベンチの5回の実行で次の結果を得ました。
実行時間1:11500 s:788888890 実行時間2:8547 s:788888890 実行時間1:11485 s:788888890 実行時間2:8547 s:788888890 実行時間1:11469 s:788888890 実行時間2:8547 s:788888890 実行時間1:11500 s:788888890 実行時間2:8547 s:788888890 実行時間1:11484 s:788888890 実行時間2:8547 s:788888890
本当に簡単な解決策:
public int numLength(int n) {
for (int length = 1; n % Math.pow(10, length) != n; length++) {}
return length;
}
または、代わりに長さが目的の数値よりも大きいか小さいかを確認できます。
public void createCard(int cardNumber, int cardStatus, int customerId) throws SQLException {
if(cardDao.checkIfCardExists(cardNumber) == false) {
if(cardDao.createCard(cardNumber, cardStatus, customerId) == true) {
System.out.println("Card created successfully");
} else {
}
} else {
System.out.println("Card already exists, try with another Card Number");
do {
System.out.println("Enter your new Card Number: ");
scan = new Scanner(System.in);
int inputCardNumber = scan.nextInt();
cardNumber = inputCardNumber;
} while(cardNumber < 95000000);
cardDao.createCard(cardNumber, cardStatus, customerId);
}
}
}
乗算ベースのソリューションはまだ見ていません。対数、除算、および文字列ベースのソリューションは、何百万ものテストケースに対してかなり扱いにくくなるため、以下を対象としますints
。
/**
* Returns the number of digits needed to represents an {@code int} value in
* the given radix, disregarding any sign.
*/
public static int len(int n, int radix) {
radixCheck(radix);
// if you want to establish some limitation other than radix > 2
n = Math.abs(n);
int len = 1;
long min = radix - 1;
while (n > min) {
n -= min;
min *= radix;
len++;
}
return len;
}
基数10では、これは機能します。これは、nが本質的に9、99、999と比較されるためです。
残念ながら、これはオーバーフローによるlong
すべてのインスタンスを置き換えるだけでは移植できませんint
。一方で、それはたまたま起こりますだろう拠点2と10のために働く(がひどく、他の拠点のほとんどのために失敗しました)。オーバーフローポイントのルックアップテーブル(または除算テスト... ew)が必要です
/**
* For radices 2 &le r &le Character.MAX_VALUE (36)
*/
private static long[] overflowpt = {-1, -1, 4611686018427387904L,
8105110306037952534L, 3458764513820540928L, 5960464477539062500L,
3948651115268014080L, 3351275184499704042L, 8070450532247928832L,
1200757082375992968L, 9000000000000000000L, 5054470284992937710L,
2033726847845400576L, 7984999310198158092L, 2022385242251558912L,
6130514465332031250L, 1080863910568919040L, 2694045224950414864L,
6371827248895377408L, 756953702320627062L, 1556480000000000000L,
3089447554782389220L, 5939011215544737792L, 482121737504447062L,
839967991029301248L, 1430511474609375000L, 2385723916542054400L,
3902460517721977146L, 6269893157408735232L, 341614273439763212L,
513726300000000000L, 762254306892144930L, 1116892707587883008L,
1617347408439258144L, 2316231840055068672L, 3282671350683593750L,
4606759634479349760L};
public static int len(long n, int radix) {
radixCheck(radix);
n = abs(n);
int len = 1;
long min = radix - 1;
while (n > min) {
len++;
if (min == overflowpt[radix]) break;
n -= min;
min *= radix;
}
return len;
}
設計あり(問題に基づく)。これは、分割統治の代替手段です。最初に列挙型を定義します(それがunsigned int専用であることを考慮して)。
public enum IntegerLength {
One((byte)1,10),
Two((byte)2,100),
Three((byte)3,1000),
Four((byte)4,10000),
Five((byte)5,100000),
Six((byte)6,1000000),
Seven((byte)7,10000000),
Eight((byte)8,100000000),
Nine((byte)9,1000000000);
byte length;
int value;
IntegerLength(byte len,int value) {
this.length = len;
this.value = value;
}
public byte getLenght() {
return length;
}
public int getValue() {
return value;
}
}
次に、列挙型の値を通過するクラスを定義し、適切な長さを比較して返します。
public class IntegerLenght {
public static byte calculateIntLenght(int num) {
for(IntegerLength v : IntegerLength.values()) {
if(num < v.getValue()){
return v.getLenght();
}
}
return 0;
}
}
このソリューションの実行時間は、分割統治アプローチと同じです。
num>=Nine.getValue()
?
たいていの場合、これを「提示」したいという理由でこれを実行したいと考えています。つまり、最終的に明示的または暗黙的に最終的に「toString-ed」(または別の方法で変換)する必要があるということです。提示する前(たとえば印刷)。
その場合は、必要な「toString」を明示的にして、ビットを数えるようにしてください。
再帰ループを使用してこれを実現できます
public static int digitCount(int numberInput, int i) {
while (numberInput > 0) {
i++;
numberInput = numberInput / 10;
digitCount(numberInput, i);
}
return i;
}
public static void printString() {
int numberInput = 1234567;
int digitCount = digitCount(numberInput, 0);
System.out.println("Count of digit in ["+numberInput+"] is ["+digitCount+"]");
}
int num = 02300;
int count = 0;
while(num>0){
if(num == 0) break;
num=num/10;
count++;
}
System.out.println(count);
簡単な再帰的な方法
int get_int_lenght(current_lenght, value)
{
if (value / 10 < 10)
return (current_lenght + 1);
return (get_int_lenght(current_lenght + 1, value))
}
未検証
10による連続除算を使用して、数字を表示することができます。
int a=0;
if (no < 0) {
no = -no;
} else if (no == 0) {
no = 1;
}
while (no > 0) {
no = no / 10;
a++;
}
System.out.println("Number of digits in given number is: "+a);
これは私が作った本当に簡単な方法で、どんな数でも機能します:
public static int numberLength(int userNumber) {
int numberCounter = 10;
boolean condition = true;
int digitLength = 1;
while (condition) {
int numberRatio = userNumber / numberCounter;
if (numberRatio < 1) {
condition = false;
} else {
digitLength++;
numberCounter *= 10;
}
}
return digitLength;
}
それが機能する方法は、数値カウンタ変数を使用することで、10 = 1桁のスペースです。たとえば、.1 = 1/10 => 1桁のスペース。したがって、int number = 103342;
6を取得すると、.000001スペースに相当するため、6になります。また、誰かがより良い変数名を持っていますnumberCounter
か?これ以上のことは考えられません。
編集:ちょうどより良い説明を考えました。基本的に、このwhileループが行っていることは、数値が1未満になるまで10で除算することです。基本的に、何かを10で割ると、それは1つの数値スペースだけ戻ります。そのため、数値の桁数が1未満になるまで10で割るだけです。
以下は、数値を小数でカウントできる別のバージョンです。
public static int repeatingLength(double decimalNumber) {
int numberCounter = 1;
boolean condition = true;
int digitLength = 1;
while (condition) {
double numberRatio = decimalNumber * numberCounter;
if ((numberRatio - Math.round(numberRatio)) < 0.0000001) {
condition = false;
} else {
digitLength++;
numberCounter *= 10;
}
}
return digitLength - 1;
}
変換してみint型に文字列、その後の長さを取得する文字列を。これでintの長さが得られます。
public static int intLength(int num){
String n = Integer.toString(num);
int newNum = n.length();
return newNum;
}
number
ネガティブな場合は見逃します。