web-dev-qa-db-fra.com

Comment récupérer efficacement le premier chiffre décimal du nombre

Une solution évidente est:

int n = 2134;
while(n > 9)
    n /= 10;

ce qui prend du temps linéaire. Pouvons-nous faire plus vite?

Est-ce plus rapide que le temps linéaire:

char s[100];
sprintf(s, "%d", n);
n = s[0]-'0';

Quels sont les autres moyens (l'efficacité est la préoccupation principale)?
J'ai vu ceci , sauf que je n'ai besoin que de trouver le premier chiffre. (En outre, je ne comprends pas la réponse).

13
mohit

Certains processeurs ont des instructions qui calculent "quelle taille" un nombre est très rapide (voir http://en.wikipedia.org/wiki/Leading_zero_count ). Cela peut être utilisé pour choisir rapidement une puissance de 10 et diviser par celle-ci, au lieu de diviser par 10 à plusieurs reprises.

Supposons qu'on vous donne une fonction clz qui calcule le nombre de bits initiaux nuls dans la représentation binaire d'un nombre (0 ... 32). Ensuite, vous pouvez utiliser une table de correspondance donnant la puissance appropriée de 10 pour chaque nombre de zéros non significatifs.

uint32_t powers_of_10[33] = {
    1000000000, 1000000000,
    100000000, 100000000, 100000000,
    10000000, 10000000, 10000000,
    1000000, 1000000, 1000000, 1000000,
    100000, 100000, 100000,
    10000, 10000, 10000,
    1000, 1000, 1000, 1000,
    100, 100, 100,
    10, 10, 10,
    1, 1, 1, 1, 1
};

int CalcFirstDecimalDigit(uint32_t x)
{
    int leading_zeros = clz(x);
    x /= powers_of_10[leading_zeros];
    if (x >= 10)
        return 1;
    else
        return x;
}
21
anatolyg

par exemple. pour en 32 bits non signé:

Étape 1: détermine (par recherche binaire) à quel intervalle la valeur est: 

0 .. 9
10 .. 99
100 .. 999
1000 .. 9999
10000 .. 99999
100000 .. 999999
1000000 .. 9999999
10000000 .. 99999999
100000000 .. 999999999
1000000000 .. 4294967295

prend au maximum 4 comparés

Étape 2:  

Que calculer le chiffre de tête par une division.

13
MrSmith42

vous pouvez le faire en O(1) temps constant, mais aux dépens d'une très grande utilisation de la mémoire. C'est le même vieux compromis temps/mémoire.

Vous pouvez créer une table de correspondance de 2 ^ 31 entrées (signé int), 4 bits par entrée (avec 4 bits, vous pouvez coder le premier chiffre 1-9 du nombre en représentation décimale).

alors vous pouvez utiliser votre int pour accéder à la table de correspondance et obtenir le premier chiffre dans O (1). La table de consultation prendra 2 ^ 31 * 4 bits -> 1024 Mo 

c'est le moyen le plus rapide auquel je puisse penser ... 

5
Gianluca Ghettini

Votre deuxième exemple devrait utiliser sprintf. Quoi qu'il en soit, cela ne peut pas être plus rapide car le numéro entier est imprimé, ainsi tous les chiffres sont recherchés.

La question/réponse liée utilise une propriété de logarithme: pour un nombre de x chiffres, son logarithme en base 10 est compris entre x et x+1. Mais, en raison d’erreurs en virgule flottante, cette méthode ne fonctionne pas correctement dans certains cas. Prenez également en compte le fait que la virgule flottante est plus lente que l'arithmétique entière.

Ainsi, la solution la plus simple est aussi la plus rapide.

5
Mihai Maruseac

Je suis à peu près sûr que sprintf (comme je le suppose) sera considérablement plus lent. Vous pouvez faire une optimisation pour réduire le nombre d'opérations de division (qui est l'une des instructions les plus lentes sur presque tous les processeurs). 

Donc, on pourrait faire quelque chose comme ça:

 while(n > 10000)
   n /= 1000;

 while(n >= 9)
   n /= 10;

Bien sûr, si la vitesse est vraiment importante. 

5
Mats Petersson

Voici une sorte de variation sur une recherche binaire. Comme une recherche binaire, il s’agit de O (log n). La rapidité dépend de la rapidité avec laquelle vous pouvez diviser des nombres entiers.

if (n >= 100000000)
    n /= 100000000
if (n >= 10000)
    n /= 10000
if (n >= 100)
    n /= 100
if (n >= 10)
    n /= 10

La méthode se développe facilement pour les entiers avec une plage plus grande.

3
Mark Ransom

Vous pouvez faire simplement ceci:

//Shashank Jain
#include<iostream>
#include<cmath>
using namespace std;
int main()
{
    int num,fdigit;
    cin>>num;
    if(num<0)
        num*=-1;
    int l=log10(num); // l = (length of number -1)

    fdigit=num/pow(10,l);

    cout<<fdigit<<endl;
    return 0;
}
2
Shashank Jain
int FirstDigit ( int Number ) {

    // to obtain the <number of digits -1> use this math formula:

    int DigitsInNumber = (int) lg(Number);           

    long TenExpon = pow(10, DigitsInNumber);

    return (Number / TenExpon);                //first digit
}

aussi: lg(n) = ln(n) / ln(10);

1
Dumitriu Sebi

    for(int i=0; i<n; i++)
    {  
        e=5; //Specify the number of digits in the number OR Exponential value of 10
        while(e>=0)
        {   
            tenp=pow(10,e); //#include <math.h>
            if(arr[i]/tenp!=0)
            {
                q[i][e]=arr[i]/tenp%10;
            }
            e--;
        }
    }

Cependant, la complexité de ce code doit être O (n ^ 2), ce qui n’est pas souhaitable.

0
Afrah

Votre première solution (en supposant que n soit> = 0) est presque optimale et je pense qu’elle ne pourrait être sensiblement améliorée qu’en utilisant le langage Assembly en ligne. Mais cela ne vaut la peine que si vous traitez des millions de ces nombres.

Votre deuxième solution est - comment puis-je mettre cela bien? - plus d'une approche Java-ish: Performance? La-di-da, qui se soucie de ...

0
TonyK