[英]Way to get number of digits in an int?
有沒有比這種方法更簡潔的方法來獲取 int 中的位數?
int numDigits = String.valueOf(1000).length();
您的基於字符串的解決方案非常好,沒有什么“不整潔”的地方。 你必須意識到,在數學上,數字沒有長度,也沒有數字。 長度和數字都是特定基數(即字符串)中數字的物理表示的屬性。
基於對數的解決方案(某些)與基於字符串的解決方案在內部執行的操作相同,並且可能執行速度更快(微不足道),因為它只產生長度並忽略數字。 但我實際上不會認為它的意圖更清晰 - 這是最重要的因素。
對數是你的朋友:
int n = 1000;
int length = (int)(Math.log10(n)+1);
注意:僅對 n > 0 有效。
最快的方法:分而治之。
假設您的范圍是 0 到 MAX_INT,那么您有 1 到 10 位數字。 您可以使用分治法來接近此間隔,每個輸入最多進行 4 次比較。 首先,通過一次比較將 [1..10] 划分為 [1..5] 和 [6..10],然后使用一次比較將每個長度為 5 的區間划分為一個長度為 3 的區間和一個長度為 2 的區間。 長度為2的區間需要多比較一次(共3次比較),長度為3的區間可分為長度為1的區間(解)和長度為2的區間。 因此,您需要進行 3 或 4 次比較。
沒有除法,沒有浮點運算,沒有昂貴的對數,只有整數比較。
代碼(長但快):
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;
}
}
}
基准測試(在 JVM 預熱之后) - 請參閱下面的代碼以了解基准測試是如何運行的:
完整代碼:
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;
}
再次,基准測試:
在我編寫了基准測試之后,我從 Java 6 中偷偷摸摸到了 Integer.toString,我發現它使用了:
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;
}
我將它與我的分而治之解決方案進行了基准測試:
我的速度大約是 Java 6 解決方案的 4 倍。
關於您的基准測試的兩條評論:Java 是一個復雜的環境,需要即時編譯和垃圾收集等等,所以為了公平比較,每當我運行基准測試時,我總是:(a)附上兩個測試在按順序運行它們 5 或 10 次的循環中。 很多時候,第二次循環的運行時間與第一次完全不同。 並且 (b) 在每次“方法”之后,我都會執行 System.gc() 來嘗試觸發垃圾收集。 否則,第一種方法可能會生成一堆對象,但不足以強制進行垃圾回收,然后第二種方法會創建一些對象,堆耗盡,然后垃圾回收運行。 然后第二種方法是“收費”的,用於拾取第一種方法留下的垃圾。 很不公平!
也就是說,以上都沒有在這個例子中產生顯着差異。
無論有沒有這些修改,我得到的結果都與你不同。 當我運行它時,是的,toString 方法的運行時間為 6400 到 6600 毫秒,而 log 方法的運行時間為 20,000 到 20,400 毫秒。 日志方法不是稍微快一點,而是對我來說慢了 3 倍。
請注意,這兩種方法涉及非常不同的成本,因此這並不完全令人震驚:toString 方法將創建許多必須清理的臨時對象,而 log 方法需要更密集的計算。 所以也許區別在於,在內存較少的機器上,toString 需要更多的垃圾收集輪次,而在處理器較慢的機器上,額外的日志計算會更痛苦。
我還嘗試了第三種方法。 我寫了這個小函數:
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;
}
運行時間為 1600 到 1900 毫秒——不到 toString 方法的 1/3,以及我機器上的 log 方法的 1/10。
如果您的數字范圍很廣,則可以通過開始除以 1,000 或 1,000,000 以減少循環次數來進一步加快速度。 我沒玩過那個。
還不能發表評論,所以我將作為單獨的答案發布。
基於對數的解決方案不會為非常大的長整數計算正確的位數,例如:
long n = 99999999999999999L;
// correct answer: 17
int numberOfDigits = String.valueOf(n).length();
// incorrect answer: 18
int wrongNumberOfDigits = (int) (Math.log10(n) + 1);
使用 Java
int nDigits = Math.floor(Math.log10(Math.abs(the_integer))) + 1;
使用import java.lang.Math.*;
在一開始的時候
使用 C
int nDigits = floor(log10(abs(the_integer))) + 1;
在開頭使用inclue math.h
由於整數的以 10 為底的位數僅為1 + truncate(log10(number)) ,因此您可以執行以下操作:
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);
}
}
編輯是因為我上次編輯修復了代碼示例,但沒有修復描述。
另一種字符串方法。 簡短而甜蜜 - 對於任何整數n
。
int length = ("" + n).length();
Marian 的解決方案適用於長類型數字(最多 9,223,372,036,854,775,807),以防有人想要復制和粘貼它。 在我編寫的程序中,高達 10000 的數字更有可能,所以我為它們做了一個特定的分支。 無論如何,它不會產生重大影響。
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;
}
}
}
}
}
普通的舊數學怎么樣? 除以 10 直到達到 0。
public static int getSize(long number) {
int count = 0;
while (number > 0) {
count += 1;
number = (number / 10);
}
return count;
}
我可以試試嗎? ;)
基於 Dirk 的解決方案
final int digits = number==0?1:(1 + (int)Math.floor(Math.log10(Math.abs(number))));
瑪麗安的解決方案,現在使用三元:
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)));
}
因為我們可以。
我看到人們使用 String 庫,甚至使用 Integer 類。 這沒有錯,但獲取位數的算法並不復雜。 我在這個例子中使用了 long 但它與 int 一樣好。
private static int getLength(long num) {
int count = 1;
while (num >= 10) {
num = num / 10;
count++;
}
return count;
}
沒有 String API,沒有 utils,沒有類型轉換,只是純 java 迭代 ->
public static int getNumberOfDigits(int input) {
int numOfDigits = 1;
int base = 1;
while (input >= base * 10) {
base = base * 10;
numOfDigits++;
}
return numOfDigits;
}
如果你願意,你可以長期追求更大的價值。
好奇,我試圖對其進行基准測試...
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);
}
}
結果是:
Run time 1: 6765 s: 400000000 Run time 2: 6000 s: 400000000
現在我想知道我的基准測試是否真的意味着什么,但我確實在多次運行基准測試本身時得到了一致的結果(在一毫秒內的變化)...... :) 看起來嘗試優化它是沒有用的......
編輯:根據 ptomli 的評論,我在上面的代碼中將 'number' 替換為 'i' 並在 5 次運行中得到以下結果:
Run time 1: 11500 s: 788888890 Run time 2: 8547 s: 788888890 Run time 1: 11485 s: 788888890 Run time 2: 8547 s: 788888890 Run time 1: 11469 s: 788888890 Run time 2: 8547 s: 788888890 Run time 1: 11500 s: 788888890 Run time 2: 8547 s: 788888890 Run time 1: 11484 s: 788888890 Run time 2: 8547 s: 788888890
有設計(基於問題)。 這是分而治之的替代方案。 我們將首先定義一個枚舉(考慮到它僅用於無符號整數)。
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;
}
}
現在我們將定義一個遍歷枚舉值並比較並返回適當長度的類。
public class IntegerLenght {
public static byte calculateIntLenght(int num) {
for(IntegerLength v : IntegerLength.values()) {
if(num < v.getValue()){
return v.getLenght();
}
}
return 0;
}
}
該解決方案的運行時間與分治法相同。
這個遞歸方法怎么樣?
private static int length = 0;
public static int length(int n) {
length++;
if((n / 10) < 10) {
length++;
} else {
length(n / 10);
}
return length;
}
簡單的解決方案:
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);
}
一個非常簡單的解決方案:
public int numLength(int n) {
for (int length = 1; n % Math.pow(10, length) != n; length++) {}
return length;
}
或者,您可以檢查數字是大於還是小於所需數字的長度。
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);
}
}
}
我還沒有看到基於乘法的解決方案。 對於數百萬個測試用例,對數、除法和基於字符串的解決方案將變得相當笨拙,所以這里有一個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;
}
在以 10 為底的情況下,這是有效的,因為 n 本質上是與 9、99、999... 進行比較,因為 min 是 9、90、900...,而 n 被 9、90、900...減去
不幸的是,由於溢出,僅通過替換int
的每個實例就不能移植到long
。 另一方面,它恰好適用於 2 號和 10 號基地(但對於大多數其他基地來說嚴重失敗)。 您需要一個查找表來查找溢出點(或除法測試...... 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;
}
我們可以使用遞歸循環來實現這一點
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+"]");
}
一個人想要這樣做主要是因為他/她想“呈現”它,這主要意味着它最終需要顯式或隱式地被“toString-ed”(或以另一種方式轉換); 在展示之前(例如打印)。
如果是這種情況,那么只需嘗試明確必要的“toString”並計算位。
我在查看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;
}
}
}
計算int
變量中位數的有效方法之一是定義具有所需數量的條件語句的方法digitsCounter 。
方法很簡單,我們將檢查n
位數字所在的每個范圍:
0 : 9是個Single
10 : 99是Double
100 : 999是Triple
等等...
static int digitsCounter(int N)
{ // N = Math.abs(N); // if `N` is -ve
if (0 <= N && N <= 9) return 1;
if (10 <= N && N <= 99) return 2;
if (100 <= N && N <= 999) return 3;
if (1000 <= N && N <= 9999) return 4;
if (10000 <= N && N <= 99999) return 5;
if (100000 <= N && N <= 999999) return 6;
if (1000000 <= N && N <= 9999999) return 7;
if (10000000 <= N && N <= 99999999) return 8;
if (100000000 <= N && N <= 999999999) return 9;
return 10;
}
一種更簡潔的方法是取消對下限的檢查,因為如果我們以順序方式進行,則不需要它。
static int digitsCounter(int N)
{
N = N < 0 ? -N : N;
if (N <= 9) return 1;
if (N <= 99) return 2;
if (N <= 999) return 3;
if (N <= 9999) return 4;
if (N <= 99999) return 5;
if (N <= 999999) return 6;
if (N <= 9999999) return 7;
if (N <= 99999999) return 8;
if (N <= 999999999) return 9;
return 10; // Max possible digits in an 'int'
}
理想情況下,只要整數不為零,整數除以 10 多次將返回位數。 這樣一個簡單的方法可以創建如下。
public static int getNumberOfDigits(int number) {
int numberOfDigits = 0;
while(number != 0) {
number /= 10;
numberOfDigits++;
}
return numberOfDigits;
}
這取決於您所說的“整潔”是什么意思。 我認為下面的代碼相當簡潔,而且運行速度很快。
它基於瑪麗安的回答,擴展為使用所有long
值並使用? :
? :
運營商。
private static int numberOfDigits(final long n)
{
return n == Long.MIN_VALUE ? 19 : n < 0l ? numberOfDigits(-n) :
n < 100000000l ? // 1-8
n < 10000l ? // 1-4
n < 100l ? // 1-2
n < 10l ? 1 : 2 : // 1-2
n < 1000l ? 3 : 4 : // 3-4
n < 1000000l ? // 5-8
n < 100000l ? 5 : 6 : // 5-6
n < 10000000l ? 7 : 8 : // 7-8
n < 10000000000000000l ? // 9-16
n < 1000000000000l ? // 9-12
n < 10000000000l ? // 9-10
n < 1000000000l ? 9 : 10 : // 9-10
n < 100000000000l ? 11 : 12 : // 11-12
n < 100000000000000l ? // 13-16
n < 10000000000000l ? 13 : 14 : // 13-14
n < 1000000000000000l ? 15 : 16 : // 15-16
n < 100000000000000000l ? 17 : // 17-19
n < 1000000000000000000l ? 18 :
19;
}
是否有比此方法更整潔的方法來獲取int中的位數?
int numDigits = String.valueOf(1000).length();
是否有比此方法更整潔的方法來獲取int中的位數?
int numDigits = String.valueOf(1000).length();
這是我制作的一個非常簡單的方法,適用於任何數字:
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;
}
它的工作方式是使用數字計數器變量是 10 = 1 位空間。 例如 .1 = 1 個十分位 => 1 位空格。 因此,如果您有int number = 103342;
你會得到 6,因為這相當於 0.000001 個空格。 另外,有沒有人有一個更好的numberCounter
變量名? 我想不出更好的了。
編輯:只是想到了一個更好的解釋。 本質上,這個while循環所做的就是讓你將你的數字除以10,直到它小於1。 本質上,當您將某物除以 10 時,您會將其移回一個數字空間,因此您只需將其除以 10,直到您的數字中的位數達到 <1。
這是另一個可以計算小數位數的版本:
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;
}
是否有比此方法更整潔的方法來獲取int中的位數?
int numDigits = String.valueOf(1000).length();
是否有比此方法更整潔的方法來獲取int中的位數?
int numDigits = String.valueOf(1000).length();
是否有比此方法更整潔的方法來獲取int中的位數?
int numDigits = String.valueOf(1000).length();
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.