การสร้างตัวเลขสุ่มใน C ++ 11: การสร้างมันทำงานอย่างไร [ปิด]


102

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

ใครก็ได้โปรดอธิบาย

  • พวกเขาคืออะไร?
  • หมายความว่าอย่างไร
  • วิธีสร้าง?
  • พวกเขาทำงานอย่างไร?
  • ฯลฯ

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


6
การถามเกี่ยวกับ RNG โดยไม่ทราบว่าการแจกแจงคืออะไรเหมือนกับการถามเกี่ยวกับตัวแยกนิพจน์โดยไม่รู้ว่านิพจน์คืออะไร ... ไลบรารี RNG ใน C ++ 11 มุ่งเป้าไปที่ผู้ที่รู้สถิติบางอย่างและมีความต้องการมากกว่าการแจกแจงแบบแบนที่สร้างโดยrandคุณควรดูวิกิพีเดียอย่างรวดเร็วสำหรับแนวคิดพื้นฐานทางสถิติและ RNG มิฉะนั้นจะเป็นการยากที่จะอธิบายให้คุณทราบถึงเหตุผล<random>และการใช้งานชิ้นส่วนต่างๆ
Matteo Italia

26
@Matteo: แทบจะไม่ เด็กสามารถเข้าใจแนวคิดที่ว่าแม่พิมพ์สร้างตัวเลขสุ่มโดยไม่เข้าใจว่าการแจกแจงคืออะไร
Benjamin Lindley

3
@ เบ็นจามิน: และนั่นคือจุดที่ความเข้าใจของเขาหยุดลงซึ่งเป็นเพียงก้าวแรก (เครื่องยนต์) และโดยไม่เข้าใจว่าเหตุใดจึงสำคัญที่พวกเขาจะสร้างการกระจายแบบแบน ส่วนที่เหลือทั้งหมดของห้องสมุดยังคงเป็นปริศนาโดยไม่เข้าใจการแจกแจงและแนวคิดสถิติอื่น ๆ
Matteo Italia

คำตอบ:


142

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

ทำไม "มีโอกาสเท่ากัน"

สมมติว่าคุณมีตัวสร้างตัวเลขสุ่มอย่างง่ายที่สร้างตัวเลข 0, 1, ... , 10 แต่ละตัวที่มีความน่าจะเป็นเท่ากัน (คิดว่านี่คือคลาสสิกrand()) ตอนนี้คุณต้องการตัวเลขสุ่มในช่วง 0, 1, 2 ซึ่งแต่ละตัวมีความน่าจะเป็นเท่ากัน ปฏิกิริยากระตุกเข่าของคุณจะเกิดrand() % 3ขึ้น แต่เดี๋ยวก่อนส่วนที่เหลือ 0 และ 1 เกิดขึ้นบ่อยกว่าส่วนที่เหลือ 2 ดังนั้นจึงไม่ถูกต้อง!

นี่คือเหตุผลที่เราต้องการการแจกแจงที่เหมาะสมซึ่งใช้แหล่งที่มาของจำนวนเต็มสุ่มที่สม่ำเสมอและเปลี่ยนเป็นการแจกแจงที่เราต้องการUniform[0,2]ดังตัวอย่าง ดีที่สุดที่จะฝากไว้ในห้องสมุดดีๆ!

เครื่องยนต์

ดังนั้นหัวใจสำคัญของการสุ่มทั้งหมดคือเครื่องกำเนิดตัวเลขสุ่มหลอกที่ดีที่สร้างลำดับของตัวเลขที่กระจายอย่างสม่ำเสมอในช่วงเวลาหนึ่งและซึ่งควรมีช่วงเวลาที่ยาวนานมาก การใช้งานมาตรฐานrand()มักไม่ใช่สิ่งที่ดีที่สุดดังนั้นจึงเป็นการดีที่จะมีทางเลือก Linear-Congruential และ Mersenne twister เป็นสองทางเลือกที่ดี (LG มักใช้ด้วยrand()เช่นกัน); อีกครั้งเป็นเรื่องดีที่จะให้ห้องสมุดจัดการเรื่องนั้น

มันทำงานอย่างไร

ง่าย: ขั้นแรกติดตั้งเครื่องยนต์และเพาะเมล็ด เมล็ดพันธุ์จะกำหนดลำดับทั้งหมดของตัวเลข "สุ่ม" ดังนั้น a) ใช้หมายเลขอื่น (เช่นนำมาจาก/dev/urandom) ในแต่ละครั้งและ b) เก็บเมล็ดพันธุ์ไว้หากคุณต้องการสร้างลำดับของตัวเลือกแบบสุ่มขึ้นมาใหม่

#include <random>

typedef std::mt19937 MyRNG;  // the Mersenne Twister with a popular choice of parameters
uint32_t seed_val;           // populate somehow

MyRNG rng;                   // e.g. keep one global instance (per thread)

void initialize()
{
  rng.seed(seed_val);
}

ตอนนี้เราสามารถสร้างการกระจาย:

std::uniform_int_distribution<uint32_t> uint_dist;         // by default range [0, MAX]
std::uniform_int_distribution<uint32_t> uint_dist10(0,10); // range [0,10]
std::normal_distribution<double> normal_dist(mean, stddeviation);  // N(mean, stddeviation)

... และใช้เครื่องยนต์เพื่อสร้างตัวเลขสุ่ม!

while (true)
{
  std::cout << uint_dist(rng) << " "
            << uint_dist10(rng) << " "
            << normal_dist(rng) << std::endl;

}

ภาวะพร้อมกัน

เหตุผลที่สำคัญอีกประการหนึ่งที่ชอบ<random>มากกว่าแบบดั้งเดิมrand()คือตอนนี้มีความชัดเจนและชัดเจนว่าจะสร้างเธรดการสร้างตัวเลขแบบสุ่มได้อย่างไร: ให้แต่ละเธรดมีเอ็นจิ้นเธรดโลคัลของตัวเองติดตั้งบนเธรดโลคัลหรือซิงโครไนซ์การเข้าถึง ไปยังวัตถุเครื่องยนต์

อื่น ๆ

  • บทความที่น่าสนใจใน TR1 สุ่ม CodeGuru
  • Wikipediaมีบทสรุปที่ดี (ขอบคุณ @Justin)
  • โดยหลักการแล้วเครื่องยนต์แต่ละตัวควรพิมพ์คำว่า a result_typeซึ่งเป็นประเภทอินทิกรัลที่ถูกต้องเพื่อใช้สำหรับเมล็ดพันธุ์ ฉันคิดว่าฉันมีการใช้งานบั๊กกี้ครั้งหนึ่งซึ่งบังคับให้ฉันบังคับให้เมล็ดพันธุ์std::mt19937ไปuint32_tที่ x64 ในที่สุดสิ่งนี้ควรได้รับการแก้ไขและคุณสามารถพูดได้MyRNG::result_type seed_valและทำให้เครื่องยนต์เปลี่ยนได้ง่ายมาก

เป็นอีกครั้งที่ Kerrek เอาชนะฉันด้วยคำตอบที่ดีกว่าที่ฉันกำลังทำอยู่ +1
Justin ᚅᚔᚈᚄᚒᚔ

@ จัสติน: ฉันแน่ใจว่าฉันพลาดสิ่งต่างๆมากมายอย่าลังเลที่จะเพิ่มประเด็นเพิ่มเติมในหัวข้อนี้! :-)
Kerrek SB

13
สำหรับส่วน "เติมเต็ม" ฉันคิดว่าstd::random_deviceควรค่าแก่การกล่าวถึงมากกว่า/dev/urandom
Cubbi

2
ตัวอย่างของการstd::random_deviceที่สามารถพบได้ที่นี่
WKS

1
รหัสในบทความ Wikipedia เป็นรถ สุ่มและ random2 เหมือนกัน จากความคิดเห็นในข้อมูลโค้ดเป็นที่ชัดเจนว่าผู้เขียนไม่เข้าใจวิธีใช้คุณสมบัติใน <random>
user515430

3

ตัวสร้างตัวเลขสุ่มคือสมการที่กำหนดตัวเลขจะให้หมายเลขใหม่แก่คุณ โดยปกติคุณจะระบุหมายเลขแรกหรือหมายเลขที่ดึงมาจากเวลาของระบบ

ทุกครั้งที่คุณขอหมายเลขใหม่จะใช้หมายเลขก่อนหน้าเพื่อดำเนินการสมการ

ตัวสร้างตัวเลขสุ่มไม่ถือว่าดีมากหากมีแนวโน้มที่จะสร้างตัวเลขเดียวกันบ่อยกว่าตัวเลขอื่น ๆ เช่นถ้าคุณต้องการตัวเลขสุ่มระหว่างหนึ่งถึง 5 และคุณมีการแจกแจงตัวเลขนี้:

  • 1: 1%
  • 2: 80%
  • 3: 5%
  • 4: 5%
  • 5: 9%

2 ถูกสร้างขึ้น FAR บ่อยกว่าหมายเลขอื่น ๆ ดังนั้นจึงมีแนวโน้มที่จะผลิตได้มากกว่าตัวเลขอื่น ๆ หากตัวเลขทั้งหมดเท่ากันคุณจะมีโอกาส 20% ในการได้รับแต่ละหมายเลขทุกครั้ง กล่าวอีกนัยหนึ่งการแจกแจงข้างต้นไม่สม่ำเสมอมากเพราะเป็นที่ชื่นชอบ 2 การกระจายที่มีทั้งหมด 20% จะเท่ากัน

โดยปกติแล้วหากคุณต้องการตัวเลขสุ่มจริงคุณจะดึงข้อมูลจากบางอย่างเช่นสภาพอากาศหรือแหล่งธรรมชาติอื่น ๆ แทนที่จะเป็นตัวสร้างตัวเลขสุ่ม


8
เครื่องกำเนิดตัวเลขสุ่มส่วนใหญ่สร้างการแจกแจงได้ดี มันไม่ได้เป็นแบบสุ่ม ปัญหาคือพวกเขาคำนวณและทำให้คุณสามารถเดาหมายเลขถัดไปที่มีจำนวนเพียงพอในลำดับ (ข้อเท็จจริงนี้ทำให้พวกเขาไม่ดีต่อความปลอดภัยซึ่งจำเป็นต้องใช้ตัวเลขสุ่มอย่างแท้จริง) สำหรับเกมและสิ่งต่างๆคุณควรจะสบายดี
Martin York

5
ฉันค่อนข้างแน่ใจว่า OP กำลังขอข้อมูลเฉพาะเกี่ยวกับสิ่งอำนวยความสะดวกที่ให้ไว้ในส่วนหัว C ++ <random> คำตอบนี้ไม่ได้ระบุถึงการเขียนโปรแกรมด้วยซ้ำ C ++
Benjamin Lindley

1
@Martin: ความปลอดภัยไม่จำเป็นต้องมีแหล่งที่มาของตัวเลขสุ่มอย่างแท้จริง AES ในโหมดตัวนับ (ตัวอย่างหนึ่ง) สามารถทำได้ค่อนข้างดีแม้ว่าจะถูกกำหนด ต้องใช้เอนโทรปีในจำนวนที่เหมาะสม แต่ไม่ใช่การสุ่มที่แท้จริง
Jerry Coffin

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