Anzahl der Ziffern in einer Ganzzahl in Java

Anzahl der Stellen in einer Ganzzahl in Java

1. Einführung

In diesem kurzen Tutorial werden wirdifferent ways of getting the number of digits in an Integer in Java untersuchen.

Wir werden auch diese verschiedenen Methoden analysieren und herausfinden, welcher Algorithmus am besten zu unserer Situation passt.

2. Anzahl der Stellen inInteger

Bei den hier diskutierten Methoden berücksichtigen wir nur positive ganze Zahlen. Wenn wir eine negative Eingabe erwarten, können wir zuerstMath.abs(number) verwenden, bevor wir eine dieser Methoden anwenden.

2.1. String-basierte Lösung

Der einfachste Weg, die Anzahl der Stellen inInteger zu ermitteln, besteht darin, sie inString zu konvertieren und die Methodelength() aufzurufen. Dies gibt die Länge derString-Darstellung unserer Zahl zurück:

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

But, this may be a sub-optimal approach, as this statement involves memory allocation for a String, for each evaluation. Die JVM muss zuerst unsere Nummer analysieren und ihre Ziffern in separateString kopieren und auch eine Reihe verschiedener Vorgänge ausführen (z. B. temporäre Kopien aufbewahren, Unicode-Konvertierungen durchführen usw.).

Wenn wir nur ein paar Zahlen auswerten müssen, können wir uns eindeutig für diese Lösung entscheiden, da der Unterschied zwischen dieser und jeder anderen Vorgehensweise selbst bei großen Zahlen vernachlässigbar ist.

2.2. Logarithmischer Ansatz

Wenn wir für die in Dezimalform dargestellten Zahlen das Protokoll in Basis 10 nehmen und aufrunden, erhalten wir die Anzahl der Ziffern in dieser Zahl:

int length = (int) (Math.log10(number) + 1);

Beachten Sie, dasslog100 einer beliebigen Zahl nicht definiert ist. Wenn wir also eine Eingabe mit dem Wert0 erwarten, können wir dies ebenfalls überprüfen.

The logarithmic approach is significantly faster than String based approach as it doesn’t have to go through the process of any data conversion. Es handelt sich lediglich um eine einfache Berechnung ohne zusätzliche Objektinitialisierung oder Schleifen.

2.3. Wiederholte Multiplikation

Bei dieser Methode nehmen wir eine temporäre Variable (initialisiert auf 1) und multiplizieren sie kontinuierlich mit 10, bis sie größer als unsere Zahl wird. Während dieses Vorgangs verwenden wir auch einelength-Variable, die die Länge der Nummer verfolgt:

int length = 0;
long temp = 1;
while (temp <= number) {
    length++;
    temp *= 10;
}
return length;

In diesem Code entspricht die Zeiletemp *= 10 dem Schreiben vontemp = (temp << 3) + (temp << 1). Da die Multiplikation bei einigen Prozessoren im Vergleich zu Schichtoperatoren in der Regel kostspieliger ist, ist letztere möglicherweise effizienter.

2.4. Teilen mit Zweierpotenzen

Wenn wir den Bereich unserer Anzahl kennen, können wir eine Variation verwenden, die unsere Vergleiche weiter reduziert. Diese Methode dividiert die Zahl durch Zweierpotenzen (z. 1, 2, 4, 8 usw.):

Diese Methode dividiert die Zahl durch Zweierpotenzen (z. 1, 2, 4, 8 usw.):

int length = 1;
if (number >= 100000000) {
    length += 8;
    number /= 100000000;
}
if (number >= 10000) {
    length += 4;
    number /= 10000;
}
if (number >= 100) {
    length += 2;
    number /= 100;
}
if (number >= 10) {
    length += 1;
}
return length;

Es macht sich die Tatsache zunutze, dass durch Addition von Zweierpotenzen jede Zahl dargestellt werden kann. Beispielsweise kann 15 als 8 + 4 + 2 + 1 dargestellt werden, was alles Zweierpotenzen sind.

Bei einer 15-stelligen Zahl würden wir in unserem vorherigen Ansatz 15 Vergleiche durchführen, die wir bei dieser Methode auf nur 4 reduziert haben.

2.5. Teilen und erobern

Dies ist vielleichtthe bulkiest approach im Vergleich zu allen anderen hier beschriebenen, aber natürlichthis one is the fastest, da wir keine Art von Konvertierung, Multiplikation, Addition oder Objektinitialisierung durchführen.

Wir erhalten unsere Antwort in nur drei oder vier einfachenif-Anweisungen:

if (number < 100000) {
    if (number < 100) {
        if (number < 10) {
            return 1;
        } else {
            return 2;
        }
    } else {
        if (number < 1000) {
            return 3;
        } else {
            if (number < 10000) {
                return 4;
            } else {
                return 5;
            }
        }
    }
} else {
    if (number < 10000000) {
        if (number < 1000000) {
            return 6;
        } else {
            return 7;
        }
    } else {
        if (number < 100000000) {
            return 8;
        } else {
            if (number < 1000000000) {
                return 9;
            } else {
                return 10;
            }
        }
    }
}

Ähnlich wie beim vorherigen Ansatz können wir diese Methode nur anwenden, wenn wir den Bereich unserer Zahl kennen.

3. Benchmarking

Nachdem wir die möglichen Lösungen gut verstanden haben, führen wir nun ein einfaches Benchmarking aller unserer Methoden mitJava Microbenchmark Harness (JMH)durch.

Die folgende Tabelle zeigt die durchschnittliche Verarbeitungszeit für jede Operation (in Nanosekunden):

Benchmark                            Mode  Cnt   Score   Error  Units
Benchmarking.stringBasedSolution     avgt  200  32.736 ± 0.589  ns/op
Benchmarking.logarithmicApproach     avgt  200  26.123 ± 0.064  ns/op
Benchmarking.repeatedMultiplication  avgt  200   7.494 ± 0.207  ns/op
Benchmarking.dividingWithPowersOf2   avgt  200   1.264 ± 0.030  ns/op
Benchmarking.divideAndConquer        avgt  200   0.956 ± 0.011  ns/op

Die aufString basierende Lösung, die am einfachsten ist, ist auch die teuerste Operation - da dies die einzige ist, die Datenkonvertierung und Initialisierung neuer Objekte erfordert.

Der logarithmische Ansatz ist im Vergleich zur vorherigen Lösung wesentlich effizienter, da keine Datenkonvertierung erforderlich ist. Als einzeilige Lösung kann sie eine gute Alternative zumString--basierten Ansatz sein.

Die wiederholte Multiplikation beinhaltet eine einfache Multiplikation proportional zur Länge der Zahl. Wenn eine Zahl beispielsweise fünfzehn Stellen lang ist, umfasst diese Methode fünfzehn Multiplikationen.

Die nächste Methode nutzt jedoch die Tatsache, dass jede Zahl durch Zweierpotenzen dargestellt werden kann (der Ansatz ähnelt BCD) und reduziert sie auf 4 Divisionsoperationen, sodass sie noch effizienter als die erstere ist.

Schließlich, wie wir schließen können,the most efficient algorithm is the verbose Divide and Conquer implementation - was die Antwort in nur drei oder vier einfachen if-Anweisungen liefert. Wir können es verwenden, wenn wir einen großen Datensatz von Zahlen haben, die wir analysieren müssen.

4. Fazit

In diesem kurzen Artikel haben wir einige Möglichkeiten beschrieben, wie die Anzahl der Stellen inInteger ermittelt werden kann, und die Effizienz jedes Ansatzes verglichen.

Und wie immer finden Sie den vollständigen Codeover on GitHub.