Чи існує більш охайний спосіб отримати довжину 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 разів. Досить часто тривалість другого проходу через цикл сильно відрізняється від першого. І (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 підходу до StSring і 1/10 підходу до журналу на моїй машині.
Якщо у вас був широкий діапазон чисел, ви можете прискорити його далі, розпочавши ділення на 1000 або 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
на початку
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
Тепер мені залишається цікаво, чи мій показник насправді щось означає, але я отримую послідовні результати (варіації в межах мс) протягом декількох циклів самого еталону ... :) Схоже, марно намагатися і оптимізувати це ...
редагувати: після коментаря ptomli, я замінив 'число' на 'i' у наведеному вище коді та отримав наступні результати протягом 5 пробіг лавки:
Час виконання 1: 11500 с: 788888890 Час виконання 2: 8547 с: 788888890 Час виконання 1: 11485 с: 788888890 Час виконання 2: 8547 с: 788888890 Час виконання 1: 11469 с: 788888890 Час виконання 2: 8547 с: 788888890 Час виконання 1: 11500 с: 788888890 Час виконання 2: 8547 с: 788888890 Час виконання 1: 11484 с: 788888890 Час виконання 2: 8547 с: 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 ... так як min дорівнює 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;
}
З дизайном (на основі проблеми). Це альтернатива ділити-перемагай. Спочатку визначимо enum (враховуючи, що це лише для непідписаного 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;
}
}
Тепер ми визначимо клас, який проходить через значення enum та порівняємо та повернемо відповідну довжину.
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()
?
Хочеться це зробити здебільшого тому, що він / вона хоче "представити" це, що здебільшого означає, що його нарешті потрібно "в String-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. Нічого поганого в цьому немає, але алгоритм отримання кількості цифр не такий складний. У цьому прикладі я використовую довгий, але він працює так само чудово з 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
? Я не можу придумати нічого кращого.
Редагувати: Подумав лише про краще пояснення. По суті, це, що робить цикл, - це зробити так, щоб ви розділили число на 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
негативно.