web-dev-qa-db-ja.com

Intの桁数を取得する方法は?

このメソッドよりもintの長さを取得するためのより良い方法はありますか?

int length = String.valueOf(1000).length();
349
fnst

あなたの文字列ベースの解決策は完全にOKです、それに関して「不公平な」ものは何もありません。あなたは数学的に、数字は長さを持たず、数字も持たないことを理解しなければなりません。長さと数字は両方とも、 物理的表現 特定の基数、つまり文字列内の数値のプロパティです。

対数ベースの解決策は、文字列ベースの解決策と内部的に同じことを(一部)実行しますが、長さだけを生成して桁数を無視するので(おそらくわずかに)高速になります。しかし、私は実際にそれを意図的に明確にするとは思わないでしょう - そしてそれが最も重要な要素です。

314

対数はあなたの友達です:

int n = 1000;
int length = (int)(Math.log10(n)+1);

注意:n> 0の場合のみ有効です。

248
Dmitry Brant

最速のアプローチ:分割と征服。

範囲が0からMAX_INTであると仮定すると、1から10桁です。各入力につき最大4つの比較を行いながら、分割統治を使用してこの間隔に近づくことができます。まず、[1..10]を1回の比較で[1..5]と[6..10]に分割し、次に1回の比較を使用して、長さ5の区間をそれぞれ長さ3と長さ2の区間に分割します。長さ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ウォームアップ後) - ベンチマークの実行方法を確認するには、以下のコードを参照してください。

  1. ベースラインメソッド(String.lengthを含む):2145ms
  2. log10法:711ms =ベースラインより3.02倍速い
  3. 繰り返し除算:2797ms =ベースラインより0.77倍高速
  4. 分割統治:74ミリ秒= 28.99
    ベースラインより倍速い

フルコード:

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 faster than baseline");

    c = new Chronometer(true);
    allMethod3();
    c.stop();
    System.out.println(c + " = " + StringTools.formatDouble((double)baseline / c.getValue() , "0.00") + " times faster than baseline");

    c = new Chronometer(true);
    allMethod4();
    c.stop();
    System.out.println(c + " = " + StringTools.formatDouble((double)baseline / c.getValue() , "0.00") + " times faster than 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;
}

ベンチマーク:

  1. ベースラインメソッド(String.lengthを含む):2145ms
  2. log10法:711ms =ベースラインより3.02倍速い
  3. 繰り返し除算:2797ms =ベースラインより0.77倍高速
  4. 分割統治:74ミリ秒= 28.99
    ベースラインより倍速い

編集: 私がベンチマークを書いた後、私は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;
}

私はそれを私の分割統治法と照らし合わせてベンチマークしました。

  1. 分割統治:104ミリ秒
  2. Java 6ソリューション - 繰り返しと比較:406ms

私のものは約4倍速いです。

149
Marian

ベンチマークについての2つのコメント:Javaは複雑な環境です。ジャストインタイムコンパイルやガベージコレクションなどがあるので、ベンチマークを実行するときは常に公平な比較をするために、常に(a)2つのテストを囲みます。 5〜10回連続して実行するループ内。多くの場合、2回目のループの実行時間は最初のものとはかなり異なります。そして(b)それぞれの "アプローチ"の後に、ガベージコレクションを引き起こすためにSystem.gc()を実行します。それ以外の場合、最初の方法では大量のオブジェクトが生成される可能性がありますが、ガベージコレクションを強制するのに十分ではありません。2番目の方法ではいくつかのオブジェクトが作成され、ヒープが使い果たされ、ガベージコレクションが実行されます。次に、2番目のアプローチは、最初のアプローチで残されたゴミを拾うことに対して「請求」されます。非常に不公平です!

とはいえ、上記のどちらでもこの例では大きな違いはありません。

これらの変更の有無にかかわらず、私はあなたがしたものとは非常に異なる結果を得ました。これを実行したとき、はい、toStringのアプローチは6400から6600ミリの実行時間を与えましたが、logのアプローチは20,000から20,400ミリを占めていました。わずかに速いのではなく、対数アプローチは3倍遅くなりました。

2つのアプローチは非常に異なるコストを伴うので、これはまったく衝撃的ではありません:toStringアプローチはクリーンアップしなければならない一時オブジェクトをたくさん作成しますが、logアプローチはより多くの計算を必要とします。そのため、おそらくメモリが少ないマシンではtoStringがより多くのガベージコレクションラウンドを必要としますが、プロセッサが遅いマシンではlogの余分な計算がもっと面倒になるでしょう。

私はまた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以下、そしてlogアプローチの1/10以下でした。

あなたが広範囲の数を持っていたならば、あなたはループを通る回数を減らすために1,000または1,000,000で割ることから始めることによってそれをさらにスピードアップすることができます。私はそれで遊んでいません。

11
Jay

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を使う

9
Santosh

整数の基数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);
    }
}

編集済み 最後の編集でコード例は修正されたが説明は修正されなかったため。

8
Dirk

まだコメントを残すことができないので、私は別の答えとして投稿するつもりです。

対数ベースのソリューションでは、非常に大きい長整数に対して正しい桁数が計算されません。次に例を示します。

long n = 99999999999999999L;

// correct answer: 17
int numberOfDigits = String.valueOf(n).length();

// incorrect answer: 18
int wrongNumberOfDigits = (int) (Math.log10(n) + 1); 

対数ベースのソリューションは、大きな整数で誤った桁数を計算します

7
moodcheerful

誰かがそれをコピー&ペーストしたい場合に備えて、Marianのソリューションは long タイプ番号(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;
                }
            }
        }
    }
}
5
J.A.I.L.

普通の数学はどうですか? 0になるまで10で割ります。

public static int getSize(long number) {
        int count = 0;
        while (number > 0) {
            count += 1;
            number = (number / 10);
        }
        return count;
    }
3
Sinista

試すことができますか? ;)

dirkのソリューションに基づく

final int digits = number==0?1:(1 + (int)Math.floor(Math.log10(Math.abs(number))));
3
DmitryK

別の文字列アプローチ短くて甘い - 任意の整数n

int length = ("" + n).length();
2
ThisClark

Marianの解決法、今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)));
    }

できるから。

2
UserNotFound

興味津々、私はそれをベンチマークしようとしました...

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]実行時間1:11500 [1] s:788888890 [2]実行時間2:8547 [1] s:788888890 [1]実行時間1:1 11485 
 s:788888890 
実行時間2:8547 
 s:788888890 
 
実行時間1:11469 
 s:788888890 [2]実行時間2:8547 [1] s:788888890 [1]実行時間1:11500 [2] s:788888890 [2]実行時間2: 8547 
 s:788888890 
 
実行時間1:11484 
 s:788888890 
実行時間2:8547 
 s:788888890 
1
Jean

乗算ベースの解決策はまだ見たことがありません。対数、除数、および文字列ベースのソリューションは、何百万ものテストケースに対してかなり扱いにくくなるため、intsname__には次のようにします。

/**
 * 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進法では、minが9、90、900 ...でnが9、90、900 ...で減算されるので、nは9、99、999 ...と本質的に比較されているので、これはうまくいきます。

あいにく、これはlongname__に移植可能ではありません。オーバーフローのため、intname__のすべてのインスタンスを置き換えるだけです。他方で、それはまさしくそのように起こります 意志 / 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;
}
0
Jonathan Smith

デザイン付き(問題あり)。これは分割統治の代替案です。最初にenumを定義します(これは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;
    }
}

今度はenumの値を通過するクラスを定義し、適切な長さを比較して返します。

public class IntegerLenght {
    public static byte calculateIntLenght(int num) {    
        for(IntegerLength v : IntegerLength.values()) {
            if(num < v.getValue()){
                return v.getLenght();
            }
        }
        return 0;
    }
}

このソリューションの実行時間は、分割統治アプローチと同じです。

0
androider

代わりに、長さはあなたが数が希望の数より大きいか小さいかどうかを確認することができます。

    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);
    }
}

}

0
Szabi Zsoldos

これをしたいのは主に彼/彼女がそれを "提示"したいからです。それは大抵それがとにかく明示的または黙示的に "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+"]");
    }
0
ericdemo07

この関数は、Integer.Javaソースコードを見てから作成しました。

private static int stringSize(int x) {
    final int[] sizeTable = {9, 99, 999, 9_999, 99_999, 999_999, 9_999_999,
            99_999_999, 999_999_999, Integer.MAX_VALUE};
    for (int i = 0; ; ++i) {
        if (x <= sizeTable[i]) {
            return i + 1;
        }
    }
}
0
shellhub

本当に簡単な解決策:

public int numLength(int n) {
  for (int length = 1; n % Math.pow(10, length) != n; length++) {}
  return length;
}
0
VoidCatz

この再帰的な方法はどうですか?

    private static int length = 0;

    public static int length(int n) {
    length++;
    if((n / 10) < 10) {
        length++;
    } else {
        length(n / 10);
    }
    return length;
}
0
Jedi Dula

簡単な解決策:

public class long_length {
    long x,l=1,n;
    for (n=10;n<x;n*=10){
        if (x/n!=0){
            l++;
        }
    }
    System.out.print(l);
}
0
mikegh