이 방법보다 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 예열 후)-벤치 마크 실행 방법을 보려면 아래 코드를 참조하십시오.
- 기본 방법 (String.length 포함) : 2145ms
- log10 방법 : 기준선의 711ms = 3.02 배
- 반복 분할 : 기준선보다 2797ms = 0.77 배
- 분할 정복 :
기준선보다 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;
}
다시, 벤치 마크 :
- 기본 방법 (String.length 포함) : 2145ms
- log10 방법 : 기준선의 711ms = 3.02 배
- 반복 분할 : 기준선보다 2797ms = 0.77 배
- 분할 정복 :
기준선보다 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;
}
분할 및 정복 솔루션과 비교하여 벤치마킹했습니다.
- 분할 정복 : 104ms
- 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);
}
}
마지막 편집으로 코드 예제가 수정되었지만 설명이 수정되었으므로 수정 되었습니다.