이 방법보다 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);
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 예열 후)-벤치 마크 실행 방법을 보려면 아래 코드를 참조하십시오.
전체 코드 :
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
벤치 마크에 대한 두 가지 의견 : 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
초기에
the_integer
is 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
어쨌든 반올림하여 캐스팅 합니다.
Marian의 솔루션은 누군가가 복사 및 붙여 넣기를 원할 경우 긴 유형 번호 (최대 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
코드의 최악의 복잡한 경우 인 200 만 개의 숫자 배열을 만들고 System.nanoTime()
다른 솔루션의 최악의 복잡한 경우에 대해 클럭킹 시험을 수행하는 데 사용 하십시오. ++ 실제로, "평균 복잡성"테스트를 위해 무작위 범위를 0
~로 설정 한 배열로 배열을 시도하십시오. Long.MAX_VALUE
++ 결과는 매우 충격적입니다.
int,
이 루프는 11 시간에서 최대를 행한다. 당신의 주장에 대한 증거가 있습니까?
Ternary와 함께하는 Marian의 솔루션 :
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
이제 벤치 마크가 실제로 의미가 있는지 궁금해하지만 벤치 마크 자체의 여러 실행에 대해 일관된 결과 (ms 이내의 변화)를 얻습니다 ... :) 이것을 시도하고 최적화하는 것이 쓸모없는 것처럼 보입니다 ...
편집 : 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 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와 비교되고 있기 때문에 작동합니다. 분은 9, 90, 900 ...이고 n은 9, 90, 900으로 빼기 때문입니다 ...
불행히도 이것은 오버플로로 인한 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;
}
디자인 (문제에 따라). 이것은 분할 및 정복의 대안입니다. 우리는 먼저 열거 형을 정의 할 것입니다 (서명없는 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 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 공백과 같습니다. 또한 누구나 더 나은 변수 이름을 가지고 numberCounter
있습니까? 나는 더 나은 것을 생각할 수 없다.
편집 : 더 나은 설명을 생각했습니다. 본질적 으로이 while 루프가하는 일은 1보다 작을 때까지 숫자를 10으로 나누는 것입니다. 기본적으로 무언가를 10으로 나누면 하나의 숫자 공간으로 이동하므로 숫자의 자릿수가 <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;
}