วิธีรับจำนวนหลักใน int?


386

มีวิธี neater สำหรับการรับความยาวของ int กว่าวิธีนี้หรือไม่?

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

7
กำหนดความยาวของโปรด int
ทอม

24
ฉันคิดว่าเขาต้องการนับตัวเลขในจำนวน
Alberto Zaccagni

3
คำตอบที่ผู้คนให้คุณถูกต้อง ... พวกเขาให้ความยาวของคุณโดยไม่แปลงเป็นสตริง ... แต่ทำไมคุณไม่ต้องการแปลงให้เป็นสตริง? มันเป็นเรื่องของความเร็วหรือไม่? ถ้าเป็นเช่นนั้นฉันไม่มั่นใจว่าวิธีการเหล่านี้จะเร็วกว่านี้ ... คุณอาจต้องการทดสอบบางอย่าง (หรือตัดสินใจว่าจะเป็นเรื่องสำคัญหรือไม่)
38846

3
@ptomli เลขฐานสิบหกหลักยังคงเป็นหลักเพียงในระบบฐานที่แตกต่างกัน
Mark Pim

2
@Pomli แน่นอน แต่ทั้งคู่ในฟังก์ชัน Integer.toString และในการสนทนาทั่วไปทศนิยมจะเป็นค่าเริ่มต้น เมื่อธนาคารบอกฉันว่า "เขียนจำนวนเช็คของคุณในช่องนี้" ฉันไม่ถามพวกเขาว่าฉันควรเขียนเป็นทศนิยมฐานสิบหกหรือฐานแปด เราถือว่าทศนิยมเว้นแต่จะระบุหรือเรียกตามบริบท
Jay

คำตอบ:


349

โซลูชันที่อิงกับสตริงของคุณนั้นใช้ได้อย่างสมบูรณ์แบบไม่มีสิ่งใดที่ "ไม่เรียบร้อย" เกี่ยวกับมัน คุณต้องตระหนักว่าในทางคณิตศาสตร์ตัวเลขไม่มีความยาวหรือไม่มีตัวเลข ความยาวและตัวเลขเป็นคุณสมบัติทั้งสองของการแสดงทางกายภาพของตัวเลขในฐานที่เฉพาะเจาะจงเช่นสตริง

วิธีแก้ปัญหาแบบลอการิทึม (บางส่วน) เป็นแบบเดียวกับที่ใช้แบบสตริงภายในและอาจทำได้เร็วกว่าเล็กน้อย (ไม่สำคัญ) เพราะมันผลิตความยาวและไม่สนใจตัวเลข แต่ฉันจะไม่คิดว่ามันชัดเจนในเจตนา - และนั่นคือปัจจัยที่สำคัญที่สุด


54
+1 สำหรับพิจารณาถึงเจตนาของรหัสเมื่อเลือกวิธีการแก้ปัญหา
pupeno

5
Datapoint: บนเครื่องของฉันเมธอดการบันทึกดูเหมือนว่าจะทำงานเร็วกว่าสองเท่าของเมธอดความยาวสตริง ฉันจะไม่เรียกว่าไม่มีนัยสำคัญหากวิธีการที่ได้รับเรียกว่ามากหรือในส่วนเวลาที่สำคัญของรหัส
CPerkins

1
ดูการทดสอบหน่วยมาตรฐานของฉันด้านล่าง (ซึ่งอาจมีข้อบกพร่องเช่นกันฉันไม่มีผู้เชี่ยวชาญด้านการวัดประสิทธิภาพ) จากการวิ่งจำนวนมาก (100,000 ครั้ง) ความเร็วเป็น 11s ถึง 8s บนเครื่องของฉันแทบจะไม่เร็วเป็นสองเท่า
ฌอง

5
@CPerkins การเพิ่มประสิทธิภาพก่อนวัยอันควร คุณรู้ว่าเกม
Michael Borgwardt

11
บางส่วน (ค่อนข้างช้า): อาจทำงานไม่ถูกต้องสำหรับค่าลบขึ้นอยู่กับว่าคุณคาดหวังว่า "-" จะเป็นตัวเลขหรือไม่ การเพิ่มMath.abs()จะแก้ไขปัญหานี้ได้
YingYang

265

ลอการิทึมเป็นเพื่อนของคุณ:

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

NB: ใช้ได้กับ n> 0 เท่านั้น


2
และนี่คือเร็วกว่าหรือดีกว่าการใช้ตัวแปรของฉัน
fnst

+1 คุณทุบตีฉันทีละวินาทีและคำตอบของคุณถูกต้องที่ซึ่งฉันปิดไปเล็กน้อย หมายเหตุแม้ว่าว่าคอมไพเลอร์จะบ่นเนื่องจากมีการโยนไปint
เดิร์ค

2
@Tom ทำไมคุณถึงคิดว่ามันแพง หนึ่งอาจคิดว่าตัวประมวลผลทางคณิตศาสตร์จะประมวลผลดังนั้นจึงอาจใกล้เคียงกับความเร็วของการเพิ่ม แม้ว่าจาวาไม่ได้ใช้ตัวประมวลผลร่วมตอนนี้มันเป็นข้อสันนิษฐานที่ดีว่ามันอาจ ... (เราเพียงแค่ไม่สนใจความหมายที่ไม่ได้รับการศึกษาของคุณมากขึ้นว่าจาวาช้าเพราะคุณอาจไม่สนใจหลักฐาน - หรือ ถ้าคุณเป็นคุณจะไปshootout.alioth.debian.orgและหาตัวเอง)
บิล K

8
ทำงาน ... ยกเว้นค่าที่คุณกำลังตรวจสอบ = 0 ซึ่งจะให้ผลลัพธ์ที่แปลก (-2147483647) Math.log10 API: "ถ้าอาร์กิวเมนต์มีค่าเป็นศูนย์หรือลบศูนย์ผลลัพธ์จะเป็นค่าลบอนันต์"
mujimu

2
+1 การนำเสนอวิธีการที่ไม่เกี่ยวข้องกับการจัดสรรหน่วยความจำวัตถุซึ่งเป็นสิ่งจำเป็นสำหรับการเพิ่มการใช้ซ้ำเพื่อหลีกเลี่ยงการรวบรวม GC
Michael Wojcik

159

วิธีที่เร็วที่สุด: แบ่งและพิชิต

สมมติว่าช่วงของคุณคือ 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) - ดูรหัสด้านล่างเพื่อดูว่าเกณฑ์มาตรฐานทำงานอย่างไร:

  1. วิธีการพื้นฐาน (ด้วย String.length): 2145ms
  2. เมธอด log10: 711ms = 3.02 เท่าเร็วเท่ากับ baseline
  3. หารซ้ำ: 2797ms = 0.77 เท่าเร็วที่สุด
  4. หารและพิชิต: 74ms = 28.99
    เท่าเร็วที่สุด

รหัสเต็ม:

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

มาตรฐานอีกครั้ง:

  1. วิธีการพื้นฐาน (ด้วย String.length): 2145ms
  2. เมธอด log10: 711ms = 3.02 เท่าเร็วเท่ากับ baseline
  3. หารซ้ำ: 2797ms = 0.77 เท่าเร็วที่สุด
  4. หารและพิชิต: 74ms = 28.99
    เท่าเร็วที่สุด

แก้ไข: หลังจากที่ฉันเขียนเกณฑ์มาตรฐานฉันได้แอบเข้าไปใน Integer.toString จาก Java 6 และฉันพบว่ามันใช้:

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

ฉันเปรียบเทียบกับโซลูชันแบ่งและพิชิตของฉัน:

  1. หารและพิชิต: 104ms
  2. โซลูชัน Java 6 - ทำซ้ำและเปรียบเทียบ: 406ms

Mine มีความเร็วประมาณ 4x เท่าโซลูชัน Java 6


7
มันดูดีมาก คุณสามารถเขียนมันให้เล็กลงได้อีกหรือไม่โดยใช้ตัวดำเนินการ: ได้รับการยอมรับมากขึ้น
André Pareis

88
พูดคุยเกี่ยวกับการปรับให้เหมาะสมก่อนวัยอันควร: D
Gordon Gustafson

2
ฉันชอบมัน! แล้วสวิตช์บล็อกแทนที่จะเป็น if-elseses ซ้อนกันล่ะ
Kebman

2
ฉันไม่ได้ตระหนักถึงสิ่งเหล่านี้หากงบอื่นจะเร็วกว่าการแปลง int เป็น String จากนั้นเรียกความยาว +1
Ogen

15
ใช้ผู้ประกอบการที่สามนำมาลงที่ 101 ตัวอักษร: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
โจนาธาน Gawrych

13

สองความคิดเห็นเกี่ยวกับเกณฑ์มาตรฐานของคุณ: Java เป็นสภาพแวดล้อมที่ซับซ้อนสิ่งที่มีเพียงการรวบรวมเวลาและการเก็บขยะและอื่น ๆ เพื่อให้ได้รับการเปรียบเทียบที่ยุติธรรมเมื่อใดก็ตามที่ฉันใช้มาตรฐานฉันมักจะ: (a) ล้อมรอบการทดสอบทั้งสอง ในลูปที่รันพวกมันตามลำดับ 5 หรือ 10 ครั้ง บ่อยครั้งที่รันไทม์ของการผ่านครั้งที่สองผ่านลูปนั้นค่อนข้างแตกต่างจากครั้งแรก และ (b) หลังจาก "เข้าใกล้" ทุกครั้งฉันจะทำ System.gc () เพื่อพยายามเรียกชุดเก็บขยะ มิฉะนั้นวิธีแรกอาจสร้างกลุ่มของวัตถุ แต่ไม่เพียงพอที่จะบังคับให้รวบรวมขยะจากนั้นวิธีที่สองจะสร้างวัตถุสองสามชิ้นฮีปจะหมดและเรียกใช้การรวบรวมขยะ จากนั้นแนวทางที่สองจะถูก "เรียกเก็บเงิน" เพื่อเก็บขยะที่เหลือโดยวิธีแรก ไม่ยุติธรรมมาก!

ที่กล่าวว่าไม่ได้สร้างความแตกต่างอย่างมีนัยสำคัญในตัวอย่างนี้

ไม่ว่าจะมีการดัดแปลงหรือไม่ก็ตามฉันได้ผลลัพธ์ที่แตกต่างจากที่คุณทำ เมื่อฉันรันสิ่งนี้ใช่วิธี toString ให้เวลารัน 6400 ถึง 6600 มิลลิวินาทีในขณะที่วิธีการบันทึกมีค่าสูงสุด 20,000 ถึง 20,400 มิลลิวินาที แทนที่จะเป็นเร็วขึ้นเล็กน้อยวิธีการบันทึกก็ช้าลง 3 เท่าสำหรับฉัน

โปรดทราบว่าทั้งสองวิธีเกี่ยวข้องกับค่าใช้จ่ายที่แตกต่างกันมากดังนั้นจึงไม่น่าตกใจอย่างสิ้นเชิง: วิธี toString จะสร้างวัตถุชั่วคราวจำนวนมากที่ต้องทำความสะอาดในขณะที่วิธีการบันทึกใช้การคำนวณที่เข้มข้นกว่า ดังนั้นความแตกต่างก็คือบนเครื่องที่มีหน่วยความจำน้อย 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;           
}

นั่นวิ่งใน 1,600 ถึง 1,900 millis - น้อยกว่า 1/3 ของวิธี toString และ 1/10 วิธีเข้าสู่ระบบในเครื่องของฉัน

หากคุณมีตัวเลขจำนวนมากคุณสามารถเร่งความเร็วได้มากขึ้นโดยเริ่มจากการหารด้วย 1,000 หรือ 1,000,000 เพื่อลดจำนวนครั้งในการวนซ้ำ ฉันไม่ได้เล่นกับสิ่งนั้น


คุณได้ลองใส่ค่าที่แตกต่างกัน? VM ฮ็อตสป็อตสามารถปรับกราฟให้เหมาะสมมิฉะนั้นจะส่งผลให้เกิดการวัดผิดเนื่องจากมันจะส่งคืนสิ่งที่คำนวณไว้เดิมทุกครั้ง
Erik Aigner

11

ใช้จาวา

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ในการเริ่มต้น


1
เพียงแค่ FYI จะส่งผลให้ไม่มีที่สิ้นสุดหากthe_integerเป็น0เช่นนั้นตรวจสอบสิ่งนั้น
Erik Aigner

10

ยังไม่สามารถแสดงความคิดเห็นได้ดังนั้นฉันจะโพสต์เป็นคำตอบแยกต่างหาก

วิธีแก้ปัญหาแบบลอการิทึมไม่ได้คำนวณจำนวนหลักที่ถูกต้องสำหรับจำนวนเต็มที่มีขนาดใหญ่มากเช่น:

long n = 99999999999999999L;

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

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

วิธีแก้ปัญหาแบบลอการิทึมคำนวณจำนวนหลักที่ไม่ถูกต้องในจำนวนเต็มขนาดใหญ่


ลอง (int) (Math.log10 (n + j)) แทนโดยที่ j คือ 10 - (n - n / 10 * 10)
Erick Stone

8

เนื่องจากจำนวนหลักในฐาน 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);
    }
}

แก้ไขเนื่องจากการแก้ไขล่าสุดของฉันแก้ไขตัวอย่างรหัส แต่ไม่ใช่คำอธิบาย


เย็น. แต่ฉันคิดว่ามันจำเป็นต้องมี abs (หมายเลข) และ "0" ก็เป็นกรณีพิเศษด้วยหรือไม่
DmitryK

ใช่. หากคุณต้องการบัญชีสำหรับสัญญาณคุณจะต้องทำอะไรเช่น1 + (int) Math.floor (Math.log10 (Math.abs (ตัวเลข))) + ((จำนวน <0) 1: 0)
Dirk

5
Math.floorเป็นซ้ำซ้อนบิตไม่ได้หรือไม่ การร่ายintจะทำให้มันกลมลงเลยล่ะค่ะ
CompuChip

5

โซลูชันของ Marian ดัดแปลงมาสำหรับหมายเลขแบบยาว (มากถึง 9,223,372,036,854,775,807) ในกรณีที่มีคนต้องการคัดลอกและวาง ในโปรแกรมฉันเขียนสิ่งนี้สำหรับตัวเลขที่มากถึง 10,000 มีความเป็นไปได้มากกว่าดังนั้นฉันจึงสร้างสาขาเฉพาะสำหรับพวกเขา อย่างไรก็ตามมันจะไม่สร้างความแตกต่างอย่างมีนัยสำคัญ

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

ชื่อเรื่องของคำถามนี้ควรเปลี่ยนเป็น "วิธีรับตัวเลขใน int / long หรือไม่" (และเพิ่มแท็ก 'long')
JAIL

4

วิธีการสตริงอื่น สั้นและหวาน - nสำหรับจำนวนเต็มใด

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

ใช้ได้กับจำนวนเต็มบวกnและศูนย์เท่านั้น สามารถใช้("" + Math.abs(n)).length()เพื่อรับความยาวของจำนวนเต็มลบ
ThisClark

3

ฉันลองได้ไหม ;)

ขึ้นอยู่กับการแก้ปัญหาของเดิร์ค

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

3

คณิตศาสตร์โบราณแบบธรรมดาเป็นอย่างไร? หารด้วย 10 จนกว่าคุณจะถึง 0

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

1
คุณทดสอบแล้วหรือยัง คุณรู้ไหมว่าแม้มันจะยากสำหรับแง่มุมของมนุษย์ แต่มันก็ไม่ได้ผลเหมือนกันกับ "วิธีคิด" ของเครื่องใช่ไหม? --- ให้ฉันเสนอสิ่งหนึ่ง: สร้างอาร์เรย์ของสองล้านหมายเลขโดยเฉพาะอย่างยิ่งLong.MAX_VALUEซึ่งเป็นกรณีที่ซับซ้อนที่สุดของรหัสของคุณและใช้System.nanoTime()ในการทดลองตอกบัตรกับกรณีที่ซับซ้อนที่สุดของโซลูชันอื่น ++ ที่จริงแล้วลองใช้อาร์เรย์ที่เต็มไปด้วยชุดเครื่องมือสุ่มที่ตั้งค่าไว้ที่ช่วง0ไปจนถึงLong.MAX_VALUEเช่นเดียวกับการทดสอบ "ความซับซ้อนโดยเฉลี่ย" ++ คุณอาจพบผลลัพธ์ ... น่าตกใจมาก
XenoRo

@thelima วิธีนี้ทำงานไม่ถูกต้องสำหรับศูนย์หรือเชิงลบ แต่นั่นเป็นข้อผิดพลาดเล็กน้อย หลักการมีลักษณะที่ถูกต้องสำหรับฉัน คุณหมายถึงผลลัพธ์ "ตกตะลึง" อะไร?
Jay

สมมุติว่าคอมพิวเตอร์นั่น ... อืม ... พวกเขาไม่ชอบแบ่ง และในกรณีที่จำเป็นต้องประมวลผล "จำนวนมาก" ของจำนวนมากและแต่ละหลักในแต่ละหมายเลขที่ประมวลผลจะต้องมีการหาร ... เอาล่ะ ... สิ่งต่าง ๆ "เริ่มช้าลงอย่างรวดเร็วจริง ๆ " ... ถ้าคุณจับฉัน ความหมาย ... --- นี่คือเหตุผลที่คุณเห็นคำตอบมากมายที่นี่โดยใช้รหัสตามการทดสอบและการเปรียบเทียบกับแต่ละหลักทศนิยมโดยใช้ 'if's แทนที่จะเป็นดิวิชั่น: ถ้าไม่ใช่เร็วกว่าอย่างน้อยก็รักษาความเร็วส่วนใหญ่ไว้โดยไม่คำนึงถึง ในกรณีที่เลวร้ายที่สุด --- ทำแบบทดสอบระหว่างการใช้หน่วยงานและลอการิทึมในจำนวนมาก ...
XenoRo

@TheLima คุณกำลังพูดถึงอะไร? สำหรับการint,วนซ้ำนี้จะดำเนินการสูงสุด 11 ครั้ง คุณมีหลักฐานยืนยันหรือไม่?
มาร์ควิสแห่ง Lorne

@EJP จากมุมมองฮาร์ดแวร์การแบ่งเป็นกระบวนการวนซ้ำ อัลกอริทึมการหารที่เร็วที่สุดที่ฉันรู้จักคือ radix4 ซึ่งสร้าง 4 บิตต่อการวนซ้ำ ดังนั้นการแบ่ง 32 บิตต้องการการวนซ้ำ 8 ครั้งเป็นอย่างน้อย ยกตัวอย่างเช่นการคูณสามารถทำได้ในแบบคู่ขนานและสามารถแยกย่อยเป็นการคูณที่ง่ายกว่า ไม่ว่าจะเป็นระดับบิตจนถึงระดับบิต (ต้องการการดำเนินงานเพียง 5 ครั้ง) หรือมีการแยกย่อยบางส่วนพร้อมกับตารางค้นหาที่ส่วนท้าย (ขนาดคลาสสิก VS การแลกเปลี่ยนความเร็ว) มันไม่ได้เกี่ยวกับ "จำนวนการวนซ้ำ"; ปัญหาเกี่ยวกับแผนกอยู่ที่ "สิ่งที่แต่ละการทำซ้ำหมายถึง / ไม่ในระดับฮาร์ดแวร์"
XenoRo

2

ทางออกของ Marian ตอนนี้พร้อม 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)));
    }

เพราะเราทำได้


2
อ่านยากนะ อาจเพิ่มช่องว่างและ / หรือบรรทัดใหม่
michaelb958 - GoFundMonica

แต่ไอ้มันพกพาได้!
เทรเวอร์รูดอล์ฟ

1

อยากรู้อยากเห็นฉันพยายามที่จะสร้างมาตรฐาน ...

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


    }
}

ผลลัพธ์คือ:

เวลารัน 1: 6765
s: 400000000
เวลารัน 2: 6000
s: 400000000

ตอนนี้ฉันถูกทิ้งให้สงสัยว่าเกณฑ์มาตรฐานของฉันจริง ๆ แล้วหมายถึงบางสิ่ง แต่ฉันได้รับผลลัพธ์ที่สอดคล้องกัน (การเปลี่ยนแปลงภายในมิลลิวินาที) มากกว่าการทดสอบมาตรฐานหลายตัวเอง ... :) ดูเหมือนว่ามันไร้ประโยชน์ที่จะลองและเพิ่มประสิทธิภาพ ...


แก้ไข: ตามความคิดเห็นของ ptomli ฉันแทนที่ 'number' โดย 'i' ในรหัสด้านบนและได้ผลลัพธ์ต่อไปนี้มากกว่า 5 การรันของผู้พิพากษา:

เวลารัน 1: 11500
s: 788888890
เวลารัน 2: 8547
s: 788888890

เวลารัน 1: 11485
s: 788888890
เวลารัน 2: 8547
s: 788888890

เวลารัน 1: 11469
s: 788888890
เวลารัน 2: 8547
s: 788888890

เวลารัน 1: 11500
s: 788888890
เวลารัน 2: 8547
s: 788888890

เวลารัน 1: 11484
s: 788888890
เวลารัน 2: 8547
s: 788888890

1
เพื่อความสนุกของมันความแตกต่างระหว่างการแจกแจงค่าของจำนวนคืออะไรตั้งแต่ 0 ถึงล้านล้าน? :)
ptomli


0

ทางออกที่ง่าย:

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

ทางออกที่ง่ายมาก:

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

ฉันจะไม่เรียกหนึ่งบรรทัดสำหรับลูปที่มีเนื้อความว่างเปล่าง่ายๆ หรือโมดูโลที่มีกำลัง 10 เพื่อดูว่าคุณได้สิ่งเดียวกันกลับมา (คุณไม่สามารถใช้การเปรียบเทียบได้หรือไม่)
Teepeemm

0

หรือแทนความยาวที่คุณสามารถตรวจสอบว่าหมายเลขมีขนาดใหญ่กว่าหรือเล็กกว่านั้นตามจำนวนที่ต้องการ

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

}


ฉันไม่เข้าใจ ดูเหมือนว่าคุณกำลังตอบคำถามอื่น
Teepeemm

0

ฉันยังไม่เห็นโซลูชันที่ใช้การคูณ การแก้ปัญหาลอการิทึมการหารและการใช้สตริงจะค่อนข้างไม่สะดวกกับกรณีทดสอบหลายล้านครั้งดังนั้นนี่คือหนึ่งใน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 ...

น่าเสียดายที่นี่ไม่สามารถพกพาไปได้longด้วยการแทนที่ทุกกรณีintเนื่องจากล้น ในทางกลับกันมันเกิดขึ้นเพียงเพื่อจะได้ฐาน 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;
}

0

ด้วยการออกแบบ (ขึ้นอยู่กับปัญหา) นี่คือทางเลือกของการหารและพิชิต ก่อนอื่นเราจะกำหนด enum (พิจารณาเฉพาะสำหรับ 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;
    }
}

ตอนนี้เราจะกำหนดคลาสที่ผ่านค่าของ enum และเปรียบเทียบและส่งคืนความยาวที่เหมาะสม

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

เวลารันของโซลูชันนี้เหมือนกับวิธีแบ่งและพิชิต


การหารและพิชิตจะเริ่มตรงกลางและแบ่งพื้นที่การค้นหาที่เหลือ นี่เป็นเวลาทำงานแบบเชิงเส้น แต่มันจะไม่สำคัญสำหรับการเปรียบเทียบเพียง 9 รายการเท่านั้น แต่จะไม่สับสนถ้าnum>=Nine.getValue()ใช่
Teepeemm

0

ใครอยากทำสิ่งนี้ส่วนใหญ่เพราะเขา / เธอต้องการที่จะ "นำเสนอ" ซึ่งส่วนใหญ่หมายความว่าในที่สุดมันก็จำเป็นต้องเป็น "toString-ed" (หรือเปลี่ยนแปลงในทางอื่น) อย่างชัดเจนหรือโดยปริยาย ก่อนที่จะสามารถนำเสนอ (พิมพ์ตัวอย่าง)

หากเป็นเช่นนั้นให้ลองทำการ "toString" ที่จำเป็นและนับจำนวนบิต


0

เราสามารถทำได้โดยใช้วนซ้ำ

    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

ฉันเขียนฟังก์ชันนี้หลังจากดู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;
        }
    }
}

0

ฉันเห็นผู้คนที่ใช้ห้องสมุด String หรือแม้กระทั่งใช้คลาส Integer ไม่มีอะไรผิดปกติ แต่อัลกอริทึมสำหรับการรับจำนวนหลักนั้นไม่ซับซ้อน ฉันใช้ตัวอย่างนี้มานาน แต่มันใช้ได้ดีกับ int

 private static int getLength(long num) {

    int count = 1;

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

    return count;
}

0

ไม่มี 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;
 }

คุณสามารถลองใช้ค่าที่ยิ่งใหญ่กว่านี้ได้ถ้าต้องการ


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

โซลูชัน "หารด้วย 10" ถูกโพสต์ครั้งแรกโดย Sinista เมื่อสองปีก่อน
Teepeemm

-1

วิธีการเวียนเกิดง่าย ๆ

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

ไม่ได้ทดสอบ


3
คุณควรทดสอบ (และตรวจสอบให้แน่ใจว่าเป็นจาวาที่ถูกต้องและมีรูปแบบที่เหมาะสม) แต่วิธีการ "หารด้วย 10" แบบเรียกซ้ำถูกโพสต์โดยเจไดดูลา 3 ปีที่แล้ว
Teepeemm

-2

คุณสามารถใช้ตัวเลขโดยใช้การแบ่งต่อเนื่องสิบ:

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" ถูกโพสต์ครั้งแรกโดย Sinista เมื่อ 3 ปีที่แล้ว นั่นเป็นเหตุผลเดียวที่ฉันคิดได้ว่าคุณได้รับการโหวต
Teepeemm

-2

ใส่จำนวนและสร้างและห่วงในขณะที่จะมีการบันทึกตัวเลขทั้งหมดไปยังArraylist Arraylistจากนั้นเราสามารถนำขนาดของอาร์เรย์ออกมาซึ่งจะเป็นความยาวของค่าจำนวนเต็มที่คุณป้อน

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

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

int m=a.size();

1
ยกเว้นว่าคุณไม่ต้องการ ArrayList หรือตัวเลข
มาร์ควิสแห่ง Lorne

-2

นี่เป็นวิธีที่ง่ายมาก ๆ ที่ฉันใช้กับทุกหมายเลข:

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 เพราะนั่นเท่ากับ. 000001 ช่องว่างด้านหลัง นอกจากนี้ทุกคนมีชื่อตัวแปรที่ดีกว่าnumberCounterใช่หรือไม่ ฉันไม่สามารถคิดอะไรดีขึ้น

แก้ไข: แค่คิดถึงคำอธิบายที่ดีกว่า โดยพื้นฐานแล้วสิ่งนี้ในขณะที่ลูปกำลังทำอยู่คุณสามารถหารหมายเลขด้วย 10 จนกว่ามันจะน้อยกว่าหนึ่ง โดยพื้นฐานแล้วเมื่อคุณแบ่งบางอย่างด้วย 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;
}

-3

ลองแปลงintกับสตริงแล้วได้รับความยาวของสตริง ที่ควรจะได้รับความยาวของint

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

นี่เทียบเท่ากับรหัสต้นฉบับทั้งหมด และจะพลาดเมื่อnumberมีค่าลบ
Teepeemm
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.