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.
+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.
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 lessif(n <100){// 1 or 2if(n <10)return1;elsereturn2;}else{// 3 or 4 or 5if(n <1000)return3;else{// 4 or 5if(n <10000)return4;elsereturn5;}}}else{// 6 or moreif(n <10000000){// 6 or 7if(n <1000000)return6;elsereturn7;}else{// 8 to 10if(n <100000000)return8;else{// 9 or 10if(n <1000000000)return9;elsereturn10;}}}
Test porównawczy (po rozgrzewce JVM) - zobacz poniższy kod, aby zobaczyć, jak uruchomiono test porównawczy:
metoda bazowa (z String.length): 2145ms
metoda log10: 711ms = 3,02 razy szybciej niż poziom wyjściowy
powtarzany podział: 2797 ms = 0,77 razy szybciej niż poziom wyjściowy
Dziel i rządź: 74ms = 28,99
razy szybciej niż poziom podstawowy
Pełny kod:
publicstaticvoid main(String[] args)throwsException{// 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 benchmarkChronometer c;
c =newChronometer(true);
allMethod1();
c.stop();long baseline = c.getValue();System.out.println(c);
c =newChronometer(true);
allMethod2();
c.stop();System.out.println(c +" = "+StringTools.formatDouble((double)baseline / c.getValue(),"0.00")+" times as fast as baseline");
c =newChronometer(true);
allMethod3();
c.stop();System.out.println(c +" = "+StringTools.formatDouble((double)baseline / c.getValue(),"0.00")+" times as fast as baseline");
c =newChronometer(true);
allMethod4();
c.stop();System.out.println(c +" = "+StringTools.formatDouble((double)baseline / c.getValue(),"0.00")+" times as fast as baseline");}privatestaticint method1(int n){returnInteger.toString(n).length();}privatestaticint method2(int n){if(n ==0)return1;return(int)(Math.log10(n)+1);}privatestaticint method3(int n){if(n ==0)return1;int l;for(l =0; n >0;++l)
n /=10;return l;}privatestaticint method4(int n){if(n <100000){// 5 or lessif(n <100){// 1 or 2if(n <10)return1;elsereturn2;}else{// 3 or 4 or 5if(n <1000)return3;else{// 4 or 5if(n <10000)return4;elsereturn5;}}}else{// 6 or moreif(n <10000000){// 6 or 7if(n <1000000)return6;elsereturn7;}else{// 8 to 10if(n <100000000)return8;else{// 9 or 10if(n <1000000000)return9;elsereturn10;}}}}privatestaticint 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;}privatestaticint 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;}privatestaticint 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;}privatestaticint 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:
metoda bazowa (z String.length): 2145ms
metoda log10: 711ms = 3,02 razy szybciej niż poziom wyjściowy
powtarzany podział: 2797 ms = 0,77 razy szybciej niż poziom wyjściowy
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:
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ę:
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.
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;
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.
publicstaticint numberOfDigits (long n){// Guessing 4 digit numbers will be more probable.// They are set in the first branch.if(n <10000L){// from 1 to 4if(n <100L){// 1 or 2if(n <10L){return1;}else{return2;}}else{// 3 or 4if(n <1000L){return3;}else{return4;}}}else{// from 5 a 20 (albeit longs can't have more than 18 or 19)if(n <1000000000000L){// from 5 to 12if(n <100000000L){// from 5 to 8if(n <1000000L){// 5 or 6if(n <100000L){return5;}else{return6;}}else{// 7 u 8if(n <10000000L){return7;}else{return8;}}}else{// from 9 to 12if(n <10000000000L){// 9 or 10if(n <1000000000L){return9;}else{return10;}}else{// 11 or 12if(n <100000000000L){return11;}else{return12;}}}}else{// from 13 to ... (18 or 20)if(n <10000000000000000L){// from 13 to 16if(n <100000000000000L){// 13 or 14if(n <10000000000000L){return13;}else{return14;}}else{// 15 or 16if(n <1000000000000000L){return15;}else{return16;}}}else{// from 17 to ...¿20?if(n <1000000000000000000L){// 17 or 18if(n <100000000000000000L){return17;}else{return18;}}else{// 19? Can it be?// 10000000000000000000L is'nt a valid long.return19;}}}}}
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”
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
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.
publicvoid createCard(int cardNumber,int cardStatus,int customerId)throwsSQLException{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 =newScanner(System.in);int inputCardNumber = scan.nextInt();
cardNumber = inputCardNumber;}while(cardNumber <95000000);
cardDao.createCard(cardNumber, cardStatus, customerId);}}
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.
*/publicstaticint 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)
*/privatestaticlong[] 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};publicstaticint 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;}
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).
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.
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.
privatestaticint getLength(long num){int count =1;while(num >=10){
num = num /10;
count++;}return count;}
bez interfejsu API napisów, bez narzędzi, bez konwersji typu, po prostu iteracja w java ->
publicstaticint 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.
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;}elseif(no ==0){
no =1;}while(no >0){
no = no /10;
a++;}System.out.println("Number of digits in given number is: "+a);
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=newArrayList<>();while(number >0){
remainder = num %10;
a.add(remainder);
number = number /10;}int m=a.size();
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:
Odpowiedzi:
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.
źródło
Math.abs()
to jednak naprawi.Logarytm jest twoim przyjacielem:
NB: ważne tylko dla n> 0.
źródło
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):
Test porównawczy (po rozgrzewce JVM) - zobacz poniższy kod, aby zobaczyć, jak uruchomiono test porównawczy:
razy szybciej niż poziom podstawowy
Pełny kod:
Ponownie, punkt odniesienia:
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:
Porównałem go z moim rozwiązaniem „dziel i rządź”:
Mój jest około 4x szybszy niż rozwiązanie Java 6.
źródło
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
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ę:
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.
źródło
Korzystanie z Java
użyj
import java.lang.Math.*;
na początkuKorzystanie z C.
użyj
inclue math.h
na początkuźródło
the_integer
jest0
, więc sprawdź to.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:
Rozwiązanie oparte na logarytmach oblicza niepoprawną liczbę cyfr w dużych liczbach całkowitych
źródło
Ponieważ liczba cyfr w podstawie 10 liczby całkowitej wynosi tylko 1 + obcięcie (log10 (liczba)) , możesz:
Edytowane, ponieważ moja ostatnia edycja naprawiła przykład kodu, ale nie opis.
źródło
Math.floor
jest trochę zbędne, prawda? Zrealizowanie iint
tak zaokrągli to w dół.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.
źródło
Kolejne podejście strunowe. Krótkie i słodkie - dla dowolnej liczby całkowitej
n
.źródło
n
i zera. Można użyć,("" + Math.abs(n)).length()
aby uzyskać długość ujemnej liczby całkowitej.Mogę spróbować? ;)
oparty na rozwiązaniu Dirka
źródło
Co powiesz na zwykłą starą matematykę? Dziel przez 10, aż osiągniesz 0.
źródło
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 zakresu0
doLong.MAX_VALUE
zbyt, tylko dla „przeciętnego złożoności” Testowanie ++ Można znaleźć wyników ... bardzo szokujące.int,
tej pętli wykonuje się maksymalnie 11 razy. Czy masz jakieś dowody na swoje twierdzenia?Rozwiązanie Mariana, teraz z Ternary:
Ponieważ możemy.
źródło
Ciekawe, próbowałem to porównać ...
wyniki są następujące:
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:
źródło
Co z tą metodą rekurencyjną?
źródło
proste rozwiązanie:
źródło
Naprawdę proste rozwiązanie:
źródło
Lub zamiast tego możesz sprawdzić, czy liczba jest większa czy mniejsza niż żądana liczba.
}
źródło
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
: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
long
prostu przez zastąpienie każdego wystąpienia zint
powodu 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)źródło
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).
Teraz zdefiniujemy klasę, która przejdzie wartości wyliczenia i porówna i zwróci odpowiednią długość.
Czas działania tego rozwiązania jest taki sam, jak w przypadku metody „dziel i rządź”.
źródło
num>=Nine.getValue()
?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.
źródło
Możemy to osiągnąć za pomocą pętli rekurencyjnej
źródło
Napisałem tę funkcję po sprawdzeniu
Integer.java
kodu źródłowego.źródło
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.
źródło
bez interfejsu API napisów, bez narzędzi, bez konwersji typu, po prostu iteracja w java ->
Jeśli chcesz, możesz długo szukać większych wartości.
źródło
źródło
Łatwy sposób rekurencyjny
nie testowany
źródło
Mógłbyś cyfry, stosując kolejne dzielenie przez dziesięć:
źródło
Wprowadź numer i utwórz
Arraylist
, a pętla while zapisze wszystkie cyfry wArraylist
. Następnie możemy wyjąć rozmiar tablicy, który będzie długością wprowadzonej wartości całkowitej.źródło
Oto bardzo prosta metoda, którą stworzyłem, która działa dla dowolnej liczby:
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ę zmiennejnumberCounter
? 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:
źródło
Spróbuj przekonwertować int do łańcucha , a następnie uzyskać długość łańcucha . To powinno uzyskać długość int .
źródło
number
będzie negatywny.