Способ получить количество цифр в int?
есть ли более аккуратный способ получить длину int как это?
int length = String.valueOf(1000).length();
27 ответов
ваше строковое решение совершенно нормально, в нем нет ничего "не аккуратного". Вы должны понимать, что математически числа не имеют длины и не имеют цифр. Длина и цифры являются свойствами a физическое представление числа в определенной базе, т. е. строки.
решение на основе логарифма делает (некоторые из) То же самое, что и строковое решение, и, вероятно, делает это (незначительно) быстрее, потому что оно только создает длину и игнорирует цифры. Но я бы на самом деле не считал это более ясным намерением - и это самый важный фактор.
логарифм-ваш друг:
int n = 1000;
int length = (int)(Math.log10(n)+1);
NB: действует только для n > 0.
самый быстрый подход: разделяй и властвуй.
предполагая, что ваш диапазон от 0 до MAX_INT, у вас есть от 1 до 10 цифр. Вы можете подойти к этому интервалу, используя divide и conquer, С до 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;
}
}
}
Benchmark (после прогрева JVM) - см. код ниже, чтобы увидеть, как был запущен бенчмарк:
- базовый метод (со строкой.длина): 2145ms
- метод log10: 711ms = 3.02 раз быстрее, чем базовый
- повторное деление: 2797ms = 0,77 раза быстрее, чем базовый "разделяй и властвуй": = 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;
}
опять же, ориентир:
- базовый метод (со строкой.длина): 2145ms
- метод log10: 711ms = 3,02 раза быстрее, чем базовый
- повторное деление: 2797ms = 0,77 раза быстрее, чем базовая линия "разделяй и властвуй": = 28.99
раз быстрее, чем исходная
Edit: После того, как я написал бенчмарк, я взял пик подхалима в Integer.toString из Java 6, и я обнаружил, что он использует:
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
шахты составляет около В 4 раза быстрее.
два комментария к вашему бенчмарку: Java-сложная среда, что с компиляцией just-in-time и сбором мусора и т. д., Поэтому, чтобы получить справедливое сравнение, всякий раз, когда я запускаю бенчмарк, я всегда: (a) заключаю два теста в цикл, который запускает их в последовательности 5 или 10 раз. Довольно часто время выполнения на втором проходе через цикл сильно отличается от первого. И (Б) после каждого "подхода" я делаю систему.gc (), чтобы попытаться запустить сборку мусора. В противном случае, первый подход может генерировать кучу объектов, но недостаточно для принудительной сборки мусора, затем второй подход создает несколько объектов, куча исчерпывается, и сборка мусора запускается. Затем второй подход "заряжается" за сбор мусора, оставленного первым подходом. Очень несправедливо!
тем не менее, ни то, ни другое не имело существенного значения в этом примере.
С этими изменениями или без них я получил совсем другие результаты, чем вы. Когда я побежал, Да, метод toString подход дал время от 6400 до 6600 Миллис, в то время как подход журнала обеспечить 20 000 до 20,400 Миллис. Вместо того, чтобы быть немного быстрее, подход к журналу был в 3 раза медленнее для меня.
обратите внимание, что два подхода связаны с очень разными затратами, поэтому это не совсем шокирует: подход toString создаст много временных объектов, которые должны быть очищены, в то время как подход log требует более интенсивных вычислений. Так что, возможно, разница в том, что на машине с меньшей памятью 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 Миллис - менее 1/3 подхода toString и 1/10 подхода log на моей машине.
Если бы у вас был широкий диапазон чисел, вы могли бы ускорить его, начав деления на 1000 или 1000000, чтобы уменьшить количество раз через петлю. Я с этим не играл.
Так как количество цифр в базе 10 целого числа просто 1 + усечение (log10 (число)), вы можете сделать:
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);
}
}
редактировать потому что мое последнее редактирование исправило пример кода, но не описание.
решение Мэриан адаптировано для долго тип чисел (до 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;
}
}
}
}
}
Использование 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
в начале
можно попробовать? ;)
на основании решения
final int digits = number==0?1:(1 + (int)Math.floor(Math.log10(Math.abs(number))));
пока не могу оставить комментарий, поэтому я опубликую его как отдельный ответ.
решение на основе логарифма не вычисляет правильное количество цифр для очень больших длинных целых чисел, например:
long n = 99999999999999999L;
// correct answer: 17
int numberOfDigits = String.valueOf(n).length();
// incorrect answer: 18
int wrongNumberOfDigits = (int) (Math.log10(n) + 1);
решение на основе логарифма вычисляет неправильное количество цифр в больших целых числах
Как насчет простой старой математики? Делите на 10, пока не достигнете 0.
public static int getSize(long number) {
int count = 0;
while (number > 0) {
count += 1;
number = (number / 10);
}
return count;
}
решение Мэриан, теперь с тройным:
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);
}
}
результаты :
Run time 1: 6765 s: 400000000 Run time 2: 6000 s: 400000000
Теперь мне остается задаться вопросом, действительно ли мой бенчмарк что-то значит, но я получаю последовательные результаты (вариации в ms) за несколько запусков самого бенчмарка ... :) Похоже, бесполезно пытаться оптимизировать это...
edit: после комментария ptomli Я заменил "номер" на "i" в коде выше и получил следующие результаты за 5 запусков скамейка:
Run time 1: 11500 s: 788888890 Run time 2: 8547 s: 788888890 Run time 1: 11485 s: 788888890 Run time 2: 8547 s: 788888890 Run time 1: 11469 s: 788888890 Run time 2: 8547 s: 788888890 Run time 1: 11500 s: 788888890 Run time 2: 8547 s: 788888890 Run time 1: 11484 s: 788888890 Run time 2: 8547 s: 788888890
Как насчет этого рекурсивного метода?
private static int length = 0;
public static int length(int n) {
length++;
if((n / 10) < 10) {
length++;
} else {
length(n / 10);
}
return length;
}
простое решение:
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);
}
очень простое решение:
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... как минимум 9, 90, 900... и n вычитается на 9, 90, 900...
к сожалению, это не портативный long
просто заменив каждый экземпляр int
из-за переполнения. С другой стороны, так уж получилось!--10-->будет работа для баз 2 и 10 (но плохо работает для большинства других баз). Вам понадобится таблица поиска для точек переполнения (или тест деления... фу)
/**
* 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;
}
}
время выполнения этого решения совпадает с подходом "разделяй и властвуй".
введите номер и создать Arraylist
, и цикл while запишет все цифры в Arraylist
. Затем мы можем вынуть размер массива, который будет длиной введенного вами целочисленного значения.
ArrayList<Integer> a=new ArrayList<>();
while(number > 0)
{
remainder = num % 10;
a.add(remainder);
number = number / 10;
}
int m=a.size();
простой рекурсивный способ
int get_int_lenght(current_lenght, value)
{
if (value / 10 < 10)
return (current_lenght + 1);
return (get_int_lenght(current_lenght + 1, value))
}
Не проверял
мы можем достичь этого, используя рекурсивный цикл
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+"]");
}
другой строкой подход. Короткий и сладкий - для любого целого числа n
.
int length = ("" + n).length();
int num = 02300;
int count = 0;
while(num>0){
if(num == 0) break;
num=num/10;
count++;
}
System.out.println(count);
вот очень простой метод, который я сделал, который работает для любого числа:
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 десятая => 1 цифра пространства. Поэтому если у вас есть int number = 103342;
вы получите 6, потому что это эквивалент .Обратно 000001 пространства. Кроме того, у кого-нибудь есть лучшее имя переменной для numberCounter
? Я не могу придумать ничего лучше.
Edit: просто подумал о лучшем объяснении. По сути этот цикл while делает это так, что вы делите свое число на 10, пока оно не станет меньше единицы. По сути, когда вы делите что-то на 10, вы перемещаете его на одно числовое пространство, поэтому вы просто делите его на 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;
}
один хочет сделать это в основном потому, что он хочет "представить" его, что в основном означает, что он, наконец, должен быть "toString-ed" (или преобразован другим способом) явно или неявно; прежде чем он может быть представлен (напечатан, например).
Если это так, то просто попробуйте сделать необходимый "toString" явным и подсчитать биты.
вы могли бы цифры, используя последовательное деление на десять:
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);
попробуйте преобразовать int до строка а затем получить длину строка. Это должно получить длину int.
public static int intLength(int num){
String n = Integer.toString(num);
int newNum = n.length();
return newNum;
}