web-dev-qa-db-fra.com

Manière d'obtenir le nombre de chiffres dans un int?

Existe-t-il un moyen plus simple d’obtenir la longueur d’un int que cette méthode?

int length = String.valueOf(1000).length();
358
fnst

Votre solution basée sur String est parfaitement OK, il n’ya rien de "mal à propos". Vous devez comprendre que, mathématiquement, les nombres n'ont pas de longueur ni de chiffres. La longueur et les chiffres sont les propriétés d’un représentation physique d’un nombre dans une base spécifique, c’est-à-dire une chaîne.

Une solution basée sur un logarithme fait (en partie) les mêmes choses que la solution basée sur String fait en interne, et le fait probablement (de manière non significative) plus rapidement car elle ne produit que la longueur et ignore les chiffres. Mais je ne considérerais pas cela plus clair dans l'intention - et c'est le facteur le plus important.

322
Michael Borgwardt

Le logarithme est votre ami:

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

NB: valable uniquement pour n> 0.

252
Dmitry Brant

L'approche la plus rapide: diviser pour régner.

En supposant que votre plage est comprise entre 0 et MAX_INT, vous avez alors 1 à 10 chiffres. Vous pouvez aborder cet intervalle en utilisant diviser pour régner, avec jusqu'à 4 comparaisons pour chaque entrée. Commencez par diviser [1..10] en [1..5] et [6..10] avec une comparaison, puis divisez chaque intervalle de longueur 5 en utilisant une comparaison en un intervalle de longueur 3 et un intervalle de longueur 2. L'intervalle de longueur 2 nécessite une comparaison supplémentaire (total de 3 comparaisons), l'intervalle de longueur 3 peut être divisé en un intervalle de longueur 1 (solution) et un intervalle de longueur 2. Donc, vous avez besoin de 3 ou 4 comparaisons.

Pas de divisions, pas d'opérations en virgule flottante, pas de logarithmes coûteux, seulement des comparaisons d'entiers.

Code (long mais rapide):

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

Benchmark (après l’échauffement de la JVM) - voir le code ci-dessous pour voir comment le benchmark a été exécuté:

  1. méthode de base (avec String.length): 2145ms
  2. méthode log10: 711 ms = 3,02 fois plus rapide que la référence
  3. division répétée: 2797ms = 0,77 fois plus rapide que la ligne de base
  4. diviser pour régner: 74 ms = 28,99
    fois plus rapide que la ligne de base

Code complet:

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 faster than baseline");

    c = new Chronometer(true);
    allMethod3();
    c.stop();
    System.out.println(c + " = " + StringTools.formatDouble((double)baseline / c.getValue() , "0.00") + " times faster than baseline");

    c = new Chronometer(true);
    allMethod4();
    c.stop();
    System.out.println(c + " = " + StringTools.formatDouble((double)baseline / c.getValue() , "0.00") + " times faster than 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;
}

Encore une fois, repère:

  1. méthode de base (avec String.length): 2145ms
  2. méthode log10: 711 ms = 3,02 fois plus rapide que la référence
  3. division répétée: 2797ms = 0,77 fois plus rapide que la ligne de base
  4. diviser pour régner: 74 ms = 28,99
    fois plus rapide que la ligne de base

Edit: Après avoir écrit le benchmark, j’ai pris un aperçu de Integer.toString à partir de Java 6, et j’ai constaté qu’il utilisait:

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

Je l'ai comparé à ma solution diviser pour mieux régner:

  1. diviser pour régner: 104ms
  2. Solution Java 6 - itérer et comparer: 406ms

Le mien est environ 4x plus vite.

151
Marian

Deux commentaires sur votre benchmark: Java est un environnement complexe, avec compilation juste-à-temps, collecte des déchets, etc., donc pour obtenir une comparaison équitable, chaque fois que je lance un benchmark, je toujours: ( a) entourez les deux tests d’une boucle qui les exécute en séquence 5 ou 10 fois. Très souvent, le temps d'exécution lors du deuxième passage dans la boucle est assez différent du premier. Et (b) Après chaque "approche", je fais un System.gc () pour essayer de déclencher un garbage collection. Sinon, la première approche peut générer un ensemble d'objets, mais pas assez pour forcer une récupération de place, puis la seconde approche crée quelques objets, le segment de mémoire est épuisé et la collecte de place est exécutée. Ensuite, la deuxième approche est "chargée" pour ramasser les déchets laissés par la première approche. C'est vraiment injuste!

Cela dit, aucun des éléments ci-dessus n'a fait de différence significative dans cet exemple.

Avec ou sans ces modifications, j'ai eu des résultats très différents de ceux que vous avez obtenus. Lorsque j’ai exécuté cela, oui, l’approche toString donnait des temps d’exécution de 6400 à 6600 millisecondes, tandis que l’approche du journal atteignait 20 000 à 20 400 millisecondes. Au lieu d'être légèrement plus rapide, l'approche du journal était 3 fois plus lente pour moi.

Notez que les deux approches impliquent des coûts très différents, ce qui n’est donc pas totalement choquant: l’approche toString créera un grand nombre d’objets temporaires qui doivent être nettoyés, tandis que l’approche de journalisation prend plus de temps à calculer. Donc, peut-être que la différence est que sur une machine avec moins de mémoire, toString nécessite plus de cycles de récupération de place, alors que sur une machine avec un processeur plus lent, le calcul supplémentaire du journal serait plus pénible.

J'ai aussi essayé une troisième approche. J'ai écrit cette petite fonction:

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

Cela a fonctionné dans 1600 à 1900 millis - moins de 1/3 de l'approche toString, et 1/10 l'approche de notation sur ma machine.

Si vous avez une large gamme de chiffres, vous pouvez accélérer le processus en commençant par diviser par 1 000 ou 1 000 000 afin de réduire le nombre de fois dans la boucle. Je n'ai pas joué avec ça.

13
Jay

tilisation de Java

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

utilisez import Java.lang.Math.*; au début

en utilisant C

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

utilisez inclue math.h au début

10
Santosh

Je ne peux pas encore laisser de commentaire, alors je posterai une réponse séparée.

La solution basée sur le logarithme ne calcule pas le nombre correct de chiffres pour les très grands entiers longs, par exemple:

long n = 99999999999999999L;

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

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

La solution basée sur le logarithme calcule le nombre incorrect de chiffres dans les entiers les plus grands

8
moodcheerful

Puisque le nombre de chiffres en base 10 d'un entier est juste 1 + tronqué (log10 (nombre)) , vous pouvez faire:

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

Edited parce que ma dernière édition a corrigé l'exemple de code, mais pas la description.

8
Dirk

La solution de Marian est adaptée pour long numéros (jusqu'à 9 223 372 036 854 775 807), au cas où quelqu'un voudrait le copier-coller. Dans le programme, j'ai écrit ceci pour des nombres allant jusqu'à 10000 étant beaucoup plus probables, j'ai donc créé une branche spécifique pour eux. Quoi qu'il en soit, cela ne fera pas une différence significative.

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;
                }
            }
        }
    }
}
5
J.A.I.L.

Que diriez-vous de vieilles mathématiques simples? Divisez par 10 jusqu'à atteindre 0.

public static int getSize(long number) {
        int count = 0;
        while (number > 0) {
            count += 1;
            number = (number / 10);
        }
        return count;
    }
3
Sinista

Puis-je essayer? ;)

basé sur la solution de Dirk

final int digits = number==0?1:(1 + (int)Math.floor(Math.log10(Math.abs(number))));
3
DmitryK

Une autre approche de chaîne. Short and sweet - pour tout entier n.

int length = ("" + n).length();
3
ThisClark

La solution de Marian, maintenant avec 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)));
    }

Parce que nous pouvons.

2
UserNotFound

Curieux, j'ai essayé de le comparer ...

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


    }
}

les résultats sont:

 Temps d'exécution 1: 6765 
 S: 400000000 
 Temps d'exécution 2: 6000 
 S: 400000000 

Il me reste maintenant à me demander si mon point de référence signifie réellement quelque chose, mais j'obtiens des résultats cohérents (variations à l'intérieur d'une ms) sur plusieurs exécutions du point de référence lui-même ... :) On dirait qu'il est inutile d'essayer d'optimiser cela ...


modifier: après le commentaire de ptomli, j'ai remplacé "nombre" par "i" dans le code ci-dessus et obtenu les résultats suivants sur 5 manches du banc:

 Temps d'exécution 1: 11500 
 S: 788888890 
 Temps d'exécution 2: 8547 
 S: 788888890 
 
 Temps d'exécution 1: 11485 
 S: 788888890 
 Temps d'exécution 2: 8547 
 S: 788888890 
 
 Temps d'exécution 1: 11469 
 S: 788888890 
 Temps d'exécution 2: 8547 
 S: 788888890 
 
 Temps d'exécution 1: 11500 
 S: 788888890 
 Temps d'exécution 2: 8547 
 S: 788888890 
 
 Temps d'exécution 1: 11484 
 S: 788888890 
 Temps d'exécution 2: 8547 
 S: 788888890 
1
Jean

Je n'ai pas encore vu de solution basée sur la multiplication. Les solutions logarithmiques, divisives et basées sur des chaînes de caractères deviendront plutôt difficiles à manier face à des millions de tests élémentaires. En voici donc une pour 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 base 10, cela fonctionne car n est essentiellement comparé à 9, 99, 999 ... alors que min est égal à 9, 90, 900 ... et que n est soustrait par 9, 90, 900 ...

Malheureusement, ceci n'est pas portable pour long simplement en remplaçant chaque instance de int pour cause de débordement. Par contre, il se trouve que volonté fonctionnera pour les bases 2 et 10 (mais échouera gravement pour la plupart des autres bases). Vous aurez besoin d'une table de recherche pour les points de débordement (ou d'un test de division ... 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;
}
0
Jonathan Smith

Avec design (basé sur le problème). Ceci est une alternative de diviser pour régner. Nous allons d’abord définir un enum (considérant que c’est seulement pour un unsigned int).

public enum IntegerLength {
    One((byte)1,10),
    Two((byte)2,100),
    Three((byte)3,1000),
    Four((byte)4,10000),
    Five((byte)5,100000),
    Six((byte)6,1000000),
    Seven((byte)7,10000000),
    Eight((byte)8,100000000),
    Nine((byte)9,1000000000);

    byte length;
    int value;

    IntegerLength(byte len,int value) {
        this.length = len;
        this.value = value;
    }

    public byte getLenght() {
        return length;
    }

    public int getValue() {
        return value;
    }
}

Nous allons maintenant définir une classe qui passe par les valeurs de l'énum, ​​comparer et renvoyer la longueur appropriée.

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

Le temps d’exécution de cette solution est identique à l’approche Diviser pour régner.

0
androider

Ou à la place de la longueur, vous pouvez vérifier si le nombre est plus grand ou plus petit que le nombre souhaité.

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

}

0
Szabi Zsoldos

On veut le faire surtout parce qu'il/elle veut le "présenter", ce qui signifie surtout qu'il doit finalement être "toString-ed" (ou transformé d'une autre manière) de manière explicite ou implicite de toute façon; avant qu'il puisse être présenté (imprimé par exemple).

Si tel est le cas, essayez simplement de rendre explicite le "toString" nécessaire et de compter les bits.

0

Nous pouvons y arriver en utilisant une boucle récursive

    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+"]");
    }
0
ericdemo07

J'ai écrit cette fonction après avoir consulté le code source Integer.Java.

private static int stringSize(int x) {
    final int[] sizeTable = {9, 99, 999, 9_999, 99_999, 999_999, 9_999_999,
            99_999_999, 999_999_999, Integer.MAX_VALUE};
    for (int i = 0; ; ++i) {
        if (x <= sizeTable[i]) {
            return i + 1;
        }
    }
}
0
shellhub

Une solution vraiment simple:

public int numLength(int n) {
  for (int length = 1; n % Math.pow(10, length) != n; length++) {}
  return length;
}
0
VoidCatz

Qu'en est-il de cette méthode récursive?

    private static int length = 0;

    public static int length(int n) {
    length++;
    if((n / 10) < 10) {
        length++;
    } else {
        length(n / 10);
    }
    return length;
}
0
Jedi Dula

solution simple:

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