Bir int rakamlar elde etmek için yolu?


386

Bir int uzunluğunu elde etmek için bu yöntemden daha temiz bir yol var mı?

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

7
int uzunluğunu tanımlayınız.
Tom

24
Sanırım sayıdaki rakamları saymak istiyor.
Alberto Zaccagni

3
İnsanların size verdiği cevaplar doğrudur ... bir dizeye dönüştürmeden size int uzunluğunu verir ... ama neden bir dizeye dönüştürmek istemiyorsunuz? Bu hızlı bir şey mi? Eğer öyleyse, bu yöntemlerin daha hızlı olacağına ikna olmadım ... bazı testler yapmak isteyebilirsiniz (veya önemli olup olmadığına karar verebilirsiniz.)
Beska

3
@ptomli onaltılık basamaklar, yalnızca farklı bir temel sistemde bulunan basamaklardır.
Mark Pim

2
@Ptomli Tabii, ancak Integer.toString işlevinde ve genel konuşmada, ondalık varsayılan değerdir. Banka bana "Çekinizin miktarını bu kutuya yaz" dediğinde, onlara ondalık, onaltılık veya sekizlik yazıp yazmamam gerektiğini sormuyorum. Bağlam tarafından aksi belirtilmedikçe veya çağrılmadığı sürece ondalık sayılır.
Jay

Yanıtlar:


349

Dize tabanlı çözümünüz gayet iyi, bu konuda "düzgün" hiçbir şey yok. Matematiksel olarak, sayıların uzunluğunun veya rakamlarının olmadığını fark etmelisiniz. Uzunluk ve basamaklar, belirli bir tabandaki bir sayının fiziksel bir temsilinin , yani bir String'in özellikleridir.

Logaritma tabanlı bir çözüm, String tabanlı olanın dahili olarak yaptığı şeylerin bazılarını (bazıları) yapar ve muhtemelen (önemsiz olarak) daha hızlı yapar çünkü yalnızca uzunluğu üretir ve rakamları yok sayar. Ama aslında bunu daha net olarak düşünmezdim - ve bu en önemli faktör.


54
Sorunu çözmek için bir yol seçerken kodun amacını düşünün +1
pupeno

5
Datapoint: Makinemde, günlük yöntemi dize uzunluğu yöntemlerinin iki katından daha hızlı çalışıyor gibi görünüyor. Eğer yöntem çok veya kodun zaman açısından kritik bir bölümünde çağrılırsa ben bu önemsiz çağırmak olmaz.
CPerkins

1
Aşağıdaki kıyaslama birimi testime bakın (ben de kusurlu olabilirim ben hiçbir kıyaslama uzmanı değilim). Çok sayıda koşuda (100 000 000), makinemdeki hız neredeyse iki kat daha hızlıdır.
Jean

5
@CPerkins. Erken optimizasyon. Spiel'i biliyorsun.
Michael Borgwardt

11
Bazı (oldukça geç) eklemeler: "-" rakamı olup olmadığına bağlı olarak negatif değerler için düzgün çalışmayabilir. Eklemek Math.abs()bunu düzeltir.
YingYang

265

Logaritma senin arkadaşın:

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

Not: sadece n> 0 için geçerlidir.


2
Ve bu benim varyantımı kullanmaktan daha mı hızlı?
fnst

+1 Beni bir saniye dövüyorsun ve cevabın doğru, benimki biraz kapalıydı. Bununla birlikte, derleyicinin int
Dirk

2
@Tom Neden pahalı olduğunu düşünüyorsun? Matematik yardımcı işlemcisinin onu çalıştıracağı varsayılabilir, bu yüzden bir ekleme hızına yakın olabilir. Java şimdi ortak işlemciyi kullanmasa bile, bunun olabileceği iyi bir varsayımdır ... Eğer olsaydınız shootout.alioth.debian.org adresine gidip kendiniz öğrenin)
Bill K

8
Çalışır ... kontrol ettiğiniz değer = 0 değilse, bu size garip sonuçlar verecektir (-2147483647). Math.log10 API: "Bağımsız değişken pozitif sıfır veya negatif sıfır ise, sonuç negatif sonsuzdur."
mujimu

2
+1 GC koleksiyonlarından kaçınmak için yeniden kullanımı en üst düzeye çıkarmak için bir zorunluluk olan nesne bellek ayırmalarını içermeyen bir yöntem sunmak.
Michael Wojcik

159

En hızlı yaklaşım: böl ve fethet.

Aralığınızın 0 ila MAX_INT olduğunu varsayarsak, 1 ila 10 rakamınız vardır. Bu aralığa, her giriş için en fazla 4 karşılaştırma ile böl ve fethet komutunu kullanarak yaklaşabilirsiniz. İlk olarak, bir karşılaştırma ile [1..10] 'u [1..5] ve [6..10]' a böldükten sonra, her bir uzunluk 5 aralığını bir karşılaştırma 3 ile bir uzunluk 2 aralığına böldüğünüz. Uzunluk 2 aralığı bir daha karşılaştırma (toplam 3 karşılaştırma) gerektirir, uzunluk 3 aralığı uzunluk 1 aralığına (çözelti) ve uzunluk 2 aralığına bölünebilir. Yani, 3 veya 4 karşılaştırmaya ihtiyacınız var.

Bölme yok, kayan nokta işlemi yok, pahalı logaritma yok, sadece tamsayı karşılaştırma.

Kod (uzun ama hızlı):

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

Karşılaştırma ölçütü (JVM ısınmasından sonra) - karşılaştırmalı değerlendirmenin nasıl yürütüldüğünü görmek için aşağıdaki koda bakın:

  1. temel yöntem (String.length ile): 2145ms
  2. log10 yöntemi: 711ms = taban çizgisinin 3.02 katı
  3. tekrarlanan bölünme: 2797ms = taban çizgisinden 0,77 kat daha hızlı
  4. böl ve fethet: 74ms =
    taban çizgisinden 28.99 kat daha hızlı

Tam kod:

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

Yine, karşılaştırma ölçütü:

  1. temel yöntem (String.length ile): 2145ms
  2. log10 yöntemi: 711ms = taban çizgisinin 3.02 katı
  3. tekrarlanan bölünme: 2797ms = taban çizgisinden 0,77 kat daha hızlı
  4. böl ve fethet: 74ms =
    taban çizgisinden 28.99 kat daha hızlı

Düzenleme: Ben kriter yazdıktan sonra, Java 6 Integer.toString içine gizlice bir tepe aldı ve ben kullanır:

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

Böl ve fethet çözümümle karşılaştırdım:

  1. böl ve fethet: 104ms
  2. Java 6 çözümü - yineleme ve karşılaştırma: 406ms

Benimki Java 6 çözümünden yaklaşık 4 kat daha hızlı.


7
harika görünüyor. daha fazla kabul görmek için
?:

88
erken optimizasyon hakkında konuşma: D
Gordon Gustafson

2
Bunu sevdim! İç içe ifsesees yerine anahtar bloğuna ne dersiniz?
Kebman

2
Başka ifadeler int sonra Dize dönüştürme sonra .length çağıran çok daha hızlı olurdu tüm bunları fark etmedi. +1
Ogen

15
Üçlü operatörü kullanarak, onu 101 karaktere 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
indirir

13

Karşılaştırmanız hakkında iki yorum: Java, tam zamanında derleme ve çöp toplama vb.İle karmaşık bir ortamdır, bu nedenle adil bir karşılaştırma elde etmek için, her kıyaslama yaptığımda, her zaman: (a) iki testi de dahil ediyorum 5 veya 10 kez sırayla çalıştıran bir döngüde. Çoğunlukla döngüden ikinci geçişteki çalışma süresi birinciden oldukça farklıdır. Ve (b) Her "yaklaşım" dan sonra, bir çöp toplama tetiklemek için bir System.gc () yapmak. Aksi takdirde, ilk yaklaşım bir grup nesne oluşturabilir, ancak bir çöp koleksiyonunu zorlamak için yeterli değildir, daha sonra ikinci yaklaşım birkaç nesne oluşturur, yığın tükenir ve çöp toplama çalışır. Daha sonra ikinci yaklaşım, ilk yaklaşımın bıraktığı çöpleri toplamak için "yüklenir". Çok haksız!

Bununla birlikte, yukarıdakilerin hiçbiri bu örnekte önemli bir fark yaratmadı.

Bu modifikasyonlarla veya modifikasyonlar olmadan, sizden çok farklı sonuçlar aldım. Bunu çalıştırdığımda, evet, toString yaklaşımı 6400 ila 6600 milis çalışma süreleri verirken, günlük yaklaşımı 20.000 ila 20.400 milis topok verdi. Biraz daha hızlı olmak yerine, günlük yaklaşımı benim için 3 kat daha yavaştı.

İki yaklaşımın çok farklı maliyetler içerdiğini unutmayın, bu tamamen şok edici değildir: toString yaklaşımı, temizlenmesi gereken çok sayıda geçici nesne yaratırken günlük yaklaşımı daha yoğun hesaplama gerektirir. Bu yüzden belki de fark, daha az belleğe sahip bir makinede, toString'in daha fazla çöp toplama turu gerektirmesi, daha yavaş işlemciye sahip bir makinede ise, fazladan günlük hesaplaması daha acı verici olacaktır.

Ayrıca üçüncü bir yaklaşım denedim. Bu küçük işlevi yazdım:

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

Bu 1600 ila 1900 milisaniyede koştu - toString yaklaşımının 1 / 3'ünden daha az ve makinemdeki günlük yaklaşımının 1 / 10'u.

Geniş bir sayı aralığınız varsa, döngüdeki sayısını azaltmak için 1.000 veya 1.000.000'a bölünerek başlatabilirsiniz. Bununla oynamadım.


Girişi değiştirmeyi denediniz mi? Hotspot VM bu grafiği başka türlü optimize edebilir ve yanlış ölçütlerle sonuçlanabilir, çünkü her seferinde aynı önceden hesaplanmış şeyi döndürür.
Erik Aigner

11

Java Kullanımı

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

kullanmak import java.lang.Math.*;başında

C Kullanımı

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

kullanmak inclue math.hbaşında


1
Sadece FYI, sonsuzluğa neden the_integerolur 0, bu yüzden kontrol edin.
Erik Aigner

10

Henüz yorum bırakamıyorum, bu yüzden ayrı bir cevap olarak göndereceğim.

Logaritma tabanlı çözüm, çok büyük uzun tamsayılar için doğru basamak sayısını hesaplamaz, örneğin:

long n = 99999999999999999L;

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

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

Logaritma tabanlı çözüm, büyük tamsayılarda yanlış basamak sayısını hesaplar


j yerine 10 - (n - n / 10 * 10) (int) (Math.log10 (n + j)) deneyin.
Erick Stone

8

Bir tamsayının 10 tabanındaki basamak sayısı sadece 1 + kısaltma (log10 (sayı)) olduğundan , şunları yapabilirsiniz:

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

Düzenlenen benim son düzenleme açıklama kod örneği sabit değil çünkü.


Güzel. ama bence abs (sayı) ve "0" da özel bir durum mu?
DmitryK

Evet. İşareti hesaba katmanız gerekiyorsa, 1 + (int) Math.floor (Math.log10 (Math.abs (sayı))) + ((sayı <0)? 1: 0)
Dirk

5
Math.floorBu biraz gereksiz değil mi? Döküm intyine de yuvarlar.
CompuChip

5

Marian'ın çözümü , biri kopyalamak ve yapıştırmak istemesi durumunda, uzun tip sayılarına uyarlanmıştır (9,223,372,036,854,775,807'ye kadar). Programda bu sayıyı 10000'e kadar olan rakamlar için çok daha olası yazdım, bu yüzden onlar için belirli bir dal yaptım. Her neyse önemli bir fark yaratmayacak.

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

Bu sorunun başlığı "int / long içinde basamak sayısı elde etmenin yolu" olarak değiştirilmeli mi? (ve 'uzun' etiketini ekledi)
JAIL

4

Başka bir dizi yaklaşımı. Kısa ve tatlı - herhangi bir tam sayı için n.

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

Yalnızca pozitif tamsayı nve sıfır için çalışır . ("" + Math.abs(n)).length()Negatif tamsayı uzunluğunu elde etmek için kullanılabilir .
ThisClark

3

Deneyebilir miyim? ;)

Dirk'in çözümüne dayalı

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

3

Sade eski matematiğe ne dersiniz? 0'a ulaşıncaya kadar 10'a bölün.

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

1
Test ettin mi? Biliyorsunuz, insani bir bakış açısından mantıklı olsa bile, makinenin "düşünce tarzı" ile gerçekten aynı şey değil, değil mi? --- Bir şey önereyim: Long.MAX_VALUEKodunuzun en kötü karmaşıklık durumu olan iki milyon sayıdan oluşan bir dizi yapın System.nanoTime()ve diğer çözümün en kötü karmaşıklık durumlarına karşı bir saatli deneme yapmak için kullanın . ++ Aslında aralığına rasgeleleştirici kümesi tarafından doldurulan bir dizi denemek 0için Long.MAX_VALUEsadece Çok şoke ... sonuçlarını bulabilirsiniz ++ test "ortalama karmaşıklığı" için de.
XenoRo

@thelima Bu sıfır veya negatifler için düzgün çalışmıyor, ancak bu küçük bir hata. İlke benim için doğru görünüyor. Hangi "şok edici" sonuçtan bahsediyorsunuz?
Jay

Diyelim ki bilgisayarlar ... Şey ... Bölmeyi sevmiyorlar. Ve büyük sayıların büyük "kuyruklarının" işlenmesi gerektiği ve işlenen her sayıdaki her rakamın bir bölünme gerektirdiği durumlarda ... Şey ... İşler "gerçekten yavaş yavaş başlıyor" ... Anlamı ... --- Bu yüzden cevapların çoğunu, bölümler yerine 'if' kullanarak test ve her ondalık basamağa dayalı kodları kullanarak burada görüyorsunuz: Daha hızlı değilse, en azından hızının çoğunu korur en kötü durumları. --- Çok sayıda bölüm ve logaritma kullanma arasında bir test yapın ...
XenoRo

@TheLima neden bahsediyorsun? Bunun için int,bu döngü maksimum 11 kez yürütülür. İddialarınız için kanıtınız var mı?
Lorne Marquis

@EJP Bir donanım bakış açısından, bölme yinelemeli bir süreçtir. Bildiğim en hızlı bölme algoritması, yineleme başına 4 bit üreten radix4'tür; 32 bitlik bir bölünmenin en az 8 yinelemeye ihtiyacı vardır. Örneğin çarpmalar paralel olarak yapılabilir ve daha basit çarpmalara bölünebilir; ya bit seviyesine kadar (sadece 5 işlem gerektirir) ya da kısmi arıza artı sonunda bir arama tablosu ile (Klasik boyut VS hız değişimi). Bu sadece "kaç tekrar" ile ilgili değil; bölümlerle ilgili sorun, "her yinelemenin bir donanım düzeyinde ne anlama geldiğini / ne yaptığını" yatıyor
XenoRo

2

Marian's Solution, şimdi Ternary ile:

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

Çünkü yapabiliriz.


2
Okumak biraz zor. Belki biraz boşluk ve / veya satırsonu ekleyin.
michaelb958 - GoFundMonica

Ama lanet olsun taşınabilir!
Trevor Rudolph

1

Meraklı, ben kıyaslamaya çalıştım ...

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


    }
}

sonuçlar:

Çalışma süresi 1: 6765
s: 400000000
Çalışma süresi 2: 6000
s: 400000000

Şimdi kıyaslamamın gerçekten bir şey ifade edip etmediğini merak ediyorum ama kıyaslamanın kendisinin birden fazla çalışması üzerinde tutarlı sonuçlar (bir ms içinde varyasyonlar) alıyorum ... :) Bunu denemek ve optimize etmek işe yaramaz gibi görünüyor ...


edit: ptomli'nin yorumunu takiben, yukarıdaki kodda 'sayı'yı' i 'ile değiştirdim ve tezgahın 5 çalışmasında aşağıdaki sonuçları aldım:

Çalışma süresi 1: 11500
s: 788888890
Çalışma süresi 2: 8547
s: 788888890

Çalışma süresi 1: 11485
s: 788888890
Çalışma süresi 2: 8547
s: 788888890

Çalışma süresi 1: 11469
s: 788888890
Çalışma süresi 2: 8547
s: 788888890

Çalışma süresi 1: 11500
s: 788888890
Çalışma süresi 2: 8547
s: 788888890

Çalışma süresi 1: 11484
s: 788888890
Çalışma süresi 2: 8547
s: 788888890

1
Sadece eğlenmek için, 0'dan bir trilyona kadar sayı değerlerinin dağılımı arasındaki fark nedir? :)
ptomli

0

Bu özyinelemeli yöntem ne olacak?

    private static int length = 0;

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

0

basit çözüm:

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

Gerçekten basit bir çözüm:

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

Boş bir gövdeli basit bir döngü için bir satır çağırmazdım. Ne de aynı şeyi geri alıp almadığınızı görmek için 10'luk bir güç (sadece bir karşılaştırma kullanamaz mısınız?).
Teepeemm

0

Ya da bunun yerine uzunluk, sayının istenen sayıdan daha büyük veya daha küçük olup olmadığını kontrol edebilirsiniz.

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

}


Anlamıyorum. Görünüşe göre farklı bir soruya cevap veriyorsunuz.
Teepeemm

0

Henüz çarpma tabanlı bir çözüm görmedim. Logaritma, bölme ve dizi tabanlı çözümler milyonlarca test vakasına karşı oldukça kullanışsız olacaktır, bu yüzden aşağıdakilerden biri 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;
}

Taban 10'da, bu işe yarar çünkü n aslında 9, 99, 999 ile karşılaştırılır ... çünkü min 9, 90, 900 ... ve n 9, 90, 900 ile çıkarılır ...

Ne yazık ki, bu longsadece inttaşma nedeniyle her örneğini değiştirerek taşınabilir değildir . Öte yandan, sadece bu yüzden olur edecektir üsleri 2 ve 10 için çalışmak (ama kötü diğer üslerin çoğu için başarısız). Taşma noktaları için bir arama tablosuna ihtiyacınız olacak (veya bir bölüm testi ... 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

Tasarım ile (probleme dayalı). Bu, bölün ve fethetmenin bir alternatifidir. İlk önce bir numaralandırma tanımlayacağız (sadece işaretsiz bir int için olduğunu düşünüyoruz).

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

Şimdi sıralamanın değerlerinden geçen bir sınıf tanımlayacağız ve uygun uzunluğu karşılaştıracağız.

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

Bu çözümün çalışma süresi böl ve fethet yaklaşımıyla aynıdır.


Bölme ve fethetme ortadan başlayacak ve kalan arama alanını ikiye ayıracaktır. Bu doğrusal bir çalışma süresine sahiptir. Ancak sadece 9 karşılaştırma için önemli olmayacak. Ama bu karışıklık olmaz mı num>=Nine.getValue()?
Teepeemm

0

Kişi bunu çoğunlukla "sunmak" istediği için yapmak ister, bu da eninde sonunda açıkça veya dolaylı olarak "toString-ed" (veya başka bir şekilde dönüştürülmüş) olması gerektiği anlamına gelir; sunulmadan önce (örneğin basılı).

Eğer durum buysa, gerekli "toString" i açık hale getirmeye çalışın ve bitleri sayın.


0

Bunu yinelemeli bir döngü kullanarak başarabiliriz

    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

Bu işlevi Integer.javakaynak koduna baktıktan sonra yazdım .

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

String kütüphaneleri kullanan, hatta Integer sınıfını kullanan insanlar görüyorum. Bunda yanlış bir şey yok ama basamak sayısını elde etme algoritması o kadar karmaşık değil. Bu örnekte uzun bir süre kullanıyorum ama bir int ile gayet iyi çalışıyor.

 private static int getLength(long num) {

    int count = 1;

    while (num >= 10) {
        num = num / 10;
        count++;
    }

    return count;
}

0

hiçbir String API, hiçbir yarar, hiçbir tür dönüştürme, sadece saf java yineleme ->

public static int getNumberOfDigits(int input) {
    int numOfDigits = 1;
    int base = 1;
    while (input >= base * 10) {
        base = base * 10;
        numOfDigits++;
    }
    return numOfDigits;
 }

İsterseniz daha büyük değerler için uzun sürebilirsin.


-1
    int num = 02300;
    int count = 0;
    while(num>0){
         if(num == 0) break;
         num=num/10;
         count++;
    }
    System.out.println(count);

"10'a böl" çözümü ilk olarak iki yıl önce Sinista tarafından yayınlandı.
Teepeemm

-1

Kolay özyinelemeli yol

int    get_int_lenght(current_lenght, value)
{
 if (value / 10 < 10)
    return (current_lenght + 1);
return (get_int_lenght(current_lenght + 1, value))
}

test edilmedi


3
Muhtemelen test etmelisiniz (ve geçerli Java olduğundan ve düzgün biçimlendirildiğinden emin olmalısınız). Ancak 3 yıl önce Jedi Dula tarafından tekrarlanan bir “10'a böl” yaklaşımı yayınlandı.
Teepeemm

-2

Rakamları art arda on kullanarak bölme yapabilirsiniz:

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

"10'a böl" yaklaşımı ilk olarak 3 yıl önce Sinista tarafından yayınlandı. Düşüşünüz olduğunu düşünebilmemin tek nedeni bu.
Teepeemm

-2

Numarayı girin ve bir oluşturun Arraylist, while döngüsü tüm basamakları Arraylist. Ardından, girdiğiniz tamsayı değerinin uzunluğu olan dizinin boyutunu çıkarabiliriz.

ArrayList<Integer> a=new ArrayList<>();

while(number > 0) 
{ 
    remainder = num % 10; 
    a.add(remainder);
    number = number / 10; 
} 

int m=a.size();

1
ArrayList'e veya rakamlara ihtiyacınız yok.
Lorne Marquis

-2

İşte herhangi bir sayı için çalışan gerçekten basit bir yöntem:

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

Sayı sayacı değişkeni ile çalışma şekli 10 = 1 basamaklı boşluktur. Örneğin .1 = 1 onda biri => 1 basamaklı boşluk. Bu nedenle int number = 103342;, 6 elde edersiniz, çünkü bu geri .000001 boşluklarına eşdeğerdir. Ayrıca, herkes için daha iyi bir değişken adı var numberCountermı? Daha iyi bir şey düşünemiyorum.

Edit: Sadece daha iyi bir açıklama düşündüm. Temelde bu döngü yaparken bunu yapmak, böylece numaranızı bire kadar olana kadar 10'a bölmenizdir. Temel olarak, bir şeyi 10'a böldüğünüzde, onu bir sayı boşluğuna geri taşırsınız, bu nedenle sayınızdaki basamak miktarı için <1'e ulaşıncaya kadar onu 10'a bölersiniz.

Ondalık bir sayıdaki sayı miktarını sayabilen başka bir sürüm:

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

-3

Dönüştürmeyi deneyin int a dize ve sonra uzunluğunu almak dize . Bu int uzunluğunu almak gerekir .

public static int intLength(int num){
    String n = Integer.toString(num);
    int newNum = n.length();
    return newNum;
}

Bu tamamen orijinal koda eşdeğerdir. Ve numbernegatif olduğunda özleyeceğim .
Teepeemm
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.