เหตุใด C ++ แรนด์ () จึงดูเหมือนว่าจะสร้างเฉพาะลำดับความสำคัญเท่ากันเท่านั้น


146

ในแอปพลิเคชันขนาดเล็กที่เขียนด้วย C / C ++ ฉันกำลังประสบปัญหากับrandฟังก์ชั่นและอาจเป็นเมล็ด:

ฉันต้องการสร้างลำดับของตัวเลขสุ่มที่มีคำสั่งต่างกันเช่นมีค่าลอการิทึมที่แตกต่างกัน (ฐาน 2) แต่ดูเหมือนว่าตัวเลขทั้งหมดที่ผลิตมีคำสั่งเดียวกันโดยมีความผันผวนระหว่าง 2 ^ 25 และ 2 ^ 30

เป็นเพราะrand()เมล็ดมีเวลา Unix ซึ่งตอนนี้เป็นจำนวนค่อนข้างมาก? ฉันลืมอะไร ฉันกำลังเพาะเพียงครั้งเดียวที่จุดเริ่มต้นของrand()main()


7
FWIW เป็น C หรือ C ++ หรือไม่ ถ้าโดย C / C ++ คุณหมายถึงคุณสามารถใช้ C ++ ได้จริงและการกล่าวถึง C นั้นเป็นเพียงการสุ่มบางทีen.cppreference.com/w/cpp/numeric/random/binomial_distributionนี้สามารถช่วยได้
R. Martinho Fernandes

9
น่าเสียดายที่คุณเดิมพันกับม้าผิด เมล็ดไม่ควรเป็นปัญหาของคุณ ปัญหาของคุณเกิดจากการแจกจ่ายที่ไม่ถูกต้อง เนื่องจากผู้เขียนโปรแกรมที่เป็นกลางควรคาดหวังว่าrand()จะส่งคืนหมายเลขที่กระจายอย่างสม่ำเสมอ (เอกสารที่มีอันดับสูงของ Google กล่าวอย่างชัดเจน) ฉันไม่คิดว่าคำถามนี้มีประโยชน์สำหรับผู้อ่านในอนาคต นั่นเป็นเหตุผลว่าทำไมการลงคะแนน แต่อย่าให้มันทำให้คุณหมดกำลังใจในการใช้งาน SO
Emperor Orionii

12
@ doug65536 "... ที่ไม่มีการซ้ำหมายเลข" - นั่นไม่ใช่การสุ่ม! ฉันสามารถให้เงินทุนแก่การเกษียณอายุของฉันได้ที่โต๊ะลูกเต๋าหากลูกเต๋าของฉัน () ไม่เคยส่งกลับหมายเลขเดิมซ้ำสองครั้งจนกระทั่งทุกหมายเลขที่เป็นไปได้ถูกส่งคืน
Chris Gregg

6
@GalacticCowboy อย่าเข้าใจผิดว่ามีตัวเลขซ้ำ ๆ จากบทความ Wikipedia ที่คุณอ้างถึง: "ผลลัพธ์ซ้ำ ๆ ไม่ได้หมายความว่าถึงจุดสิ้นสุดของช่วงเวลาดังกล่าวเนื่องจากสถานะภายในอาจมีขนาดใหญ่กว่าผลลัพธ์" มันจะแย่มาก ๆ ถ้า PRNG สร้างมูลค่าแล้วรับประกันว่าจะไม่สร้างมูลค่านั้นอีกจนกว่าค่าทั้งหมดจะถูกส่งกลับ
Chris Gregg

12
Doug65536 ไม่มีใครรับเรื่องชกต่อย พวกเขาเพียงแค่ระบุอย่างถูกต้องว่าคุณผิด PRNG สามารถปั่นป่วนต่อไปนี้อย่างมีความสุขถ้าฉันต้องการแรนด์ระหว่าง 1 ถึง 10: 2 4 7 2 8 1 5 9 7 3 นั่นจะใช้ได้ทั้งหมดแม้จะเป็น 2 และ 7 หลาย ฉันคิดว่าคุณกำลังทำให้ PRNG สับสนกับสิ่งอำนวยความสะดวกแบบสุ่มบน iPhone ของคุณ
พักผ่อนในไซปรัส

คำตอบ:


479

มีเพียง 3% ของตัวเลขระหว่าง 1 และ 2 มี30ซึ่งไม่ได้ระหว่าง 2 25 2 30 ดังนั้นนี่ฟังดูธรรมดาดี :)

เพราะ 2 25 /2 30 = 2 -5 = 1/32 = 0.03125 = 3.125%


36
ใช่จุดดี! มีตัวเลขมากกว่า 31 คูณระหว่าง 2 ^ 25 และ 2 ^ 30 มากกว่าระหว่าง 1 และ 2 ^ 25 :) ขอบคุณสำหรับคำตอบอย่างรวดเร็ว ฉันต้องคิดใหม่โปรแกรม ตอบคำถามแล้ว
Tallaron Mathias

1
@TallaronMathias พิจารณาการตัดทอนตัวเลขด้วยการ>>เลื่อนบิต - ซึ่งจะทำให้คุณมีจำนวนน้อยลง (หรือรับโมดูลัสด้วย%)
Sean Allred

13
ฉันคาดหวังว่าสิ่งนี้จะเห็นได้ชัดกับโปรแกรมเมอร์ส่วนใหญ่: จำนวนเต็มใด ๆ ที่ไม่ได้ลงชื่อน้อยกว่า 2 ^ 25 จะต้องมี 7 บิตแรกเท่ากับ0- และถ้าทุกบิตเป็นแบบสุ่ม ...
BlueRaja - Danny Pflughoeft

118
@ BlueRaja-DannyPflughoeft - หากมีความเป็นไปได้ที่ชัดเจนคาสิโนก็จะเลิกกิจการ
Brett Hale

26
@BrettHale - ฉันไม่คิดว่าโปรแกรมเมอร์เป็นกลุ่มเป้าหมายของคาสิโน
EkoostikMartin

272

สีเขียวเบาเป็นพื้นที่ระหว่าง 0 และ 2 25 ; สีเขียวเข้มเป็นพื้นที่ระหว่าง 2 25 2 30 เห็บคือพลังของ 2

การกระจาย


42

คุณจะต้องแม่นยำมากขึ้น: คุณต้องการค่าลอการิทึมฐาน 2 ที่แตกต่างกัน แต่คุณต้องการการกระจายแบบใด ฟังก์ชัน rand () มาตรฐานสร้างการกระจายแบบสม่ำเสมอคุณจะต้องแปลงเอาต์พุตนี้โดยใช้ควอไทล์ฟังก์ชันเกี่ยวข้องกับการแจกแจงที่คุณต้องการ

ถ้าคุณบอกการกระจายเราก็สามารถบอกquantileฟังก์ชั่นที่คุณต้องการ


13
+1 การแจกแจงเป็นคำที่สำคัญ มันไม่มีเหตุผลที่จะพูดถึงตัวเลขสุ่มเมื่อไม่มีอะไรรู้เกี่ยวกับการแจกแจง เครื่องแบบเป็นเพียงกรณีพิเศษแม้ว่าจะเป็นชุดที่สำคัญ อาจเป็นสถานที่ที่ดีที่จะชี้ให้เห็นการกระจายต่าง ๆ จากไลบรารีมาตรฐาน C ++ 11
leftaroundabout

18

ถ้าคุณต้องการออเดอร์ที่มีขนาดต่างกันทำไมไม่ลองกันpow(2, rand())ล่ะ หรืออาจเลือกคำสั่งโดยตรงเป็นแรนด์ () ตามที่แฮโรลด์แนะนำ?


3
ความคิดที่ดี แต่คุณควรแก้ไขคำตอบของคุณโดยใช้ pow แทน ^ (ซึ่งเป็นตัวดำเนินการ xor แบบลอจิคัลไม่ใช่กำลังในภาษา C)
kriss

6
เนื่องจากrand()สามารถขึ้นไปได้RAND_MAXคุณจำเป็นต้องปรับจำนวนสุ่มของคุณเพื่อให้ได้ผลลัพธ์ที่ไม่ล้น ...
Floris

@ Floris: แต่ถ้าคุณขยายช่วงเล็ก ๆ ที่นับได้ในช่วงที่มีขนาดใหญ่มากคุณจะมีรูจำนวนมากซึ่งอาจไม่ใช่สิ่งที่ OP คาดหวัง
André Caron

13

@ C4stor ทำให้เป็นจุดที่ดี แต่สำหรับกรณีทั่วไปที่มากขึ้นและเข้าใจได้ง่ายขึ้นสำหรับมนุษย์ (ฐาน 10): สำหรับช่วงตั้งแต่ 1 ถึง 10 ^ n, ~ 90% ของตัวเลขอยู่ระหว่าง 10 ^ (n-1) ถึง 10 ^ n ดังนั้น ~ 99% ของตัวเลขเริ่มจาก 10 ^ (n-2) ถึง 10 ^ n เพิ่มทศนิยมให้มากที่สุดเท่าที่คุณต้องการ

คณิตศาสตร์ตลกถ้าคุณทำเช่นนี้ต่อไปสำหรับ n คุณจะเห็นว่าตั้งแต่ 1 ถึง 10 ^ n 99.9999 ... % = 100%ของตัวเลขมาจาก 10 ^ 0 ถึง 10 ^ n ด้วยวิธีนี้

ตอนนี้เกี่ยวกับรหัสถ้าคุณต้องการตัวเลขสุ่มที่มีขนาดสุ่มจาก 0 ถึง 10 ^ n คุณสามารถทำได้:

  1. สร้างตัวเลขสุ่มเล็ก ๆ จาก 0 ถึง n

  2. หากคุณทราบช่วงที่ n มีให้สร้างลำดับสุ่มจำนวนมาก 10 ^ k โดยที่ k> max {n}

  3. ตัดตัวเลขสุ่มที่ยาวขึ้นเพื่อรับตัวเลข n ของตัวเลขสุ่มขนาดใหญ่นี้


46
คุณถูกต้องครบถ้วน แต่สำหรับคำตอบที่เข้าใจง่ายจริงๆ OP ควรถามตัวเองว่าทำไม 90% ของตัวเลขสุ่มระหว่าง 1 ถึง 100 จึงเป็นตัวเลขสองหลัก
ถามเกี่ยวกับโมนิก้า

13

คำตอบ (และถูกต้อง) ขั้นพื้นฐานได้รับและยอมรับแล้วข้างต้น: มี 10 ตัวเลขระหว่าง 0 และ 9, 90 หมายเลขระหว่าง 10 และ 99, 900 ระหว่าง 100 และ 999 เป็นต้น

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

s = rand() & 31; // a random number between 0 and 31 inclusive, assuming RAND_MAX = 2^32-1
r = rand() >> s; // right shift

มันไม่สมบูรณ์แบบ แต่มันเร็วกว่าการคำนวณมาก pow(2, rand()*scalefactor)มาก มันจะเป็น "ก้อน" ในแง่ที่ว่าการแจกแจงจะเหมือนกันสำหรับตัวเลขภายในตัวประกอบ 2 (เครื่องแบบ 128 ถึง 255 ครึ่งความหนาแน่นสำหรับ 256 ถึง 1,023 ฯลฯ )

นี่คือฮิสโตแกรมของความถี่ของตัวเลข 0 ถึง 31 (ในตัวอย่าง 1M):

ป้อนคำอธิบายรูปภาพที่นี่


nitpick: สิ่งนี้ส่งเสริมจำนวนน้อยมากที่มากกว่าหนึ่งอาจคาดหวัง ความน่าจะเป็นที่จะได้ศูนย์เป็นสูงกว่าอย่างมีนัยสำคัญ 10
Mooing Duck

ดี - ประเด็นทั้งหมดนี้คือการสนับสนุนคนจำนวนน้อยดังนั้นฉันดีใจที่มันใช้งานได้! ฉันใช้การจำลองแบบมอนติคาร์โลและสิ่งนี้ทำให้ฉันมีความน่าจะเป็นลดลงเป็น 2 เนื่องจากตัวเลขสองเท่า - ไม่ต่างจากการกระจายบันทึก อัปเดตคำตอบพร้อมรูปภาพ
Floris

ไม่ฉันหมายถึงด้วยrand()>>(rand()&31);ใครจะคาดหวัง 1 / 32nd ของตัวเลขที่มี 32 บิตและ 1 / 32nd ของตัวเลขมี 31 บิตและ 1/32 ของตัวเลขมี 30 บิต ฯลฯ แต่นั่นคือไม่ใช่ผลลัพธ์ที่คุณได้รับเพียงประมาณ 1 / 64th ของตัวเลขจะส่งผลให้ 32 บิตในขณะที่เกือบครึ่งควรเป็น 0 เนื่องจากคณิตศาสตร์จิตของฉันไม่เห็นด้วยกับการวัดของคุณฉันจะต้องทำการวัดด้วยตัวเอง สิ่งนี้ออกมา
Mooing Duck

2
ฉันไม่ได้ตั้งใจจะบอกว่ารหัสของคุณผิด มันอาจเป็นสิ่งที่ฉันจะทำ มันก็สมควรได้รับการเตือนว่าผลที่ได้จะไม่ได้เป็นค่อนข้างกระจายเป็นหนึ่งอาจคาดหวัง
Mooing Duck

1
ฉันคิดว่าปัญหามาจากการคิด 0 เป็น 1 บิต ... นั่นเป็นปริศนาที่คุณพบเมื่อคุณผสมจำนวนเต็มและลอการิทึม มันเป็นการออกกำลังกายที่ดีและคุณให้บางสิ่งบางอย่างแก่ฉัน "ทดสอบขีด จำกัด ของอัลกอริทึมของคุณ" - มันไม่มีวันเก่า
Floris

5

มีจำนวนตัวเลขที่เท่ากับระหว่าง 0 ถึง 2 ^ 29 และ 2 ^ 29 และ 2 ^ 30

อีกวิธีหนึ่งในการดูปัญหา: พิจารณาการแทนเลขฐานสองของจำนวนสุ่มที่คุณสร้างความน่าจะเป็นที่บิตสูงสุดคือ 1 เท่ากับ 1/2 ดังนั้นคุณจะได้รับคำสั่ง 29 ในครึ่งกรณี สิ่งที่คุณต้องการคือการดูตัวเลขที่ต่ำกว่า 2 ^ 25 แต่นั่นหมายความว่า 5 บิตสูงสุดเป็นศูนย์ทั้งหมดซึ่งเกิดขึ้นกับความน่าจะเป็นต่ำที่ 1/32 โอกาสที่จะเกิดขึ้นแม้ว่าคุณจะเรียกใช้เป็นเวลานานคุณจะไม่เห็นคำสั่งซื้อที่ต่ำกว่า 15 เลย (ความน่าจะเป็นคือการกลิ้ง 6 6 ครั้งติดต่อกัน)

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



2

หากคุณต้องการใช้ตัวเลขสุ่มจากบริการออนไลน์ที่คุณสามารถใช้ wget ได้คุณอาจต้องการเห็นว่าคุณสามารถใช้บริการเช่น random.org สำหรับการสร้างหมายเลขสุ่มของคุณคุณสามารถจับพวกเขาโดยใช้ wget แล้วอ่านตัวเลขจาก ไฟล์ที่ดาวน์โหลด

wget -q https://www.random.org/integers/?num=100&min=1&max=100&col=5&base=10&format=html&rnd=new -O new.txt

http://programmingconsole.blogspot.in/2013/11/a-better-and-different-way-to-generate.html


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