Есть ли более аккуратный способ получения длины типа 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);
Примечание: действует только при 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;
}
Опять же, ориентир:
Изменить: После того, как я написал тест, я взял краткий обзор 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;
}
Я сравнил его с моим решением «разделяй и властвуй»:
Мой примерно в 4 раза быстрее, чем решение Java 6.
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 - это сложная среда, которая включает в себя своевременную компиляцию, сборку мусора и так далее, поэтому, чтобы получить справедливое сравнение, всякий раз, когда я запускаю тест, я всегда: (a) заключаю два теста в цикле, который запускает их в последовательности 5 или 10 раз. Довольно часто время выполнения на втором проходе цикла сильно отличается от первого. И (б) После каждого «подхода» я делаю 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 миллисекунд - менее 1/3 от подхода toString и 1/10 от лог-подхода на моей машине.
Если бы у вас был широкий диапазон чисел, вы могли бы ускорить его, начав делить на 1000 или 1000000, чтобы уменьшить количество циклов. Я не играл с этим.
Использование 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
в начале
the_integer
есть 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 + усечение (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);
}
}
Отредактировано, потому что мое последнее редактирование исправило пример кода, но не описание.
Math.floor
немного избыточно, не так ли? Кастинг int
будет округлять его в любом случае.
Решение Мариана адаптировано для длинных номеров (до 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;
}
}
}
}
}
Как насчет простой старой математики? Разделите на 10, пока не достигнете 0.
public static int getSize(long number) {
int count = 0;
while (number > 0) {
count += 1;
number = (number / 10);
}
return count;
}
Long.MAX_VALUE
, который является наихудшим вариантом сложности вашего кода, и используйте его System.nanoTime()
для тестирования синхронизации с наихудшими случаями сложности другого решения. ++ На самом деле, попробуйте это массив , заполненный набором рандомизер в диапазоне 0
до Long.MAX_VALUE
тоже, только для «средней сложности» тестирования ++ Вы можете найти результаты ... очень шокирует.
int,
этого цикла выполняется максимум 11 раз. У вас есть доказательства ваших утверждений?
Решение Мариана, теперь с Тернарием:
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 с: 400000000 Время выполнения 2: 6000 с: 400000000
Теперь мне остается только задаться вопросом, действительно ли мой эталон что-то значит, но я получаю согласованные результаты (вариации в течение мс) за несколько прогонов самого эталона ... :) Похоже, бесполезно пытаться оптимизировать это ...
edit: после комментария ptomli я заменил «число» на «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
Как насчет этого рекурсивного метода?
private static int length = 0;
public static int length(int n) {
length++;
if((n / 10) < 10) {
length++;
} else {
length(n / 10);
}
return length;
}
Действительно простое решение:
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
за переполнения. С другой стороны, так получилось, что он будет работать для баз 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;
}
С дизайном (на основе проблемы). Это альтернатива разделяй и властвуй. Сначала мы определим перечисление (учитывая, что это только для беззнакового целого).
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+"]");
}
Я написал эту функцию после просмотра 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;
}
}
}
Я вижу людей, использующих библиотеки String или даже использующих класс Integer. Ничего плохого в этом нет, но алгоритм получения количества цифр не так уж и сложен. Я использую long в этом примере, но он работает так же хорошо с int.
private static int getLength(long num) {
int count = 1;
while (num >= 10) {
num = num / 10;
count++;
}
return count;
}
нет String API, нет утилит, нет преобразования типов, просто чистая Java-итерация ->
public static int getNumberOfDigits(int input) {
int numOfDigits = 1;
int base = 1;
while (input >= base * 10) {
base = base * 10;
numOfDigits++;
}
return numOfDigits;
}
Вы можете долго идти на большие значения, если хотите.
Простой рекурсивный способ
int get_int_lenght(current_lenght, value)
{
if (value / 10 < 10)
return (current_lenght + 1);
return (get_int_lenght(current_lenght + 1, value))
}
не проверено
Вы могли бы цифры, используя последовательное деление на десять:
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);
Введите число и создайте Arraylist
, и цикл while запишет все цифры в Arraylist
. Затем мы можем вывести размер массива, который будет длиной целочисленного значения, которое вы ввели.
ArrayList<Integer> a=new ArrayList<>();
while(number > 0)
{
remainder = num % 10;
a.add(remainder);
number = number / 10;
}
int m=a.size();
Вот действительно простой метод, который я сделал, который работает для любого числа:
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
? Я не могу придумать ничего лучшего.
Изменить: Просто подумал о лучшем объяснении. По сути, этот цикл while делает так, что вы делите свое число на 10, пока оно не станет меньше единицы. По сути, когда вы делите что-то на 10, вы перемещаете его назад на один номер, поэтому вы просто делите это на 10, пока не достигнете <1 для количества цифр в вашем номере.
Вот еще одна версия, которая может считать количество чисел в десятичном виде:
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;
}
Попробуйте преобразовать int в строку, а затем получите длину строки . Это должно получить длину Int .
public static int intLength(int num){
String n = Integer.toString(num);
int newNum = n.length();
return newNum;
}
number
отрицательно.