[java] int에서 자릿수를 얻는 방법?

이 방법보다 int의 길이를 얻는 더 좋은 방법이 있습니까?

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



답변

문자열 기반 솔루션은 완벽하게 괜찮습니다. “정확하지 않은”것은 없습니다. 수학적으로 숫자는 길이가없고 숫자도 없다는 것을 알아야합니다. 길이와 숫자는 둘 다 특정 밑 (즉, 문자열)에서 숫자 의 물리적 표현 의 속성입니다 .

로그 기반 솔루션은 문자열 기반 솔루션과 내부적으로 동일한 작업을 수행하며 길이 만 생성하고 숫자를 무시하기 때문에 (중요하지 않게) 더 빠릅니다. 그러나 실제로 의도가 더 명확하다고 생각하지는 않습니다. 이것이 가장 중요한 요소입니다.


답변

로그는 당신의 친구입니다 :

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

NB : n> 0에만 유효합니다.


답변

가장 빠른 방법 : 나누고 정복하십시오.

범위가 0 ~ MAX_INT라고 가정하면 1 ~ 10 자리 숫자입니다. 나누기와 정복을 사용하여이 간격에 접근 할 수 있으며 각 입력 당 최대 4 개의 비교가 가능합니다. 먼저 [1..10]을 [1..5]와 [6..10]으로 한 번의 비교로 나눈 다음 각 길이 5 간격을 하나의 비교를 사용하여 하나의 길이 3과 하나의 길이 2 간격으로 나눕니다. 길이 2 구간은 하나 이상의 비교 (전체 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. 분할 정복 :
    기준선보다 74ms = 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 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;
}

다시, 벤치 마크 :

  1. 기본 방법 (String.length 포함) : 2145ms
  2. log10 방법 : 기준선의 711ms = 3.02 배
  3. 반복 분할 : 기준선보다 2797ms = 0.77 배
  4. 분할 정복 :
    기준선보다 74ms = 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. 분할 정복 : 104ms
  2. Java 6 솔루션-반복 및 비교 : 406ms

광산은 Java 6 솔루션보다 약 4 배 빠릅니다.


답변

벤치 마크에 대한 두 가지 의견 : Java는 복잡한 환경입니다 .Just-in-Time 컴파일 및 가비지 수집 등으로 벤치 마크를 실행할 때마다 공정한 비교를 얻으려면 항상 : (a) 두 테스트를 동봉하십시오. 루프를 순서대로 5 회 또는 10 회 실행합니다. 루프를 통한 두 번째 패스의 런타임은 첫 번째와 상당히 다릅니다. 그리고 (b) 각각의 “접근법”후에 System.gc ()를 수행하여 가비지 수집을 시작합니다. 그렇지 않으면, 첫 번째 접근 방식은 많은 오브젝트를 생성 할 수 있지만 가비지 콜렉션을 강제 실행하기에는 충분하지 않으며, 두 번째 접근 방식은 오브젝트를 생성하고 힙이 소진되며 가비지 콜렉션이 실행됩니다. 그런 다음 두 번째 방법은 첫 번째 방법으로 남은 쓰레기를 수거하기 위해 “충전”됩니다. 매우 불공평하다!

즉, 위의 어느 것도이 예에서 큰 차이를 만들지 않았습니다.

이러한 수정 여부에 관계없이 귀하와는 다른 결과를 얻었습니다. 내가 이것을 실행했을 때, 예, toString 접근 방식은 6400 ~ 6600 밀리미터의 실행 시간을 제공했지만 로그 접근 방식은 20,000 ~ 20,400 밀리미터를 차지했습니다. 약간 더 빠르지 않고 로그 접근 방식이 3 배 느 렸습니다.

두 접근 방식은 비용이 매우 다르므로 완전히 충격을주지는 않습니다. toString 접근 방식은 정리해야하는 많은 임시 객체를 생성하는 반면 로그 접근 방식은 더 많은 계산을 수행합니다. 따라서 메모리가 적은 컴퓨터에서는 toString에 더 많은 가비지 콜렉션 라운드가 필요하지만 프로세서가 느린 컴퓨터에서는 로그 계산이 더 어려워 질 수 있습니다.

또한 세 번째 접근법을 시도했습니다. 나는이 작은 기능을 썼다 :

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으로 나누기 시작하여 루프를 통과하는 횟수를 줄임으로써 속도를 더 높일 수 있습니다. 나는 그와 함께 연주하지 않았습니다.


답변

자바 사용하기

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초기에


답변

아직 댓글을 남길 수 없으므로 별도의 답변으로 게시하겠습니다.

로그 기반 솔루션은 다음과 같이 매우 큰 정수에 대해 올바른 자릿수를 계산하지 않습니다.

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

마지막 편집으로 코드 예제가 수정되었지만 설명이 수정되었으므로 수정 되었습니다.