Math.random () กับ Random.nextInt (int)


136

ความแตกต่างระหว่างสิ่งที่เป็นMath.random() * nและRandom.nextInt(n)ที่nเป็นจำนวนเต็ม?


ฉันไม่รู้คณิตศาสตร์ แต่ฉันรู้ว่า FindBugs บ่นว่าคุณใช้Math.random()
finnw

3
โปรดจำไว้ว่า Random ไม่มีวิธีคงที่ดังนั้นให้ใช้: (new Random ()). nextInt (n)) เพื่อให้คณิตศาสตร์สร้างการใช้จำนวนเต็มที่คล้ายกัน: Math.floor ((Math.random () * n) +1);
Dimitri Dewaele

คำตอบ:


172

นี่คือคำอธิบายโดยละเอียดว่าเหตุใด " Random.nextInt(n)ทั้งมีประสิทธิภาพมากกว่าและมีความเอนเอียงน้อยกว่าMath.random() * n" จากโพสต์ฟอรัม Sun ที่ Gili เชื่อมโยงถึง:

Math.random () ใช้ Random.nextDouble () ภายใน

Random.nextDouble () ใช้ Random.next () สองครั้งเพื่อสร้างคู่ที่มีบิตกระจายสม่ำเสมอโดยประมาณในแมนทิสซาดังนั้นจึงมีการกระจายอย่างสม่ำเสมอในช่วง 0 ถึง 1- (2 ^ -53)

Random.nextInt (n) ใช้ Random.next () น้อยกว่าสองครั้งโดยเฉลี่ย - จะใช้ครั้งเดียวและหากค่าที่ได้รับสูงกว่าค่าผลคูณสูงสุดของ n ต่ำกว่า MAX_INT ก็จะลองอีกครั้งมิฉะนั้นจะส่งกลับค่าโมดูโล n ป้องกันไม่ให้ค่าที่สูงกว่าค่าพหุคูณสูงสุดของ n ต่ำกว่า MAX_INT บิดเบือนการแจกแจง) ดังนั้นจึงส่งคืนค่าที่กระจายอย่างสม่ำเสมอในช่วง 0 ถึง n-1

ก่อนที่จะปรับขนาดด้วย 6 ผลลัพธ์ของ Math.random () คือค่าหนึ่งใน 2 ^ 53 ที่เป็นไปได้ซึ่งดึงมาจากการแจกแจงแบบสม่ำเสมอ

การสเกลด้วย 6 ไม่ได้เปลี่ยนจำนวนค่าที่เป็นไปได้และการร่ายเป็น int จะบังคับให้ค่าเหล่านี้เป็นหนึ่งในหก 'ถัง' (0, 1, 2, 3, 4, 5) แต่ละที่เก็บข้อมูลจะสอดคล้องกับช่วงที่ครอบคลุมอย่างใดอย่างหนึ่ง 1501199875790165 หรือ 1501199875790166 ของค่าที่เป็นไปได้ (เนื่องจาก 6 ไม่ใช่ disvisor ของ 2 ^ 53) ซึ่งหมายความว่าสำหรับการทอยลูกเต๋าจำนวนเพียงพอ (หรือดายที่มีจำนวนด้านมากเพียงพอ) ดายจะแสดงว่าตัวเองเอนเอียงไปทางถังที่ใหญ่กว่า

คุณจะต้องรอเป็นเวลานานมากในการทอยลูกเต๋าเพื่อให้เอฟเฟกต์นี้ปรากฏขึ้น

Math.random () ยังต้องการการประมวลผลประมาณสองเท่าและอาจมีการซิงโครไนซ์


3
Random.nextInt และ nextDouble ยังขึ้นอยู่กับการซิงโครไนซ์
adrianos

ในบริบทนี้ "เอนเอียงน้อย" หมายความว่าอย่างไร
ΦXocę웃Пepeúpaツ

2
@ ΦXocę웃Пepeúpaツหมายความว่าตัวเลขบางตัวมีแนวโน้มที่จะถูกวาดมากกว่าตัวเลขอื่น เนื่องจากมีความลำเอียงในการเลือกตัวเลขมากกว่าตัวเลขอื่น ๆ (ดังนั้นจึงไม่สุ่มทั้งหมดหรือให้ชุดตัวอย่างขนาดใหญ่พอ)
นายอำเภอ ford

1
โปรดทราบว่าความคิดเห็นสุดท้ายของเธรดนั้นรายงาน: "อคติที่อธิบายเป็นส่วนหนึ่งใน 2 ^ 53 แต่ความยาวรอบสูงสุดของ PRNG ที่ใช้คือ 2 ^ 48 เท่านั้นดังนั้นสิ่งที่คุณจะเห็นในแอปพลิเคชันคือข้อมูล การกระจายของ PRNG ที่เป็นพื้นฐานไม่ใช่อคติ " สิ่งนี้จะชี้ให้เห็นว่าทั้งสองวิธีนี้เทียบเท่ากัน
ภาพลวงตาดิจิทัล

2
@ ΦXocę웃Пepeúpaツแทนที่6ด้วย5ลูกบาศก์ลูกเต๋า: มันจะเป็น "5-biased" คุณสามารถทอยลูกเต๋าสองสามครั้งก่อนที่คุณจะสังเกตเห็นว่ามีบางอย่างผิดปกติกับลูกเต๋า คุณถูกบังคับให้ทำการตรวจสอบอย่างละเอียดถี่ถ้วนก่อนที่คุณจะสังเกตเห็นว่ามีบางอย่างผิดปกติกับเครื่องกำเนิดไฟฟ้าแบบสุ่ม
DávidHorváth

27

จุดสำคัญอีกประการหนึ่งคือ Random.nextInt (n) สามารถทำซ้ำได้เนื่องจากคุณสามารถสร้างวัตถุสุ่มสองชิ้นที่มีเมล็ดพันธุ์เดียวกันได้ สิ่งนี้ไม่สามารถทำได้ด้วย Math.random ()


14

อ้างอิงจากhttps://forums.oracle.com/forums/thread.jspa?messageID=6594485龵 Random.nextInt(n)ทั้งมีประสิทธิภาพมากกว่าและมีความเอนเอียงน้อยกว่าMath.random() * n


21
ฉันอยากจะแนะนำให้อ้างถึงโพสต์ของเขาและสรุปเล็กน้อยที่นี่ ลิงก์ควรเป็นส่วนเสริมสำหรับคำตอบของคุณ
jjnguy

0

ตามตัวอย่างนี้Random.nextInt(n)มีผลลัพธ์ที่คาดเดาได้น้อยกว่าดังนั้น Math.random () * n ตาม [อาร์เรย์เรียงเร็วกว่าอาร์เรย์ไม่ได้เรียงลำดับ] [1] ผมคิดว่าเราสามารถพูดได้ Random.nextInt (n) เป็นเรื่องยากที่จะคาดการณ์

usingRandomClass: เวลา: 328ไมล์วินาที

usingMathsRandom: เวลา: 187ไมล์วินาที

package javaFuction;
import java.util.Random;
public class RandomFuction 
{
    static int array[] = new int[9999];
    static long sum = 0;
    public static void usingMathsRandom() {
        for (int i = 0; i < 9999; i++) {
         array[i] = (int) (Math.random() * 256);
       }

        for (int i = 0; i < 9999; i++) {
            for (int j = 0; j < 9999; j++) {
                if (array[j] >= 128) {
                    sum += array[j];
                }
            }
        }
    }

    public static void usingRandomClass() {
        Random random = new Random();
        for (int i = 0; i < 9999; i++) {
            array[i] = random.nextInt(256);
        }

        for (int i = 0; i < 9999; i++) {
            for (int j = 0; j < 9999; j++) {
                if (array[j] >= 128) {
                    sum += array[j];
                }
            }

        }

    }

    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        usingRandomClass();
        long end = System.currentTimeMillis();
        System.out.println("usingRandomClass " + (end - start));
        start = System.currentTimeMillis();
        usingMathsRandom();
        end = System.currentTimeMillis();
        System.out.println("usingMathsRandom " + (end - start));

    }

}

1
ในลูปที่สองให้คุณตรวจสอบ> = 50 ซึ่งเป็นจริงมากกว่า 50% ของกรณี นั่นจะทำให้คำสั่ง if นี้เป็นจริงเกือบตลอดเวลาซึ่งทำให้คาดเดาได้ง่ายขึ้น ผลลัพธ์ของคุณจึงเอนเอียงไปตามคำตอบของคุณ
Neuron

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