¿Hay una forma más ordenada para obtener la longitud de un int que este método?
int length = String.valueOf(1000).length();
¿Hay una forma más ordenada para obtener la longitud de un int que este método?
int length = String.valueOf(1000).length();
Respuestas:
Su solución basada en cadenas está perfectamente bien, no hay nada "descuidado" al respecto. Tienes que darte cuenta de que matemáticamente, los números no tienen una longitud, ni tienen dígitos. La longitud y los dígitos son propiedades de una representación física de un número en una base específica, es decir, una cadena.
Una solución basada en logaritmo hace (algunas de) las mismas cosas que la basada en String hace internamente, y probablemente lo hace (insignificantemente) más rápido porque solo produce la longitud e ignora los dígitos. Pero en realidad no lo consideraría más claro en su intención, y ese es el factor más importante.
Math.abs()
Sin embargo, agregar arreglará esto.
El logaritmo es tu amigo:
int n = 1000;
int length = (int)(Math.log10(n)+1);
NB: solo válido para n> 0.
El enfoque más rápido: divide y vencerás.
Suponiendo que su rango es de 0 a MAX_INT, entonces tiene de 1 a 10 dígitos. Puede acercarse a este intervalo utilizando divide y vencerás, con hasta 4 comparaciones por cada entrada. Primero, divide [1..10] en [1..5] y [6..10] con una comparación, y luego cada intervalo de longitud 5 que divide mediante una comparación en un intervalo de longitud 3 y uno de longitud 2. El intervalo de longitud 2 requiere una comparación más (total 3 comparaciones), el intervalo de longitud 3 se puede dividir en un intervalo de longitud 1 (solución) y un intervalo de longitud 2. Entonces, necesitas 3 o 4 comparaciones.
Sin divisiones, sin operaciones de coma flotante, sin costosos logaritmos, solo comparaciones enteras.
Código (largo pero 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;
}
}
}
Punto de referencia (después del calentamiento de JVM): consulte el código a continuación para ver cómo se ejecutó el punto de referencia:
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;
}
De nuevo, punto de referencia:
Editar: Después de escribir el punto de referencia, eché un vistazo a Integer.toString desde Java 6, y descubrí que 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;
}
Lo comparé con mi solución de divide y vencerás:
El mío es aproximadamente 4 veces más rápido que la solución 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
Dos comentarios sobre su punto de referencia: Java es un entorno complejo, con compilación justo a tiempo y recolección de basura, etc., para obtener una comparación justa, cada vez que ejecuto un punto de referencia, siempre: (a) adjunto las dos pruebas en un bucle que los ejecuta en secuencia 5 o 10 veces. Muy a menudo, el tiempo de ejecución en el segundo paso a través del bucle es bastante diferente del primero. Y (b) Después de cada "enfoque", hago un System.gc () para intentar desencadenar una recolección de basura. De lo contrario, el primer enfoque podría generar un montón de objetos, pero no lo suficiente como para forzar una recolección de basura, luego el segundo enfoque crea algunos objetos, el montón se agota y se ejecuta la recolección de basura. Luego, el segundo enfoque se "carga" por recoger la basura que deja el primer enfoque. ¡Muy injusto!
Dicho esto, ninguno de los anteriores hizo una diferencia significativa en este ejemplo.
Con o sin esas modificaciones, obtuve resultados muy diferentes a los de usted. Cuando ejecuté esto, sí, el enfoque toString dio tiempos de ejecución de 6400 a 6600 millis, mientras que el enfoque de registro topok 20,000 a 20,400 millis. En lugar de ser un poco más rápido, el enfoque de registro fue 3 veces más lento para mí.
Tenga en cuenta que los dos enfoques implican costos muy diferentes, por lo que esto no es totalmente impactante: el enfoque toString creará muchos objetos temporales que deben limpiarse, mientras que el enfoque de registro requiere un cálculo más intenso. Entonces, tal vez la diferencia es que en una máquina con menos memoria, toString requiere más rondas de recolección de basura, mientras que en una máquina con un procesador más lento, el cálculo adicional del registro sería más doloroso.
También probé un tercer enfoque. Escribí esta pequeña función:
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;
}
Eso funcionó en 1600 a 1900 milis, menos de 1/3 del enfoque toString y 1/10 del enfoque de registro en mi máquina.
Si tuviera un amplio rango de números, podría acelerarlo aún más dividiendo entre 1,000 o 1,000,000 para reducir el número de veces a través del ciclo. No he jugado con eso.
Usando Java
int nDigits = Math.floor(Math.log10(Math.abs(the_integer))) + 1;
usar import java.lang.Math.*;
al principio
Usando C
int nDigits = floor(log10(abs(the_integer))) + 1;
usar inclue math.h
al principio
the_integer
es 0
así, así que verifique eso.
No puedo dejar un comentario todavía, así que lo publicaré como una respuesta separada.
La solución basada en logaritmo no calcula el número correcto de dígitos para enteros largos muy grandes, por ejemplo:
long n = 99999999999999999L;
// correct answer: 17
int numberOfDigits = String.valueOf(n).length();
// incorrect answer: 18
int wrongNumberOfDigits = (int) (Math.log10(n) + 1);
La solución basada en logaritmo calcula el número incorrecto de dígitos en enteros grandes
Como el número de dígitos en la base 10 de un entero es solo 1 + truncado (log10 (número)) , puede hacer:
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 mi última edición solucionó el código de ejemplo, pero no la descripción.
Math.floor
es un poco redundante, ¿no? Lanzarlo int
lo redondeará de todos modos.
La solución de Marian se adaptó para números de tipo largos (hasta 9,223,372,036,854,775,807), en caso de que alguien quiera copiarlo y pegarlo. En el programa escribí esto para números hasta 10000 que eran mucho más probables, así que hice una rama específica para ellos. De todos modos, no hará una diferencia 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;
}
}
}
}
}
¿Qué hay de las viejas matemáticas simples? Divide entre 10 hasta llegar a 0.
public static int getSize(long number) {
int count = 0;
while (number > 0) {
count += 1;
number = (number / 10);
}
return count;
}
Long.MAX_VALUE
, cuál es el peor caso de complejidad de su código, y úselo System.nanoTime()
para hacer una prueba de reloj contra los peores casos de complejidad de la otra solución. ++ En realidad, pruébelo con una matriz llena por un aleatorizador establecido en el rango de 0
a Long.MAX_VALUE
también, solo para la prueba de "complejidad promedio" ++ Puede encontrar los resultados ... muy impactantes.
int,
este bucle se ejecuta un máximo de 11 veces. ¿Tienes alguna evidencia de tus afirmaciones?
La solución de Marian, ahora con 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 podemos
Curioso, traté de compararlo ...
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);
}
}
los resultados son:
Tiempo de ejecución 1: 6765 s: 400000000 Tiempo de ejecución 2: 6000 s: 400000000
Ahora me pregunto si mi punto de referencia realmente significa algo, pero obtengo resultados consistentes (variaciones dentro de un ms) en varias ejecuciones del punto de referencia en sí ... :) Parece que es inútil tratar de optimizar esto ...
editar: siguiendo el comentario de ptomli, reemplacé 'número' por 'i' en el código anterior y obtuve los siguientes resultados en 5 ejecuciones del banco:
Tiempo de ejecución 1: 11500 s: 788888890 Tiempo de ejecución 2: 8547 s: 788888890 Tiempo de ejecución 1: 11485 s: 788888890 Tiempo de ejecución 2: 8547 s: 788888890 Tiempo de ejecución 1: 11469 s: 788888890 Tiempo de ejecución 2: 8547 s: 788888890 Tiempo de ejecución 1: 11500 s: 788888890 Tiempo de ejecución 2: 8547 s: 788888890 Tiempo de ejecución 1: 11484 s: 788888890 Tiempo de ejecución 2: 8547 s: 788888890
Una solución realmente simple:
public int numLength(int n) {
for (int length = 1; n % Math.pow(10, length) != n; length++) {}
return length;
}
O, en cambio, la longitud que puede verificar si el número es mayor o menor que el número deseado.
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);
}
}
}
Todavía no he visto una solución basada en la multiplicación. Las soluciones basadas en logaritmos, divisiones y cadenas se volverán bastante difíciles de manejar en millones de casos de prueba, así que aquí hay una 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;
}
En la base 10, esto funciona porque n se compara esencialmente con 9, 99, 999 ... como min es 9, 90, 900 ... yn se resta por 9, 90, 900 ...
Desafortunadamente, esto no es portátil long
simplemente reemplazando cada instancia int
debido a un desbordamiento. Por otro lado, da la casualidad de que va a trabajar para las bases 2 y 10 (pero mal no para la mayoría de las otras bases). Necesitará una tabla de búsqueda para los puntos de desbordamiento (o una prueba de división ... 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;
}
Con diseño (basado en el problema). Esta es una alternativa de divide y vencerás. Primero definiremos una enumeración (considerando que es solo para un int sin signo).
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;
}
}
Ahora definiremos una clase que pasa por los valores de la enumeración y compara y devuelve la longitud adecuada.
public class IntegerLenght {
public static byte calculateIntLenght(int num) {
for(IntegerLength v : IntegerLength.values()) {
if(num < v.getValue()){
return v.getLenght();
}
}
return 0;
}
}
El tiempo de ejecución de esta solución es el mismo que el enfoque de divide y vencerás.
num>=Nine.getValue()
?
Uno quiere hacer esto principalmente porque quiere "presentarlo", lo que significa que finalmente debe ser "toString-ed" (o transformado de otra manera) explícita o implícitamente de todos modos; antes de que pueda ser presentado (impreso, por ejemplo).
Si ese es el caso, simplemente intente hacer explícita la "toString" necesaria y cuente los bits.
Podemos lograr esto usando un bucle 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+"]");
}
Escribí esta función después de buscar el Integer.java
código fuente.
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;
}
}
}
Veo personas que usan bibliotecas de cadenas o incluso usan la clase Integer. No tiene nada de malo, pero el algoritmo para obtener el número de dígitos no es tan complicado. Estoy usando un largo en este ejemplo, pero funciona igual de bien con un int.
private static int getLength(long num) {
int count = 1;
while (num >= 10) {
num = num / 10;
count++;
}
return count;
}
sin String API, sin utilidades, sin conversión de tipo, solo pura iteración de Java ->
public static int getNumberOfDigits(int input) {
int numOfDigits = 1;
int base = 1;
while (input >= base * 10) {
base = base * 10;
numOfDigits++;
}
return numOfDigits;
}
Puede optar por valores más grandes si lo desea.
Manera recursiva fácil
int get_int_lenght(current_lenght, value)
{
if (value / 10 < 10)
return (current_lenght + 1);
return (get_int_lenght(current_lenght + 1, value))
}
no probado
Podrías los dígitos usando la división sucesiva por diez:
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);
Ingrese el número y cree un Arraylist
, y el ciclo while registrará todos los dígitos en el Arraylist
. Luego podemos extraer el tamaño de la matriz, que será la longitud del valor entero que ingresó.
ArrayList<Integer> a=new ArrayList<>();
while(number > 0)
{
remainder = num % 10;
a.add(remainder);
number = number / 10;
}
int m=a.size();
Aquí hay un método realmente simple que hice que funciona para cualquier 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;
}
La forma en que funciona es con la variable del contador de números es que 10 = 1 dígito de espacio. Por ejemplo .1 = 1 décimo => espacio de 1 dígito. Por lo tanto, si tiene int number = 103342;
, obtendrá 6, porque ese es el equivalente de .000001 espacios de vuelta. Además, ¿alguien tiene un mejor nombre de variable paranumberCounter
? No se me ocurre nada mejor.
Editar: solo pensé en una mejor explicación. Esencialmente, lo que está haciendo este ciclo while es hacer que divida su número entre 10, hasta que sea menor que uno. Esencialmente, cuando divide algo por 10, lo mueve hacia atrás un espacio numérico, por lo que simplemente lo divide por 10 hasta llegar a <1 para la cantidad de dígitos en su número.
Aquí hay otra versión que puede contar la cantidad de números en un 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;
}
Intente convertir el int en una cadena y luego obtenga la longitud de la cadena . Eso debería obtener la longitud de la int .
public static int intLength(int num){
String n = Integer.toString(num);
int newNum = n.length();
return newNum;
}
number
sea negativo.