วิธีสร้างเลขสุ่มใน Java

⚡ สรุปอย่างชาญฉลาด

ตัวเลขสุ่มใน Java มาจากตัวเลือกไลบรารีมาตรฐานที่เกี่ยวข้องสี่แบบ ได้แก่ คลาส Random, Math.random, ThreadLocalRandom และ SecureRandom ซึ่งแต่ละแบบครอบคลุมส่วนผสมที่แตกต่างกันของความเร็ว ความปลอดภัยในการทำงานแบบมัลติเธรด และความแข็งแกร่งทางด้านการเข้ารหัสลับสำหรับการใช้งานจริง

  • 🎲 ชั้นเรียนแบบสุ่ม: java.util.Random มีเมธอด nextInt, nextLong และ next ให้ใช้งานDoubleและ nextBoolean สำหรับประเภทข้อมูลพื้นฐานทั่วไป
  • 🧮 คณิตศาสตร์สุ่ม: Math.random ส่งคืนค่า double ระหว่าง 0.0 (รวม 0.0) และ 1.0 (ไม่รวม 1.0) โดยใช้ instance ของ Random ที่ใช้ร่วมกัน
  • 🎯 ช่วงกำหนดเอง: ส่งค่าขอบเขตไปยัง nextInt หรือปรับขนาด Math.random เพื่อสร้างค่าภายในค่าต่ำสุดและสูงสุดที่กำหนด
  • 🧵 ความปลอดภัยของเกลียว: ThreadLocalRandom ใน java.util.concurrent จะให้ตัวสร้างตัวเลขสุ่มเฉพาะแก่แต่ละเธรด และมีประสิทธิภาพดีกว่าการใช้ตัวเลขสุ่มร่วมกันภายใต้สภาวะที่มีการแย่งชิงทรัพยากร
  • 🔐 การรักษาความปลอดภัย: ฟังก์ชัน SecureRandom ใน java.security สร้างค่าที่มีความแข็งแกร่งทางด้านการเข้ารหัสสำหรับรหัสผ่าน โทเค็น และคีย์เซสชัน
  • 🌱 ความสามารถในการทำซ้ำ: การกำหนดค่าเริ่มต้นให้กับตัวแปรสุ่มด้วยค่าความยาวคงที่ จะทำให้การทดสอบและการจำลองมีความแน่นอนในทุกครั้งที่ทำการทดลอง

วิธีสร้างเลขสุ่มใน Java

ตัวเลขสุ่มปรากฏอยู่ทุกหนทุกแห่ง Javaตั้งแต่ข้อมูลสำหรับการทดสอบหน่วยและเกมทอยลูกเต๋า ไปจนถึงโทเค็นเซสชันและสำรับไพ่ที่สับแล้ว บทช่วยสอนนี้จะอธิบายวิธีการสร้างตัวเลขสุ่มใน Java การใช้งานทุกตัวเลือกในไลบรารีมาตรฐาน และวิธีการเลือกตัวเลือกที่เหมาะสมสำหรับงานนั้นๆ

สร้างตัวเลขสุ่มใน Java

การขอ Java ไลบรารีมาตรฐานมีวิธีการสร้างเลขสุ่มอยู่ 4 วิธีที่ใช้กันทั่วไป:

  • java.util.Random เป็นคลาสอเนกประสงค์ สามารถสร้างค่าบูลีน, จำนวนเต็ม, จำนวนเต็มยาว, จำนวนทศนิยม และจำนวนทศนิยมสองเท่า ผ่านเมธอดต่างๆ เช่น nextInt, nextLongและ nextDouble.
  • Math.random เป็นฟังก์ชันช่วยแบบคงที่ที่ส่งค่ากลับเป็นเลขทศนิยมระหว่าง 0.0 (รวม) และ 1.0 (ไม่รวม) โดยใช้ตัวแปรที่ใช้ร่วมกัน Random ภายใน
  • ThreadLocalRandom in java.util.concurrent แต่ละเธรดจะมีตัวสร้างของตัวเอง ทำให้หลายเธรดไม่แย่งกันใช้ตัวสร้างเดียว
  • SecureRandom in java.security สร้างเลขสุ่มเทียมที่มีความแข็งแกร่งทางด้านการเข้ารหัสสำหรับรหัสผ่าน ตัวระบุเซสชัน และคีย์

ส่วนด้านล่างนี้แสดงวิธีการสร้างตัวเลขสุ่มสิบตัวด้วยแต่ละวิธี

ตัวอย่าง: สร้างตัวเลขสุ่มโดยใช้ Java คลาสสุ่ม

ตัวอย่างแรกใช้ java.util.Random เพื่อสร้างจำนวนเต็มสิบจำนวนในช่วง 0 ถึง 99 รวมทั้งสองจำนวน:

import java.util.Random;

public class RandomNumbers {
    public static void main(String[] args) {
        Random objGenerator = new Random();
        for (int iCount = 0; iCount < 10; iCount++) {
            int randomNumber = objGenerator.nextInt(100);
            System.out.println("Random No : " + randomNumber);
        }
    }
}

Output:

Random No : 17
Random No : 57
Random No : 73
Random No : 48
Random No : 68
Random No : 86
Random No : 34
Random No : 97
Random No : 73
Random No : 18

ตัวอย่างของ Random ถูกสร้างขึ้นเป็น objGeneratorส่วน nextInt(int bound) เมธอดนี้จะส่งคืนค่าระหว่าง 0 (รวม 0 ด้วย) และขอบเขตที่กำหนด (ไม่รวม 0 ด้วย) ดังนั้น nextInt(100) ให้ค่าตั้งแต่ 0 ถึง 99 หากต้องการให้ลำดับนั้นสามารถสร้างซ้ำได้ ให้ส่งค่าเริ่มต้น (seed) ไปยังตัวสร้าง เช่น new Random(42)เมล็ดพันธุ์เดียวกันจะให้ลำดับเดียวกันเสมอ ซึ่งมีประโยชน์ในการทดสอบและการจำลอง

ตัวอย่าง: การใช้ Java คณิตศาสตร์สุ่ม

เพื่อสร้างค่าสุ่มสิบค่าในช่วง 0.0 ถึง 1.0 ให้เรียกใช้ฟังก์ชันคงที่ Math.random วิธีการภายใน ห่วงนี่เป็นวิธีที่เร็วที่สุดในการสร้างตัวเลขทศนิยมแบบสุ่มโดยไม่ต้องสร้างอ็อบเจ็กต์ตัวสร้าง:

public class DemoRandom {
    public static void main(String[] args) {
        for (int xCount = 0; xCount < 10; xCount++) {
            System.out.println(Math.random());
        }
    }
}

Output:

0.46518450373334297
0.14859851177803485
0.5628391820492477
0.6323378498048606
0.1740198445692248
0.9140544122258946
0.9167350036262347
0.49251219841030147
0.7426056725722353
0.2039418871298877

ภายใต้ประทุน Math.random สร้างค่าคงที่เดียว Random ในการใช้งานครั้งแรกและการโทร nextDouble บนนั้น ตัวสร้างข้อมูลร่วมตัวเดียวนั้นเหมาะสมสำหรับการสาธิต แต่จะกลายเป็นคอขวดภายใต้ภาระการทำงานแบบมัลติเธรดหนักๆ ซึ่งส่วนถัดไปจะกล่าวถึง

สร้างเลขสุ่มในช่วงที่กำหนดเอง

โปรแกรมที่ใช้งานจริงมักต้องการค่าสุ่มภายในช่วงที่กำหนด เช่น จำนวนเต็มระหว่าง 1 ถึง 6 สำหรับการทอยลูกเต๋า หรือจำนวนทศนิยมระหว่าง 10.0 ถึง 20.0 สำหรับการจำลอง โดยมีรูปแบบสองแบบที่ครอบคลุมเกือบทุกกรณี

จำนวนเต็มสุ่มระหว่างค่าต่ำสุดและค่าสูงสุด (รวมทั้งค่าต่ำสุดและค่าสูงสุด):

import java.util.Random;

public class RangeDemo {
    public static void main(String[] args) {
        Random rnd = new Random();
        int min = 1;
        int max = 6;
        // nextInt(max - min + 1) returns 0..(max-min), then shift by min
        int dice = rnd.nextInt(max - min + 1) + min;
        System.out.println("Dice roll : " + dice);
    }
}

สุ่มสองเท่าระหว่างค่าต่ำสุดและค่าสูงสุด:

double min = 10.0;
double max = 20.0;
double value = min + (max - min) * Math.random();
System.out.println("Value : " + value);

ทั้งสองรูปแบบทำงานโดยการดึงค่าจากช่วงค่าดั้งเดิมของตัวสร้าง ปรับขนาดให้เข้ากับความกว้างของช่วงค่าเป้าหมาย และเลื่อนขึ้นไปตามค่าต่ำสุด หากคุณกำลังใช้งานอยู่ Java สำหรับเวอร์ชัน 8 หรือใหม่กว่า จะให้ผลลัพธ์เดียวกันผ่านเวอร์ชันใหม่กว่า ints, longsและ doubles วิธีการสตรีมบน Random, ตัวอย่างเช่น rnd.ints(10, 1, 7).forEach(System.out::println) สำหรับการทอยลูกเต๋า 10 ครั้ง

ThreadLocalRandom สำหรับมัลติเธรด Code

การแบ่งปันเพียงครั้งเดียว Random ปลอดภัยต่อการใช้งานแบบมัลติเธรด แต่จะทำงานช้าลงเมื่อเกิดการแย่งชิงทรัพยากร เนื่องจากทุกเธรดต้องซิงโครไนซ์กับ seed ภายในเดียวกัน ThreadLocalRandomเพิ่มใน Java 7 และอาศัยอยู่ใน java.util.concurrentโดยแต่ละเธรดจะมีตัวสร้างของตัวเอง จึงไม่มีปัญหาการแย่งชิงล็อกเกิดขึ้น

import java.util.concurrent.ThreadLocalRandom;

public class ThreadLocalDemo {
    public static void main(String[] args) {
        // random int in [1, 100]
        int i = ThreadLocalRandom.current().nextInt(1, 101);

        // random long in [1000, 5000]
        long l = ThreadLocalRandom.current().nextLong(1000L, 5001L);

        // random double in [0.0, 1.0)
        double d = ThreadLocalRandom.current().nextDouble();

        System.out.println(i + " " + l + " " + d);
    }
}

ใช้ ThreadLocalRandom.current() ภายในเธรดการทำงาน สตรีมแบบขนาน และงานพร้อมกันใดๆ โปรดทราบว่า setSeed พ่น UnsupportedOperationExceptionดังนั้นจึงไม่รองรับการใช้ seed เพื่อการทำซ้ำ หากคุณต้องการ seed ที่คงที่ ให้ใช้ seed แบบธรรมดา Random.

SecureRandom สำหรับการสุ่มแบบคำนึงถึงความปลอดภัย Numbers

อินสแตนซ์ของ Random และ ThreadLocalRandom ไม่มีความปลอดภัยทางด้านการเข้ารหัส หากมีตัวอย่างผลลัพธ์เพียงไม่กี่ตัวอย่าง ผู้โจมตีก็สามารถสร้างสถานะภายในขึ้นใหม่และคาดเดาค่าในอนาคตได้ สำหรับรหัสผ่าน โทเค็นเซสชัน ค่าเกลือ และคีย์การเข้ารหัส ให้ใช้ java.security.SecureRandom แต่ในทางกลับกัน มันดึงข้อมูลเริ่มต้นจากแหล่งเอนโทรปีของระบบปฏิบัติการ และสร้างค่าที่ไม่สามารถคาดเดาได้

import java.security.SecureRandom;

public class SecureDemo {
    public static void main(String[] args) {
        SecureRandom secure = new SecureRandom();

        byte[] token = new byte[16];
        secure.nextBytes(token);          // 128-bit random token

        int otp = secure.nextInt(1_000_000); // 6-digit one-time code

        System.out.println("OTP : " + String.format("%06d", otp));
    }
}

SecureRandom ช้ากว่า Random เนื่องจากใช้อัลกอริทึมที่ทรงพลังกว่าและข้อมูลเริ่มต้นที่ละเอียดกว่า ควรสงวนไว้สำหรับงานด้านความปลอดภัย และใช้ตัวสร้างข้อมูลที่เร็วกว่าสำหรับเกม การจำลอง และข้อมูลทดสอบ

คำถามที่พบบ่อย

สร้างอินสแตนซ์ของ java.util.Random แล้วเรียกใช้เมธอด nextInt, nextLong, nextDoubleหรือ nextBoolean ก็ได้ สำหรับวิธีที่รวดเร็วและง่าย ให้ใช้ Math.random ซึ่งจะคืนค่าเป็น double ระหว่าง 0.0 ถึง 1.0 ทั้งสองวิธีนี้มีอยู่ในไลบรารีมาตรฐานอยู่แล้ว

เรียกใช้ random.nextInt(max – min + 1) + min สำหรับจำนวนเต็ม หรือ min + (max – min) * Math.random() สำหรับจำนวนทศนิยม สูตรเหล่านี้จะปรับขนาดช่วงของตัวสร้างให้เข้ากับหน้าต่างเป้าหมายของคุณและเลื่อนไปตามค่าต่ำสุด

Random เป็นคลาสเต็มรูปแบบที่มีเมธอดสำหรับประเภทข้อมูลพื้นฐานหลายประเภทและรองรับการกำหนดค่าเริ่มต้น (seeding) Math.random เป็นตัวช่วยแบบ static ที่ส่งค่าคืนเป็น double ระหว่าง 0.0 ถึง 1.0 ภายในแล้ว Math.random จะเรียกใช้ instance ของ Random ที่ใช้ร่วมกันเพียงตัวเดียว

ใช่ แต่การเรียกใช้งานพร้อมกันจะแย่งชิง seed ภายในตัวเดียวกัน ซึ่งทำให้โค้ดที่มีมัลติเธรดทำงานช้าลง ให้ใช้ ThreadLocalRandom.current() จาก java.util.concurrent เพื่อกำหนดให้แต่ละเธรดมี generator ของตัวเอง และขจัดปัญหาการแย่งชิง seed นั้น

ส่งค่า long คงที่ไปยังตัวสร้าง Random เช่น new Random(42) หรือเรียก setSeed(42) ตัวสร้างจะสร้างลำดับเดียวกันทุกครั้งที่ทำงาน ซึ่งจำเป็นสำหรับการทดสอบและการจำลองที่ทำซ้ำได้

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

เฟรมเวิร์กการเรียนรู้ของเครื่องใช้ตัวสร้างเลขสุ่มเทียมเพื่อกำหนดค่าเริ่มต้นของน้ำหนัก สับเปลี่ยนข้อมูลฝึกฝน และแบ่งชุดข้อมูลตรวจสอบ การกำหนดค่าเริ่มต้น (seed) ให้คงที่ทำให้การทดลองสามารถทำซ้ำได้ ทีมงานจึงสามารถเปรียบเทียบการทำงานของโมเดลและแก้ไขข้อผิดพลาดได้อย่างน่าเชื่อถือ

ใช่แล้ว GitHub Copilot สร้างโค้ดตัวอย่างแบบสุ่ม (Random), แบบสุ่มเฉพาะเธรด (ThreadLocalRandom) และแบบปลอดภัย (SecureRandom) พร้อมสูตรการปรับขนาดช่วงที่ตรงกัน และ JUnit การทดสอบ Revตรวจสอบข้อเสนอแนะแต่ละข้อเพื่อให้แน่ใจว่าคลาสที่เลือกนั้นเหมาะสมกับกรณีการใช้งาน โดยเฉพาะอย่างยิ่งสำหรับโค้ดที่มีความอ่อนไหวต่อความปลอดภัย

สรุปโพสต์นี้ด้วย: