Есть ли более точный способ получения длины типа int, чем этот метод?
int length = String.valueOf(1000).length();
Есть ли более точный способ получения длины типа int, чем этот метод?
int length = String.valueOf(1000).length();
Ваше решение на основе String совершенно нормально, в нем нет ничего "неосторожного". Вы должны понимать, что математически цифры не имеют длины, и у них нет цифр. Длина и цифры - оба свойства физического представления числа в конкретной базе, то есть строка.
Решение на основе логарифма делает (некоторые из) те же самые вещи, что и на основе String, и, вероятно, делает это (незначительно) быстрее, потому что оно производит только длину и игнорирует цифры. Но я бы на самом деле не считал это более ясным в намерениях - и это самый важный фактор.
Логарифм - ваш друг:
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;
}
}
}
Бенчмарк (после разгона 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 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;
}
Опять же, контрольный показатель:
Edit: После того, как я написал контрольный показатель, я взял пик sneak в 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 - сложная среда, то, что с компиляцией и сборкой "точно в срок" и т.д., поэтому, чтобы получить справедливое сравнение, всякий раз, когда я запускаю эталон, я всегда: (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 миллисекунд - менее 1/3 от подхода toString и 1/10 лог-подхода на моей машине.
Если у вас был широкий диапазон чисел, вы могли бы ускорить его, начав делиться на 1,000 или 1,000,000, чтобы сократить количество раз в цикле. Я не играл с этим.
Использование 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
в начале
Так как число цифр в базе 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);
}
}
Отредактировано, потому что мое последнее редактирование исправило пример кода, но не описание.
Не могу оставить комментарий, поэтому я отправлю в виде отдельного ответа.
Логарифмическое решение не вычисляет правильное количество цифр для очень больших длинных целых чисел, например:
long n = 99999999999999999L;
// correct answer: 17
int numberOfDigits = String.valueOf(n).length();
// incorrect answer: 18
int wrongNumberOfDigits = (int) (Math.log10(n) + 1);
Логарифмическое решение вычисляет неправильное количество цифр в больших целых числах
Марианское решение, адаптированное для длинного номера типа (до 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;
}
}
}
}
}
Еще один струнный подход. Коротко и сладко - для любого целого числа n
.
int length = ("" + n).length();
Могу ли я попробовать?;)
на основе решения Dirk
final int digits = number==0?1:(1 + (int)Math.floor(Math.log10(Math.abs(number))));
Как насчет простой старой математики? Разделите на 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 я заменил 'number' на '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;
}
С дизайном (на основе проблемы). Это альтернатива разделить и победить. Сначала мы определим перечисление (рассматривая его только для неподписанного 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;
}
}
Время выполнения этого решения такое же, как и подход "разделяй и властвуй".
Или вместо длины, которую вы можете проверить, больше или меньше, чем нужный номер.
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;
}
Мы можем добиться этого, используя рекурсивный цикл
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+"]");
}
Один хочет сделать это в основном потому, что он/она хочет "представить" его, что в основном означает, что в конечном итоге должно быть "toString-ed" (или преобразовано по-другому) явно или неявно; прежде чем он будет представлен (напечатан, например).
Если это так, просто попробуйте сделать явную "toString" явной и подсчитайте биты.
Я написал эту функцию после просмотра исходного кода 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;
}
}
}
int num = 02300;
int count = 0;
while(num>0){
if(num == 0) break;
num=num/10;
count++;
}
System.out.println(count);
Простой рекурсивный путь
int get_int_lenght(current_lenght, value)
{
if (value / 10 < 10)
return (current_lenght + 1);
return (get_int_lenght(current_lenght + 1, value))
}
не проверено
Я сделал это таким образом и получил длину числа
String Temp=IntegerVariable+"";
System.out.println(Temp.length());
Здесь действительно простой метод, который я сделал для любого числа:
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;
}
Введите номер и создайте 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 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;
}