Jak uzyskać liczbę cyfr w ciągu?

385

Czy istnieje lepszy sposób uzyskania długości int niż ta metoda?

int length = String.valueOf(1000).length();
pierwszy
źródło
7
proszę podać długość int.
Tom
24
Myślę, że chce policzyć cyfry.
Alberto Zaccagni,
3
Odpowiedzi, które ludzie ci udzielają, są poprawne ... podają długość twojego int bez konwersji na ciąg ... ale dlaczego nie chcesz przekonwertować go na ciąg? Czy to kwestia prędkości? Jeśli tak, to nie jestem przekonany, że metody te będą szybsze ... możesz chcieć zrobić kilka testów (lub zdecydować, czy to w ogóle ma znaczenie)
Beska
3
@ptomli cyfry szesnastkowe są nadal cyframi, tylko w innym systemie podstawowym.
Mark Pim
2
@Ptomli Pewnie, ale zarówno w funkcji Integer.toString, jak i ogólnie w rozmowach, dziesiętna jest domyślna. Kiedy bank mówi mi: „Wpisz kwotę czeku w tym polu”, nie pytam ich, czy mam napisać go dziesiętnie, szesnastkowo czy ósemkowo. Zakładamy ułamek dziesiętny, o ile nie określono inaczej lub nie wymaga tego kontekst.
Jay

Odpowiedzi:

349

Twoje rozwiązanie oparte na łańcuchach znaków jest w porządku, nie ma w tym nic „nieuporządkowanego”. Musisz zdać sobie sprawę, że matematycznie liczby nie mają długości ani cyfr. Długość i cyfry są właściwościami fizycznej reprezentacji liczby w określonej bazie, tj. Ciągu znaków .

Rozwiązanie oparte na logarytmie wykonuje (niektóre) te same czynności, co rozwiązanie oparte na łańcuchach, i prawdopodobnie robi to (nieznacznie) szybciej, ponieważ generuje tylko długość i ignoruje cyfry. Ale tak naprawdę nie uważałbym tego za bardziej intencjonalne - i to jest najważniejszy czynnik.

Michael Borgwardt
źródło
54
+1 za rozważenie zamiaru kodu przy wyborze sposobu rozwiązania problemu
pupeno
5
Datapoint: Na moim komputerze metoda dziennika wydaje się działać prawie dwa razy szybciej niż metody długości łańcucha. Nie nazwałbym tego tak nieznacznym, gdyby metoda była często wywoływana lub w krytycznej czasowo części kodu.
CPerkins,
1
Zobacz mój test testu porównawczego poniżej (który też może być wadliwy, nie jestem ekspertem od testów porównawczych). W przypadku dużej liczby przebiegów (100 000 000) prędkość wynosi od 11 do 8 sekund na mojej maszynie prawie dwa razy szybciej.
Jean
5
@CPerkins. Przedwczesna optymalizacja. Znasz grę.
Michael Borgwardt,
11
Niektóre (dość późne) dodawanie: Może nie działać poprawnie dla wartości ujemnych, w zależności od tego, czy spodziewasz się, że „-” będzie cyfrą, czy nie. Dodanie Math.abs()to jednak naprawi.
YingYang,
265

Logarytm jest twoim przyjacielem:

int n = 1000;
int length = (int)(Math.log10(n)+1);

NB: ważne tylko dla n> 0.

Dmitry Brant
źródło
2
Czy to jest szybsze czy lepsze niż używanie mojego wariantu?
pierwszego
+1 Pokonałeś mnie o sekundę, a twoja odpowiedź była prawidłowa, tam gdzie moja była nieco wyłączona. Uwaga jednak, że kompilator będzie narzekać z powodu braku obsady do int
Dirk
2
@ Tom Dlaczego miałbyś zakładać, że jest drogi? Można założyć, że koprocesor matematyczny go wykona, więc może być bliski prędkości dodawania. Nawet jeśli java nie używa teraz koprocesora, to jest dobre założenie, że może ... (Po prostu zignorujemy twoją jeszcze niewykształconą implikację, że Java jest powolna, ponieważ prawdopodobnie nie jesteś zainteresowany dowodami - lub gdybyś był, poszedłbyś na shootout.alioth.debian.org i sam się przekonałeś)
Bill K
8
Działa ... chyba że sprawdzana wartość = 0, co da nieparzyste wyniki (-2147483647). Math.log10 API: „Jeśli argument jest dodatni zero lub ujemny zero, to wynikiem jest ujemna nieskończoność.”
mujimu
2
+1 Przedstawienie metody, która nie wymaga alokacji pamięci obiektów, co jest niezbędne do maksymalnego ponownego użycia, aby uniknąć kolekcji GC.
Michael Wojcik,
158

Najszybsze podejście: dziel i podbijaj.

Zakładając, że twój zakres wynosi od 0 do MAX_INT, to masz od 1 do 10 cyfr. Możesz zbliżyć się do tego przedziału za pomocą dzielenia i podbijania, z maksymalnie 4 porównaniami na każde wejście. Najpierw dzielisz [1..10] na [1..5] i [6..10] z jednym porównaniem, a następnie każdy przedział długości 5 dzielisz za pomocą jednego porównania na jeden przedział długości 3 i jeden przedział długości 2. Przedział długości 2 wymaga jeszcze jednego porównania (łącznie 3 porównania), przedział długości 3 można podzielić na przedział długości 1 (rozwiązanie) i przedział długości 2. Potrzebujesz więc 3 lub 4 porównań.

Bez podziałów, bez operacji zmiennoprzecinkowych, bez drogich logarytmów, tylko porównania liczb całkowitych.

Kod (długi, ale szybki):

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;
            }
        }
    }

Test porównawczy (po rozgrzewce JVM) - zobacz poniższy kod, aby zobaczyć, jak uruchomiono test porównawczy:

  1. metoda bazowa (z String.length): 2145ms
  2. metoda log10: 711ms = 3,02 razy szybciej niż poziom wyjściowy
  3. powtarzany podział: 2797 ms = 0,77 razy szybciej niż poziom wyjściowy
  4. Dziel i rządź: 74ms = 28,99
    razy szybciej niż poziom podstawowy

Pełny kod:

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;
}

Ponownie, punkt odniesienia:

  1. metoda bazowa (z String.length): 2145ms
  2. metoda log10: 711ms = 3,02 razy szybciej niż poziom wyjściowy
  3. powtarzany podział: 2797 ms = 0,77 razy szybciej niż poziom wyjściowy
  4. Dziel i rządź: 74ms = 28,99
    razy szybciej niż poziom podstawowy

Edycja: Po napisaniu testu porównawczego wziąłem podstęp do Integer.toString z Java 6 i stwierdziłem, że używa:

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;
}

Porównałem go z moim rozwiązaniem „dziel i rządź”:

  1. Dziel i rządź: 104ms
  2. Rozwiązanie Java 6 - iteruj i porównuj: 406 ms

Mój jest około 4x szybszy niż rozwiązanie Java 6.

Marian
źródło
7
to wygląda świetnie. możesz napisać to trochę bardziej kompaktowo, używając operatora?:, aby uzyskać większą akceptację
André Pareis
88
mówić o przedwczesnej optymalizacji: D
Gordon Gustafson,
2
Lubię to! Co powiesz na blok przełączników zamiast tak zagnieżdżonych if-else?
Kebman
2
Nie zdawałem sobie sprawy z tego wszystkiego, jeśli w przeciwnym razie instrukcje byłyby TAK DUŻO szybsze niż konwersja int na String, a następnie wywołanie .length. +1
Ogen
15
Za pomocą operatora potrójnego sprowadza go do 101 znaków: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
Jonathan Gawrych
13

Dwa komentarze na temat twojego testu porównawczego: Java jest złożonym środowiskiem, z kompilacją „just-in-time” i zbieraniem śmieci itd. Aby uzyskać rzetelne porównanie za każdym razem, gdy uruchamiam test porównawczy, zawsze: (a) załączam dwa testy w pętli, która uruchamia je kolejno 5 lub 10 razy. Dość często środowisko uruchomieniowe przy drugim przejściu przez pętlę jest zupełnie inne niż pierwsze. I (b) Po każdym „podejściu” wykonuję System.gc (), aby spróbować wyrzucić śmieci. W przeciwnym razie pierwsze podejście może wygenerować wiązkę obiektów, ale niewystarczająco do wymuszenia wyrzucania elementów bezużytecznych, następnie drugie podejście tworzy kilka obiektów, stos jest wyczerpany i następuje odśmiecanie. Następnie drugie podejście jest „naliczane” za zbieranie śmieci pozostawionych przez pierwsze podejście. Bardzo niesprawiedliwie!

To powiedziawszy, żadne z powyższych nie miało znaczącej różnicy w tym przykładzie.

Z tymi modyfikacjami lub bez nich uzyskałem zupełnie inne wyniki niż ty. Kiedy to uruchomiłem, tak, podejście toString dało czasy działania od 6400 do 6600 milis, podczas gdy podejście logarytmiczne wynosi od 20 000 do 20 400 milis. Zamiast być nieco szybszym, podejście do dziennika było dla mnie 3 razy wolniejsze.

Zauważ, że oba podejścia wiążą się z bardzo różnymi kosztami, więc nie jest to całkowicie szokujące: podejście toString stworzy wiele tymczasowych obiektów, które muszą zostać oczyszczone, podczas gdy podejście oparte na logach wymaga bardziej intensywnych obliczeń. Być może różnica polega na tym, że na komputerze z mniejszą pamięcią toString wymaga więcej rund wyrzucania elementów bezużytecznych, podczas gdy na komputerze z wolniejszym procesorem dodatkowe obliczenie dziennika byłoby bardziej bolesne.

Próbowałem także trzeciego podejścia. Napisałem tę małą funkcję:

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;           
}

Działało to od 1600 do 1900 milis - mniej niż 1/3 podejścia toString i 1/10 logarytmu podejścia na mojej maszynie.

Jeśli dysponujesz szerokim zakresem liczb, możesz go jeszcze przyspieszyć, zaczynając od podzielenia przez 1 000 lub 1 000 000, aby zmniejszyć liczbę razy w pętli. Nie grałem z tym.

Sójka
źródło
Czy próbowałeś zmieniać dane wejściowe? W przeciwnym razie maszyna wirtualna punktu dostępowego może zoptymalizować ten wykres, co spowoduje nieprawidłowe testy porównawcze, ponieważ za każdym razem zwraca tę samą wstępnie obliczoną rzecz.
Erik Aigner
11

Korzystanie z Java

int nDigits = Math.floor(Math.log10(Math.abs(the_integer))) + 1;

użyj import java.lang.Math.*;na początku

Korzystanie z C.

int nDigits = floor(log10(abs(the_integer))) + 1;

użyj inclue math.hna początku

Santosh
źródło
1
Tylko FYI, spowoduje nieskończoność, jeśli tak the_integerjest 0, więc sprawdź to.
Erik Aigner
9

Nie mogę jeszcze zostawić komentarza, więc opublikuję jako osobną odpowiedź.

Rozwiązanie oparte na logarytmie nie oblicza poprawnej liczby cyfr dla bardzo dużych długich liczb całkowitych, na przykład:

long n = 99999999999999999L;

// correct answer: 17
int numberOfDigits = String.valueOf(n).length();

// incorrect answer: 18
int wrongNumberOfDigits = (int) (Math.log10(n) + 1); 

Rozwiązanie oparte na logarytmach oblicza niepoprawną liczbę cyfr w dużych liczbach całkowitych

nastrojowy
źródło
try (int) (Math.log10 (n + j)) zamiast tego gdzie j wynosi 10 - (n - n / 10 * 10).
Erick Stone,
8

Ponieważ liczba cyfr w podstawie 10 liczby całkowitej wynosi tylko 1 + obcięcie (log10 (liczba)) , możesz:

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);
    }
}

Edytowane, ponieważ moja ostatnia edycja naprawiła przykład kodu, ale nie opis.

Sztylet
źródło
Fajne. ale myślę, że potrzebuje abs (liczby), a także „0” to także przypadek specjalny?
DmitryK,
Tak. Jeśli musisz uwzględnić znak, musisz zrobić coś takiego jak 1 + (int) Math.floor (Math.log10 (Math.abs (number))) + ((number <0)? 1: 0)
Dirk
5
To Math.floorjest trochę zbędne, prawda? Zrealizowanie i inttak zaokrągli to w dół.
CompuChip
5

Rozwiązanie Mariana dostosowane do liczb długich (do 9 223 372,036,854,775,807) na wypadek, gdyby ktoś chciał je skopiować i wkleić. W programie napisałem to dla liczb do 10000, które były znacznie bardziej prawdopodobne, więc stworzyłem dla nich specjalną gałąź. W każdym razie nie zrobi to znaczącej różnicy.

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;
                }
            }
        }
    }
}
WIĘZIENIE
źródło
Czy tytuł tego pytania należy zmienić na „Sposób uzyskania liczby cyfr w ciągu int / long?” (i dodał tag „długi”)
marca12 o
4

Kolejne podejście strunowe. Krótkie i słodkie - dla dowolnej liczby całkowitej n.

int length = ("" + n).length();
ThisClark
źródło
Działa tylko dla dodatniej liczby całkowitej ni zera. Można użyć, ("" + Math.abs(n)).length()aby uzyskać długość ujemnej liczby całkowitej.
ThisClark
3

Mogę spróbować? ;)

oparty na rozwiązaniu Dirka

final int digits = number==0?1:(1 + (int)Math.floor(Math.log10(Math.abs(number))));
DmitryK
źródło
3

Co powiesz na zwykłą starą matematykę? Dziel przez 10, aż osiągniesz 0.

public static int getSize(long number) {
        int count = 0;
        while (number > 0) {
            count += 1;
            number = (number / 10);
        }
        return count;
    }
Sinista
źródło
1
Przetestowałeś to? Wiesz, że nawet jeśli ma to sens dla ludzkiego punktu widzenia, tak naprawdę nie działa tak samo z „sposobem myślenia” maszyny, prawda? --- Pozwolę sobie zaproponować jedną rzecz: Zrób tablicę dwóch milionów liczb, najlepiej Long.MAX_VALUE, która jest najgorszym przypadkiem złożoności twojego kodu, i wykorzystaj, System.nanoTime()aby wykonać próbę taktowania w odniesieniu do najgorszych przypadków złożoności drugiego rozwiązania. ++ Faktycznie, spróbuj go z tablicy wypełnionej przez zestaw Randomizer do zakresu 0do Long.MAX_VALUEzbyt, tylko dla „przeciętnego złożoności” Testowanie ++ Można znaleźć wyników ... bardzo szokujące.
XenoRo,
@thelima To nie działa poprawnie dla zera lub negatywów, ale to niewielki błąd. Ta zasada wydaje mi się poprawna. O jakim „szokującym” wyniku mówisz?
Jay,
Powiedzmy, że komputery ... Cóż ... Nie lubią się dzielić. A w przypadkach, gdy trzeba przetworzyć duże „kolejki” dużych liczb, a każda cyfra w każdej przetworzonej liczbie będzie wymagała podziału ... Cóż ... Rzeczy „zaczynają się naprawdę powoli naprawdę szybko” ... Jeśli złapiesz mój co oznacza ... --- To dlatego widzisz tutaj wiele odpowiedzi za pomocą kodów opartych na testach i porównaniu z każdą cyfrą dziesiętną za pomocą „ifs” zamiast podziałów: jeśli nie jest szybszy, przynajmniej utrzymuje większość swojej prędkości niezależnie od z najgorszych przypadków. --- Wykonaj test między użyciem podziałów i logarytmu dla dużych liczb ...
XenoRo
@TheLima o czym mówisz? Dla int,tej pętli wykonuje się maksymalnie 11 razy. Czy masz jakieś dowody na swoje twierdzenia?
Markiz Lorne
@EJP Z punktu widzenia sprzętu podział jest procesem iteracyjnym. Najszybszym znanym mi algorytmem podziału jest radix4, który generuje 4 bity na iterację; więc podział 32-bitowy wymaga co najmniej 8 iteracji. Na przykład mnożenia można wykonywać równolegle, a także można je podzielić na prostsze mnożenia; albo do poziomu bitowego (wymagającego tylko 5 operacji), albo z częściowym podziałem oraz tabelą przeglądową na końcu (kompromis prędkości VS w rozmiarze klasycznym). Nie chodzi tylko o „ile iteracji”; problem z podziałami polega na „tym, co każda iteracja implikuje / robi na poziomie sprzętowym”
XenoRo
2

Rozwiązanie Mariana, teraz z Ternary:

 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)));
    }

Ponieważ możemy.

Użytkownik nie znaleziony
źródło
2
To trochę trudne do odczytania. Może dodać spacje i / lub nowe linie.
michaelb958 - GoFundMonica
Ale cholera, czy to przenośne!
Trevor Rudolph
1

Ciekawe, próbowałem to porównać ...

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);


    }
}

wyniki są następujące:

Czas działania 1: 6765
s: 400000000
Czas pracy 2: 6000
s: 400000000

Teraz zastanawiam się, czy mój test porównawczy rzeczywiście coś znaczy, ale otrzymuję spójne wyniki (zmiany w ciągu ms) w wielu testach samego testu ... :) Wygląda na to, że nie ma sensu próbować optymalizować tego ...


edytuj: w następstwie komentarza ptomli zastąpiłem „numer” przez „i” w powyższym kodzie i uzyskałem następujące wyniki w ciągu 5 przebiegów stanowiska:

Czas działania 1: 11500
s: 788888890
Czas wykonywania 2: 8547
s: 788888890

Czas działania 1: 11485
s: 788888890
Czas wykonywania 2: 8547
s: 788888890

Czas wykonania 1: 11469
s: 788888890
Czas wykonywania 2: 8547
s: 788888890

Czas działania 1: 11500
s: 788888890
Czas wykonywania 2: 8547
s: 788888890

Czas wykonywania 1: 11484
s: 788888890
Czas wykonywania 2: 8547
s: 788888890
Drelich
źródło
1
Tylko dla zabawy, jaka jest różnica w rozkładzie wartości liczbowych, od powiedzmy 0 do bilionów? :)
ptomli
0

Co z tą metodą rekurencyjną?

    private static int length = 0;

    public static int length(int n) {
    length++;
    if((n / 10) < 10) {
        length++;
    } else {
        length(n / 10);
    }
    return length;
}
Jedi Dula
źródło
0

proste rozwiązanie:

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);
}
mikegh
źródło
0

Naprawdę proste rozwiązanie:

public int numLength(int n) {
  for (int length = 1; n % Math.pow(10, length) != n; length++) {}
  return length;
}
VoidCatz
źródło
Nie nazwałbym jednej linii pętlą z pustym ciałem prostym. Ani modulo potęgi 10, aby zobaczyć, czy odzyskasz to samo (nie możesz po prostu użyć porównania?).
Teepeemm,
0

Lub zamiast tego możesz sprawdzić, czy liczba jest większa czy mniejsza niż żądana liczba.

    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);
    }
}

}

Szabi Zsoldos
źródło
Nie rozumiem. Wygląda na to, że odpowiadasz na inne pytanie.
Teepeemm,
0

Nie widziałem jeszcze rozwiązania opartego na mnożeniu. Rozwiązania oparte na logarytmie, dywizji i łańcuchach staną się raczej niewygodne w stosunku do milionów przypadków testowych, więc oto jeden z nich 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;
}

W bazie 10 działa to, ponieważ n jest zasadniczo porównywane z 9, 99, 999 ... tak jak min wynosi 9, 90, 900 ... a n jest odejmowane przez 9, 90, 900 ...

Niestety, nie jest to przenośne po longprostu przez zastąpienie każdego wystąpienia z intpowodu przepełnienia. Z drugiej strony tak się składa, że będzie działał dla baz 2 i 10 (ale w przypadku większości innych baz bardzo się nie powiedzie). Potrzebujesz tabeli przeglądowej dla punktów przepełnienia (lub testu podziału ... 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;
}
Jonathan Smith
źródło
0

Z projektem (w oparciu o problem). Jest to alternatywa podziału i podboju. Najpierw zdefiniujemy wyliczenie (biorąc pod uwagę, że dotyczy tylko int bez znaku).

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;
    }
}

Teraz zdefiniujemy klasę, która przejdzie wartości wyliczenia i porówna i zwróci odpowiednią długość.

public class IntegerLenght {
    public static byte calculateIntLenght(int num) {    
        for(IntegerLength v : IntegerLength.values()) {
            if(num < v.getValue()){
                return v.getLenght();
            }
        }
        return 0;
    }
}

Czas działania tego rozwiązania jest taki sam, jak w przypadku metody „dziel i rządź”.

androider
źródło
Podział i podbój rozpoczynałby się na środku i przecinał pozostały obszar poszukiwań. Ma to liniowy czas działania. Ale to nie będzie miało znaczenia tylko dla 9 porównań. Ale czy to nie zadziała, jeśli num>=Nine.getValue()?
Teepeemm,
0

Ktoś chce to zrobić głównie dlatego, że chce ją „przedstawić”, co w większości oznacza, że ​​w końcu należy ją „poddać edycji” (lub przekształcić w inny sposób), jawnie lub pośrednio; zanim będzie można go przedstawić (na przykład wydrukować).

Jeśli tak jest, po prostu spróbuj jawnie określić „toString” i policz bity.

howToDeleteMyAccount
źródło
0

Możemy to osiągnąć za pomocą pętli rekurencyjnej

    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+"]");
    }
ericdemo07
źródło
0

Napisałem tę funkcję po sprawdzeniu Integer.javakodu źródłowego.

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;
        }
    }
}
skorupiak
źródło
0

Widzę ludzi korzystających z bibliotek String, a nawet korzystających z klasy Integer. Nie ma w tym nic złego, ale algorytm uzyskiwania liczby cyfr nie jest tak skomplikowany. Używam długi w tym przykładzie, ale działa równie dobrze z int.

 private static int getLength(long num) {

    int count = 1;

    while (num >= 10) {
        num = num / 10;
        count++;
    }

    return count;
}
Sameer Khanal
źródło
0

bez interfejsu API napisów, bez narzędzi, bez konwersji typu, po prostu iteracja w java ->

public static int getNumberOfDigits(int input) {
    int numOfDigits = 1;
    int base = 1;
    while (input >= base * 10) {
        base = base * 10;
        numOfDigits++;
    }
    return numOfDigits;
 }

Jeśli chcesz, możesz długo szukać większych wartości.

Sahil
źródło
-1
    int num = 02300;
    int count = 0;
    while(num>0){
         if(num == 0) break;
         num=num/10;
         count++;
    }
    System.out.println(count);
kanakangi
źródło
Rozwiązanie „dziel przez 10” zostało po raz pierwszy opublikowane przez Sinistę dwa lata wcześniej.
Teepeemm,
-1

Łatwy sposób rekurencyjny

int    get_int_lenght(current_lenght, value)
{
 if (value / 10 < 10)
    return (current_lenght + 1);
return (get_int_lenght(current_lenght + 1, value))
}

nie testowany

Valérian Polizzi
źródło
3
Prawdopodobnie powinieneś go wtedy przetestować (i upewnić się, że jest to poprawna Java i odpowiednio sformatowana). Ale Jedi Dula opublikował rekurencyjne podejście „dziel przez 10” 3 lata temu.
Teepeemm,
-2

Mógłbyś cyfry, stosując kolejne dzielenie przez dziesięć:

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);
użytkownik1590262
źródło
Podejście „dziel przez 10” zostało po raz pierwszy opublikowane przez Sinistę 3 lata temu. To jedyny powód, dla którego mogę wymyślić, że masz głos negatywny.
Teepeemm,
-2

Wprowadź numer i utwórz Arraylist, a pętla while zapisze wszystkie cyfry w Arraylist. Następnie możemy wyjąć rozmiar tablicy, który będzie długością wprowadzonej wartości całkowitej.

ArrayList<Integer> a=new ArrayList<>();

while(number > 0) 
{ 
    remainder = num % 10; 
    a.add(remainder);
    number = number / 10; 
} 

int m=a.size();
dev
źródło
1
Tyle że nie potrzebujesz ArrayList ani cyfr.
Markiz Lorne
-2

Oto bardzo prosta metoda, którą stworzyłem, która działa dla dowolnej liczby:

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; 
}

Działa ze zmienną licznika liczb w taki sposób, że 10 = 1 cyfra. Na przykład .1 = 1 dziesiąta => 1 cyfra. Dlatego jeśli masz int number = 103342;, dostaniesz 6, ponieważ jest to równowartość 0,000001 spacji z powrotem. Ponadto, czy ktoś ma lepszą nazwę zmiennej numberCounter? Nie mogę wymyślić nic lepszego.

Edycja: Pomyślałem o lepszym wyjaśnieniu. Zasadniczo to, co robi ta pętla while, polega na dzieleniu liczby przez 10, aż będzie mniejsza niż jeden. Zasadniczo, dzieląc coś przez 10, przesuwasz je o jedną spację liczbową, więc po prostu dziel to przez 10, aż osiągniesz <1 dla liczby cyfr w liczbie.

Oto kolejna wersja, która może liczyć liczbę cyfr po przecinku:

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;
}
Andrew Patterson
źródło
-3

Spróbuj przekonwertować int do łańcucha , a następnie uzyskać długość łańcucha . To powinno uzyskać długość int .

public static int intLength(int num){
    String n = Integer.toString(num);
    int newNum = n.length();
    return newNum;
}
użytkownik5458400
źródło
Jest to całkowicie równoważne z oryginalnym kodem. I przegapi, gdy numberbędzie negatywny.
Teepeemm,