Existe uma maneira mais clara de obter o comprimento de um int que esse método?
int length = String.valueOf(1000).length();
Existe uma maneira mais clara de obter o comprimento de um int que esse método?
int length = String.valueOf(1000).length();
Respostas:
Sua solução baseada em String está perfeitamente bem, não há nada "arrumado" nela. Você deve perceber que matematicamente, os números não têm comprimento nem têm dígitos. Comprimento e dígitos são propriedades de uma representação física de um número em uma base específica, isto é, uma String.
Uma solução baseada em logaritmo faz (algumas) as mesmas coisas que uma baseada em String faz internamente, e provavelmente o faz (insignificantemente) mais rapidamente porque produz apenas o comprimento e ignora os dígitos. Mas eu realmente não consideraria isso mais claro em termos de intenção - e esse é o fator mais importante.
Math.abs()
corrigirá isso, no entanto.
O logaritmo é seu amigo:
int n = 1000;
int length = (int)(Math.log10(n)+1);
NB: válido apenas para n> 0.
A abordagem mais rápida: dividir e conquistar.
Supondo que seu intervalo seja de 0 a MAX_INT, você tem de 1 a 10 dígitos. Você pode abordar esse intervalo usando dividir e conquistar, com até 4 comparações por cada entrada. Primeiro, você divide [1..10] em [1..5] e [6..10] com uma comparação e, em seguida, a cada intervalo 5, você divide usando uma comparação em um intervalo 3 e um comprimento 2. O intervalo de comprimento 2 requer mais uma comparação (total de 3 comparações), o intervalo de comprimento 3 pode ser dividido em intervalo de comprimento 1 (solução) e um intervalo de duração 2. Então, você precisa de 3 ou 4 comparações.
Sem divisões, operações de ponto flutuante, logaritmos dispendiosos, apenas comparações de números inteiros.
Código (longo mas rápido):
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;
}
}
}
Referência (após o aquecimento da JVM) - veja o código abaixo para ver como a referência foi executada:
Código completo:
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;
}
Mais uma vez, benchmark:
Edit: Depois que escrevi o benchmark, dei uma espiada no Integer.toString do Java 6 e descobri que ele usa:
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;
}
Comparei-o com a minha solução de dividir e conquistar:
O meu é cerca de 4x mais rápido que a solução 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
Dois comentários sobre seu benchmark: Java é um ambiente complexo, com compilação e coleta de lixo just-in-time e assim por diante, para obter uma comparação justa, sempre que executo um benchmark, sempre: (a) coloque os dois testes em um loop que os executa na sequência 5 ou 10 vezes. Muitas vezes, o tempo de execução na segunda passagem pelo loop é bem diferente da primeira. E (b) Após cada "abordagem", faço um System.gc () para tentar acionar uma coleta de lixo. Caso contrário, a primeira abordagem pode gerar um monte de objetos, mas não o suficiente para forçar uma coleta de lixo; a segunda abordagem cria alguns objetos, o heap está esgotado e a coleta de lixo é executada. Em seguida, a segunda abordagem é "cobrada" pela coleta do lixo deixado pela primeira abordagem. Muito injusto!
Dito isto, nenhum dos itens acima fez uma diferença significativa neste exemplo.
Com ou sem essas modificações, obtive resultados muito diferentes dos resultados obtidos. Quando eu executei isso, sim, a abordagem toString forneceu tempos de execução de 6400 a 6600 millis, enquanto a abordagem de log ultrapassou 20.000 a 20.400 millis. Em vez de ser um pouco mais rápida, a abordagem do log foi três vezes mais lenta para mim.
Observe que as duas abordagens envolvem custos muito diferentes, portanto, isso não é totalmente chocante: a abordagem toString criará muitos objetos temporários que precisam ser limpos, enquanto a abordagem de log exige uma computação mais intensa. Portanto, talvez a diferença seja que em uma máquina com menos memória, o toString requer mais rodadas de coleta de lixo, enquanto em uma máquina com um processador mais lento, o cálculo extra do log seria mais doloroso.
Eu também tentei uma terceira abordagem. Eu escrevi esta pequena função:
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;
}
Isso foi executado entre 1600 e 1900 milissegundos - menos de 1/3 da abordagem toString e 1/10 da abordagem de log na minha máquina.
Se você tivesse uma ampla variedade de números, poderia acelerar ainda mais, dividindo por 1.000 ou 1.000.000 para reduzir o número de vezes no loop. Eu não brinquei com isso.
Usando Java
int nDigits = Math.floor(Math.log10(Math.abs(the_integer))) + 1;
use import java.lang.Math.*;
no começo
Usando C
int nDigits = floor(log10(abs(the_integer))) + 1;
use inclue math.h
no começo
the_integer
for 0
, então verifique isso.
Não é possível deixar um comentário ainda, então postarei como uma resposta separada.
A solução baseada em logaritmo não calcula o número correto de dígitos para números inteiros muito grandes, por exemplo:
long n = 99999999999999999L;
// correct answer: 17
int numberOfDigits = String.valueOf(n).length();
// incorrect answer: 18
int wrongNumberOfDigits = (int) (Math.log10(n) + 1);
Solução baseada em logaritmo calcula número incorreto de dígitos em números inteiros grandes
Como o número de dígitos na base 10 de um número inteiro é apenas 1 + truncado (log10 (número)) , você pode:
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);
}
}
Editado porque minha última edição corrigiu o exemplo de código, mas não a descrição.
Math.floor
é um pouco redundante, não é? A transmissão para int
arredondará para baixo de qualquer maneira.
A solução da Marian adaptada para números longos (até 9.223.372.036.854.775.807), caso alguém queira copiá-lo e colá-lo. No programa que escrevi para números até 10000 eram muito mais prováveis, então criei um ramo específico para eles. De qualquer forma, não fará uma diferença significativa.
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;
}
}
}
}
}
Que tal matemática antiga? Divida por 10 até chegar a 0.
public static int getSize(long number) {
int count = 0;
while (number > 0) {
count += 1;
number = (number / 10);
}
return count;
}
Long.MAX_VALUE
, qual é o pior caso de complexidade do seu código e use-o System.nanoTime()
para fazer um teste de clock contra os piores casos de complexidade da outra solução. ++ Na verdade, tente com uma matriz preenchida por um randomizador definido como também 0
para Long.MAX_VALUE
, apenas para o teste de "complexidade média" ++ Você pode encontrar os resultados ... muito chocantes.
int,
esse loop, ele executa no máximo 11 vezes. Você tem alguma evidência para suas afirmações?
Solução da Marian, agora com 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)));
}
Porque nós podemos.
Curioso, tentei compará-lo ...
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);
}
}
os resultados são:
Tempo de execução 1: 6765 s: 400000000 Tempo de execução 2: 6000 s: 400000000
Agora, fico me perguntando se meu benchmark realmente significa alguma coisa, mas eu obtenho resultados consistentes (variações dentro de um ms) em várias execuções do próprio benchmark ... :) Parece que é inútil tentar otimizar isso ...
edit: após o comentário de ptomli, substitui 'number' por 'i' no código acima e obtive os seguintes resultados em 5 execuções do banco:
Tempo de execução 1: 11500 s: 788888890 Tempo de execução 2: 8547 s: 788888890 Tempo de execução 1: 11485 s: 788888890 Tempo de execução 2: 8547 s: 788888890 Tempo de execução 1: 11469 s: 788888890 Tempo de execução 2: 8547 s: 788888890 Tempo de execução 1: 11500 s: 788888890 Tempo de execução 2: 8547 s: 788888890 Tempo de execução 1: 11484 s: 788888890 Tempo de execução 2: 8547 s: 788888890
Uma solução realmente simples:
public int numLength(int n) {
for (int length = 1; n % Math.pow(10, length) != n; length++) {}
return length;
}
Ou então o comprimento que você pode verificar se o número é maior ou menor que o número desejado.
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);
}
}
}
Ainda não vi uma solução baseada em multiplicação. As soluções baseadas em logaritmo, divisão e cadeia de caracteres tornar-se-ão difíceis de manejar contra milhões de casos de teste, então aqui está um para 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;
}
Na base 10, isso funciona porque n é essencialmente comparado a 9, 99, 999 ... como min é 9, 90, 900 ... en é subtraído por 9, 90, 900 ...
Infelizmente, isso não é portátil long
apenas substituindo todas as instâncias do int
devido ao estouro. Por outro lado, acontece que ele irá trabalhar para bases 2 e 10 (mas mal não para a maioria das outras bases). Você precisará de uma tabela de pesquisa para os pontos de estouro (ou um teste de divisão ... 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;
}
Com design (baseado no problema). Esta é uma alternativa de dividir e conquistar. Primeiro, definiremos uma enumeração (considerando que é apenas para um int não assinado).
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;
}
}
Agora vamos definir uma classe que passa pelos valores da enumeração, comparar e retornar o comprimento apropriado.
public class IntegerLenght {
public static byte calculateIntLenght(int num) {
for(IntegerLength v : IntegerLength.values()) {
if(num < v.getValue()){
return v.getLenght();
}
}
return 0;
}
}
O tempo de execução dessa solução é o mesmo da abordagem de dividir e conquistar.
num>=Nine.getValue()
?
Quer-se fazer isso principalmente porque ele / ela quer "apresentá-lo", o que geralmente significa que ele finalmente precisa ser "editado por strings" (ou transformado de outra maneira) explícita ou implicitamente; antes de poder ser apresentado (impresso por exemplo).
Se for esse o caso, tente explicitar o "toString" necessário e conte os bits.
Podemos conseguir isso usando um loop recursivo
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+"]");
}
Eu escrevi essa função depois de procurar o Integer.java
código fonte.
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;
}
}
}
Eu vejo pessoas usando bibliotecas de String ou mesmo usando a classe Integer. Nada de errado com isso, mas o algoritmo para obter o número de dígitos não é tão complicado. Estou usando um longo neste exemplo, mas funciona tão bem com um int.
private static int getLength(long num) {
int count = 1;
while (num >= 10) {
num = num / 10;
count++;
}
return count;
}
API sem String, sem utilitários, sem conversão de tipo, apenas iteração java pura ->
public static int getNumberOfDigits(int input) {
int numOfDigits = 1;
int base = 1;
while (input >= base * 10) {
base = base * 10;
numOfDigits++;
}
return numOfDigits;
}
Você pode comprar valores maiores, se quiser.
int num = 02300;
int count = 0;
while(num>0){
if(num == 0) break;
num=num/10;
count++;
}
System.out.println(count);
Maneira fácil recursiva
int get_int_lenght(current_lenght, value)
{
if (value / 10 < 10)
return (current_lenght + 1);
return (get_int_lenght(current_lenght + 1, value))
}
não testado
Você poderia os dígitos usando a divisão sucessiva por dez:
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);
Digite o número e crie um Arraylist
, e o loop while registrará todos os dígitos no Arraylist
. Então podemos tirar o tamanho da matriz, que será o comprimento do valor inteiro que você digitou.
ArrayList<Integer> a=new ArrayList<>();
while(number > 0)
{
remainder = num % 10;
a.add(remainder);
number = number / 10;
}
int m=a.size();
Aqui está um método realmente simples que eu criei que funciona para qualquer número:
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;
}
O modo como funciona é com a variável do contador numérico é o espaço de 10 = 1 dígito. Por exemplo .1 = 1 décimo => espaço de 1 dígito. Portanto, se você tiver, int number = 103342;
receberá 6, porque isso equivale a 0,00001 espaços de volta. Além disso, alguém tem um nome de variável melhor paranumberCounter
? Não consigo pensar em nada melhor.
Edit: Apenas pensei em uma explicação melhor. Essencialmente, o que esse loop while está fazendo é fazer com que você divida seu número por 10, até que seja menor que um. Essencialmente, quando você divide algo por 10, você está movendo-o para trás um espaço numérico, então você simplesmente o divide por 10 até chegar a <1 para a quantidade de dígitos no seu número.
Aqui está outra versão que pode contar a quantidade de números em um decimal:
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;
}
Tente converter o int em uma sequência e obtenha o comprimento da sequência . Isso deve ter a duração do int .
public static int intLength(int num){
String n = Integer.toString(num);
int newNum = n.length();
return newNum;
}
number
é negativo.