Existe-t-il un moyen plus simple d’obtenir la longueur d’un int que cette méthode?
int length = String.valueOf(1000).length();
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.
Le logarithme est votre ami:
int n = 1000;
int length = (int)(Math.log10(n)+1);
NB: valable uniquement pour n> 0.
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é:
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:
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:
Le mien est environ 4x plus vite.
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.
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
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);
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.
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;
}
}
}
}
}
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;
}
Puis-je essayer? ;)
basé sur la solution de Dirk
final int digits = number==0?1:(1 + (int)Math.floor(Math.log10(Math.abs(number))));
Une autre approche de chaîne. Short and sweet - pour tout entier n
.
int length = ("" + n).length();
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.
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
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;
}
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.
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);
}
}
}
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.
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+"]");
}
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;
}
}
}
Une solution vraiment simple:
public int numLength(int n) {
for (int length = 1; n % Math.pow(10, length) != n; length++) {}
return length;
}
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;
}
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);
}