181783497276652981 และ 8682522807148012 ใน Random (Java 7) คืออะไร


112

ทำไมถึงได้รับเลือก181783497276652981และ?8682522807148012Random.java

นี่คือซอร์สโค้ดที่เกี่ยวข้องจาก Java SE JDK 1.7:

/**
 * Creates a new random number generator. This constructor sets
 * the seed of the random number generator to a value very likely
 * to be distinct from any other invocation of this constructor.
 */
public Random() {
    this(seedUniquifier() ^ System.nanoTime());
}

private static long seedUniquifier() {
    // L'Ecuyer, "Tables of Linear Congruential Generators of
    // Different Sizes and Good Lattice Structure", 1999
    for (;;) {
        long current = seedUniquifier.get();
        long next = current * 181783497276652981L;
        if (seedUniquifier.compareAndSet(current, next))
            return next;
    }
}

private static final AtomicLong seedUniquifier
    = new AtomicLong(8682522807148012L);

ดังนั้นการกล่าวอ้างnew Random()ไม่มีพารามิเตอร์เมล็ดใด ๆ ใช้เวลาปัจจุบัน "uniquifier เมล็ดพันธุ์" และ XORs System.nanoTime()มันด้วย จากนั้นจะใช้181783497276652981เพื่อสร้างเมล็ดพันธุ์ที่ไม่ซ้ำกันเพื่อเก็บไว้ในครั้งต่อไปnew Random()เรียกว่า

ตัวอักษร181783497276652981Lและ8682522807148012Lไม่ได้อยู่ในค่าคงที่ แต่จะไม่ปรากฏที่อื่น

ตอนแรกความคิดเห็นทำให้ฉันเป็นผู้นำได้ง่าย ค้นหาออนไลน์สำหรับบทความที่ทำให้บทความที่เกิดขึ้นจริง 8682522807148012ไม่ปรากฏในกระดาษ แต่181783497276652981ไม่ปรากฏ - เป็น substring ของหมายเลขอื่น1181783497276652981ซึ่งเป็น181783497276652981ที่มี1เพิ่มด้านหน้า

กระดาษอ้างว่า1181783497276652981เป็นตัวเลขที่ให้ผล "บุญ" ที่ดีสำหรับเครื่องกำเนิดไฟฟ้าที่สอดคล้องกันเชิงเส้น ตัวเลขนี้ถูกคัดลอกไปยัง Java ผิดพลาดหรือไม่? ไม่181783497276652981มีบุญที่ยอมรับ?

และทำไมถึงถูก8682522807148012เลือก?

ค้นหาออนไลน์สำหรับจำนวนทั้งอัตราผลตอบแทนไม่มีคำอธิบายใดเพียงหน้านี้ที่ยังสังเกตเห็นลดลงในด้านหน้าของ1181783497276652981

สามารถเลือกหมายเลขอื่นที่จะใช้งานได้เช่นเดียวกับตัวเลขสองตัวนี้หรือไม่? ทำไมหรือทำไมไม่?


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

6
8682522807148012เป็นมรดกของรุ่นก่อนหน้าของชั้นเรียนที่สามารถเห็นได้ในการแก้ไขที่ทำในปี 2010 181783497276652981Lดูเหมือนว่าจะมีการพิมพ์ผิดจริงและคุณสามารถยื่นรายงานข้อผิดพลาด
assylias

6
ไม่ว่าจะเป็นการพิมพ์ผิดเช่นข้อบกพร่องหรือคุณลักษณะที่มีแรงจูงใจที่ไม่เปิดเผย คุณจะต้องถามผู้เขียน สิ่งที่คุณได้รับที่นี่จะเป็นเพียงความคิดเห็นที่ไม่รู้ไม่มากก็น้อย หากคุณคิดว่าเป็นข้อบกพร่องโปรดส่งรายงานข้อบกพร่อง
Marquis of Lorne

1
โดยเฉพาะอย่างยิ่งเมื่อได้รับคำตอบที่แตกต่างกันนี่อาจเป็นคำถามสองข้อสำหรับค่าคงที่แต่ละค่า
Mark Hurd

1
น่าเศร้าที่เห็นปัญหาคอขวดที่ปรับขนาดได้ทั่วโลกในชั้นพื้นฐานดังกล่าว seedUniquifierสามารถโต้แย้งได้อย่างมากในกล่อง 64 คอร์ เธรดโลคัลจะปรับขนาดได้มากกว่า
usr

คำตอบ:


57
  1. ตัวเลขนี้ถูกคัดลอกไปยัง Java ผิดพลาดหรือไม่?

    ใช่ดูเหมือนจะพิมพ์ผิด

  2. 181783497276652981 มีบุญที่ยอมรับได้หรือไม่?

    สิ่งนี้สามารถกำหนดได้โดยใช้อัลกอริทึมการประเมินที่แสดงในเอกสาร แต่ผลบุญของเลข "เดิม" น่าจะสูงกว่า

  3. ทำไม 8682522807148012 จึงถูกเลือก?

    ดูเหมือนจะสุ่ม อาจเป็นผลมาจาก System.nanoTime () เมื่อเขียนโค้ด

  4. สามารถเลือกหมายเลขอื่นที่จะใช้งานได้เช่นเดียวกับตัวเลขสองตัวนี้หรือไม่?

    ไม่ใช่ว่าตัวเลขทุกตัวจะ "ดี" เท่ากัน ดังนั้นไม่

กลยุทธ์การเพาะเมล็ด

มีความแตกต่างในสคีมาเริ่มต้นระหว่างเวอร์ชันต่างๆและการใช้งาน JRE

public Random() { this(System.currentTimeMillis()); }
public Random() { this(++seedUniquifier + System.nanoTime()); }
public Random() { this(seedUniquifier() ^ System.nanoTime()); }

อันแรกไม่สามารถยอมรับได้หากคุณสร้าง RNG หลายรายการติดต่อกัน หากเวลาในการสร้างของพวกเขาอยู่ในช่วงมิลลิวินาทีเดียวกันพวกเขาจะให้ลำดับที่เหมือนกันทั้งหมด (เมล็ดพันธุ์เดียวกัน => ลำดับเดียวกัน)

อันที่สองไม่ปลอดภัยต่อด้าย เธรดหลายเธรดสามารถรับ RNG ที่เหมือนกันได้เมื่อเริ่มต้นพร้อมกัน นอกจากนี้เมล็ดพันธุ์ของการเริ่มต้นในภายหลังมักจะมีความสัมพันธ์กัน ขึ้นอยู่กับความละเอียดของตัวจับเวลาจริงของระบบลำดับเมล็ดพันธุ์อาจเพิ่มขึ้นแบบเชิงเส้น (n, n + 1, n + 2, ... ) ตามที่ระบุไว้ในเมล็ดสุ่มต้องแตกต่างกันอย่างไร? และกระดาษอ้างอิงข้อบกพร่องทั่วไปในการเริ่มต้นของเครื่องกำเนิดไฟฟ้าหมายเลขหลอกเมล็ดพันธุ์ที่สัมพันธ์กันสามารถสร้างความสัมพันธ์ระหว่างลำดับจริงของ RNG หลายตัว

แนวทางที่สามสร้างการกระจายแบบสุ่มและทำให้เมล็ดพันธุ์ที่ไม่เกี่ยวข้องกันแม้จะข้ามเธรดและการเริ่มต้นในภายหลัง ดังนั้นเอกสารจาวาปัจจุบัน:

ตัวสร้างนี้ตั้งค่าเมล็ดพันธุ์ของตัวสร้างตัวเลขสุ่มเป็นค่าที่น่าจะแตกต่างจากการเรียกใช้อื่น ๆ ของตัวสร้างนี้

สามารถขยายได้โดย "ข้ามเธรด" และ "ไม่เกี่ยวข้อง"

คุณภาพของลำดับเมล็ดพันธุ์

แต่การสุ่มของลำดับการเพาะเมล็ดนั้นดีพอ ๆ กับ RNG พื้นฐานเท่านั้น RNG ที่ใช้สำหรับลำดับ seed ในการใช้งาน java นี้ใช้เครื่องกำเนิดไฟฟ้าที่สอดคล้องกันเชิงเส้นแบบคูณ (MLCG) ที่มี c = 0 และ m = 2 ^ 64 (โมดูลัส 2 ^ 64 ได้รับโดยปริยายจากการล้นของจำนวนเต็มยาว 64 บิต) เนื่องจากศูนย์ c และกำลังของ 2 - โมดูลัส "คุณภาพ" (ความยาวรอบบิต - สหสัมพันธ์ ... ) จึงมี จำกัด . ตามที่กระดาษกล่าวไว้นอกจากความยาวของรอบโดยรวมแล้วทุกๆบิตจะมีความยาวรอบของตัวเองซึ่งจะลดลงแบบทวีคูณสำหรับบิตที่มีนัยสำคัญน้อยกว่า ดังนั้นบิตที่ต่ำกว่าจึงมีรูปแบบการทำซ้ำที่เล็กกว่า (ผลลัพธ์ของ seedUniquifier () ควรเป็นบิตย้อนกลับก่อนที่จะถูกตัดทอนเป็น 48 บิตใน RNG จริง)

แต่มันเร็ว! และเพื่อหลีกเลี่ยงการเปรียบเทียบและตั้งค่าลูปที่ไม่จำเป็นตัวห่วงควรเร็ว สิ่งนี้อาจอธิบายถึงการใช้ MLCG เฉพาะนี้โดยไม่ต้องเพิ่มโดยไม่ต้อง xoring เพียงแค่คูณ

และกระดาษดังกล่าวแสดงรายการ "ตัวคูณ" ที่ดีสำหรับ c = 0 และ m = 2 ^ 64 เป็น 1181783497276652981

สรุปทั้งหมด: สำหรับความพยายาม @ JRE- นักพัฒนา;) แต่มีการพิมพ์ผิด (แต่ใครจะรู้เว้นแต่จะมีใครประเมินมันมีความเป็นไปได้ที่ 1 อันดับที่ขาดหายไปจะช่วยปรับปรุง RNG ในการเพาะเมล็ดได้จริง)

แต่ตัวคูณบางตัวแย่กว่าแน่นอน: "1" นำไปสู่ลำดับคงที่ "2" นำไปสู่ลำดับการเคลื่อนที่แบบบิตเดียว (สัมพันธ์กัน) ...

ความสัมพันธ์ระหว่างลำดับสำหรับ RNG นั้นเกี่ยวข้องกับการจำลอง (มอนติคาร์โล) จริง ๆ โดยที่ลำดับสุ่มหลาย ๆ ลำดับจะถูกสร้างอินสแตนซ์และแม้แต่ขนานกัน ดังนั้นกลยุทธ์การเพาะเมล็ดที่ดีจึงจำเป็นเพื่อให้ได้การจำลองแบบ "อิสระ" ดังนั้นมาตรฐาน C ++ 11 จึงนำเสนอแนวคิดของลำดับเมล็ดพันธุ์สำหรับการสร้างเมล็ดพันธุ์ที่ไม่เกี่ยวข้องกัน


3
อย่างน้อยก็ยังแปลกถ้าพวกเขาทิ้งค่าที่มีนัยสำคัญน้อยที่สุดแทนที่จะเป็นค่าที่สำคัญที่สุดการคูณทุกครั้งจะสูญเสียเล็กน้อยจนในที่สุด (หลังจาก 62 ขั้นตอน) seedUniquifierจะกลายเป็นติดอยู่ที่ศูนย์
harold

9

หากคุณพิจารณาว่าสมการที่ใช้สำหรับตัวสร้างตัวเลขสุ่มคือ:

LCGEquation

โดยที่ X (n + 1) เป็นตัวเลขถัดไป a คือตัวคูณ X (n) คือจำนวนปัจจุบัน c คือส่วนเพิ่มและ m คือโมดูลัส

หากคุณดูเพิ่มเติมRandoma, c และ m จะถูกกำหนดไว้ในส่วนหัวของคลาส

private static final long multiplier = 0x5DEECE66DL;   //= 25214903917 -- 'a'
private static final long addend = 0xBL;               //= 11          -- 'c'
private static final long mask = (1L << 48) - 1;       //= 2 ^ 48 - 1  -- 'm'

และดูวิธีการprotected int next(int bits)นี้คือการใช้สมการ

nextseed = (oldseed * multiplier + addend) & mask;
//X(n+1) =  (X(n)   *      a     +    c  ) mod m

นี่ก็หมายความว่าวิธีการที่seedUniquifier()เป็นจริงที่ได้รับ X (n) หรือในกรณีแรกที่ initialisation X (0) ซึ่งเป็นจริงค่านี้มีการแก้ไขแล้วต่อไปโดยค่าของ8682522807148012 * 181783497276652981 System.nanoTime()อัลกอริทึมนี้สอดคล้องกับสมการด้านบน แต่มี X (0) = 8682522807148012, a = 181783497276652981, m = 2 ^ 64 และ c = 0 ต่อไปนี้ แต่เนื่องจาก mod m ของถูกสร้างไว้ล่วงหน้าด้วยการโอเวอร์โฟลว์ที่ยาวนานสมการข้างบนก็กลายเป็น

EQ2

เมื่อมองไปที่กระดาษค่าของ a = 1181783497276652981คือสำหรับ m = 2 ^ 64, c = 0 ดังนั้นดูเหมือนว่าจะเป็นการพิมพ์ผิดและค่า8682522807148012X (0) ซึ่งดูเหมือนจะเป็นตัวเลขที่สุ่มเลือกจากรหัสเดิม สำหรับRandom. ดังที่เห็นนี้. แต่ข้อดีของตัวเลขที่เลือกเหล่านี้ยังสามารถใช้ได้ แต่ตามที่ Thomas B. กล่าวไว้อาจจะไม่ "ดี" เท่ากับตัวเลขในกระดาษ

แก้ไข - ด้านล่างความคิดเดิมได้รับการชี้แจงแล้วดังนั้นจึงสามารถเพิกเฉยได้ แต่ปล่อยไว้เพื่ออ้างอิง

สิ่งนี้ทำให้ฉันได้ข้อสรุป:

  1. การอ้างอิงไปยังกระดาษไม่ได้มีไว้สำหรับค่าตัวเอง แต่เป็นวิธีการที่ใช้ในการรับค่าเนื่องจากค่า a, c และ m ที่แตกต่างกัน

  2. เป็นเพียงเรื่องบังเอิญที่ค่านั้นเหมือนกันนอกเหนือจากอันดับ 1 และความคิดเห็นถูกใส่ผิด (ยังคงดิ้นรนที่จะเชื่อสิ่งนี้แม้ว่า)

หรือ

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

เพื่อตอบคำถามของคุณ

สามารถเลือกหมายเลขอื่นที่จะใช้งานได้เช่นเดียวกับตัวเลขสองตัวนี้หรือไม่? ทำไมหรือทำไมไม่?

ใช่สามารถใช้ตัวเลขใดก็ได้ในความเป็นจริงหากคุณระบุค่าเมล็ดพันธุ์เมื่อคุณสร้างอินสแตนซ์แบบสุ่มคุณกำลังใช้ค่าอื่น ๆ ค่านี้ไม่มีผลกระทบใด ๆ ต่อประสิทธิภาพของเครื่องกำเนิดไฟฟ้าซึ่งกำหนดโดยค่าของ a, c และ m ซึ่งกำหนดไว้ในคลาส


1
ไม่จริง - มีสองอัลกอริทึม: (i) 1 เพื่อสร้างเมล็ดพันธุ์แบบสุ่มใหม่ทุกครั้งที่มีการเรียกตัวสร้าง algo นั้นใช้ X_n + 1 = X_n * a อย่างง่าย เนื่องจากการล้นเป็นเวลานานสิ่งนี้จึงเทียบเท่ากับ X_n + 1 = X_n * a mod m ด้วย a = 181783497276652981 และ m = 2 ^ 64 (ii) อัลโกอีกตัวหนึ่งซึ่งเริ่มต้นจากเมล็ดพันธุ์ที่กำหนดจะสร้างชุดตัวเลขสุ่ม อัลโกที่สองคือสิ่งที่คุณพูดถึงและเอกสารอธิบายว่า " นี่คือตัวสร้างตัวเลขหลอกเชิงเส้นที่สอดคล้องกันตามที่ Knuth อธิบายไว้ใน The Art of Computer Programming "
assylias

1
@assylias ฉันเห็นประเด็นของคุณแล้วจมอยู่ในซอร์สโค้ดของRandomและกระดาษที่อ้างถึงฉันมีคำถามมากเกินไปจะแก้ไขในไม่ช้าขอบคุณ
Java Devil

3

ตามลิงค์ที่คุณให้มาพวกเขาได้เลือก ( หลังจากเพิ่ม 1 ที่หายไป :) ) ผลตอบแทนที่ดีที่สุดจาก 2 ^ 64 เนื่องจากยาวไม่สามารถมีตัวเลขจาก 2 ^ 128 ได้

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