โครงสร้างข้อมูลสำหรับลูกเต๋าที่โหลดหรือไม่


130

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

ขณะนี้ฉันมีทางออก O (lg n) สำหรับปัญหานี้ ความคิดคือการเก็บตารางของความน่าจะเป็นสะสมของ k แรกสำหรับทุก k พวกเขาเพื่อสร้างจำนวนจริงแบบสุ่มในช่วง [0, 1) และทำการค้นหาแบบไบนารีเหนือตารางเพื่อให้ได้ดัชนีที่ใหญ่ที่สุดที่มีการสะสม ค่าไม่มากกว่าค่าที่เลือก ฉันชอบโซลูชันนี้ แต่ดูเหมือนแปลกที่รันไทม์ไม่ได้คำนึงถึงความน่าจะเป็น โดยเฉพาะอย่างยิ่งในกรณีที่สุดขั้วของด้านใดด้านหนึ่งขึ้นมาเสมอหรือค่าที่กระจายอย่างสม่ำเสมอเป็นไปได้ที่จะสร้างผลลัพธ์ของการหมุนใน O (1) โดยใช้วิธีการไร้เดียงสาแม้ว่าวิธีแก้ปัญหาของฉันจะยังคง

ใครบ้างมีคำแนะนำสำหรับวิธีการแก้ปัญหานี้ในลักษณะที่ "ปรับตัว" อย่างใดในมัน runtime?

แก้ไข : ตามคำตอบของคำถามนี้ฉันได้เขียนบทความที่อธิบายถึงวิธีการมากมายสำหรับปัญหานี้พร้อมกับการวิเคราะห์ของพวกเขา ดูเหมือนว่าการติดตั้งวิธีนามแฝงของ Vose จะให้เวลา process (n) เวลาในการประมวลผลล่วงหน้าและเวลา O (1) ต่อการหมุนตายซึ่งเป็นที่น่าประทับใจอย่างแท้จริง หวังว่านี่จะเป็นประโยชน์ต่อข้อมูลที่มีอยู่ในคำตอบ!


2
ก็สมควรแล้วว่ามีวิธีการแก้ปัญหา O (1) สำหรับแต่ละกรณีเฉพาะ
ทิม

คำตอบ:


117

คุณกำลังมองหาวิธีนามแฝงซึ่งให้วิธีการO (1)สำหรับการสร้างการกระจายความน่าจะเป็นแบบคงที่แบบคงที่ (สมมติว่าคุณสามารถเข้าถึงรายการในอาร์เรย์ที่มีความยาว n ในเวลาคงที่) ด้วยการตั้งค่า O (ครั้งเดียว) . คุณสามารถดูได้ในเอกสารในบทที่ 3 (PDF)ของ"การสร้างชุดรูปแบบสุ่มที่ไม่สม่ำเสมอ"โดย Luc Devroye

ความคิดที่จะใช้อาร์เรย์ของคุณน่าจะเป็นพีkและผลิตใหม่สามอาร์เรย์ n องค์ประกอบ Q kเป็นkและขk แต่ละ q kคือความน่าจะเป็นระหว่าง 0 ถึง 1 และแต่ละ a kและ b kเป็นจำนวนเต็มระหว่าง 1 ถึง n

เราสร้างตัวเลขสุ่มระหว่าง 1 ถึง n โดยสร้างตัวเลขสุ่มสองตัวคือ r และ s ระหว่าง 0 และ 1 ให้ i = floor (r * N) +1 ถ้าถามฉัน <s แล้วกลับฉันอื่นกลับมาขฉัน การทำงานในวิธีนามแฝงอยู่ในการหาวิธีการผลิต Q kเป็นkและขk


สำหรับอัลกอริทึมที่มีประโยชน์เช่นนั้น Alias ​​Method นั้นไม่เป็นที่รู้จักอย่างน่าประหลาดใจนัก
mhum

สำหรับบันทึก: ผมตีพิมพ์ C ห้องสมุดเล็ก ๆ น้อย ๆ สำหรับการสุ่มตัวอย่างโดยใช้วิธีนามแฝงapps.jcns.fz-juelich.de/ransampl
Joachim W

1
การดำเนินการตามวิธีเฉพาะของนามแฝงอาจช้ากว่าวิธีที่ซับซ้อนกว่าเวลาเช่นรูเล็ตวีล สำหรับจำนวนที่กำหนดnและสุ่มเลือกตัวเลขเพื่อสร้างเนื่องจากปัจจัยคงที่เกี่ยวข้องกับการใช้อัลกอริธึม
jfs

4

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

function get_result(node, seed):
    if seed < node.interval.start:
        return get_result(node.left_child, seed)
    else if seed < node.interval.end:
        // start <= seed < end
        return node.result
    else:
        return get_result(node.right_child, seed)

สิ่งที่ดีเกี่ยวกับการแก้ปัญหานี้คือมันง่ายมากที่จะใช้ แต่ก็ยังมีความซับซ้อนที่ดี


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

คุณสามารถรับประกันได้ว่ามันจะมีความสมดุลถ้าคุณสร้างมันในลำดับที่ถูกต้อง
hugomg

3

ฉันกำลังนึกถึงตารางของคุณ

แทนที่จะมีตารางที่มีค่าสะสมสำหรับแต่ละค่าตายตัวคุณสามารถสร้างอาร์เรย์จำนวนเต็มของความยาว xN โดยที่ x เป็นจำนวนที่ดีเลิศเพื่อเพิ่มความแม่นยำของความน่าจะเป็น

เติมอาเรย์นี้โดยใช้ดัชนี (ทำให้เป็นมาตรฐานโดย xN) เป็นค่าสะสมและในแต่ละ 'สล็อต' ในอาเรย์ให้จัดเก็บลูกเต๋ากลิ้งหากดัชนีนี้เกิดขึ้น

บางทีฉันอาจอธิบายได้ง่ายขึ้นด้วยตัวอย่าง:

ใช้สามลูกเต๋า: P (1) = 0.2, P (2) = 0.5, P (3) = 0.3

สร้างอาร์เรย์ในกรณีนี้ฉันจะเลือกความยาวง่ายพูด 10 (นั่นคือ x = 3.33333)

arr[0] = 1,
arr[1] = 1,
arr[2] = 2,
arr[3] = 2,
arr[4] = 2,
arr[5] = 2,
arr[6] = 2,
arr[7] = 3,
arr[8] = 3,
arr[9] = 3

จากนั้นเพื่อให้ได้ความน่าจะเป็นเพียงแค่สุ่มตัวเลขระหว่าง 0 ถึง 10 แล้วเข้าถึงดัชนีนั้น

วิธีนี้อาจสูญเสียความแม่นยำ แต่การเพิ่ม x และความแม่นยำจะเพียงพอ


1
เพื่อความแม่นยำที่สมบูรณ์คุณสามารถทำการค้นหาอาร์เรย์เป็นขั้นตอนแรกและสำหรับช่วงเวลาที่สอดคล้องกับหลาย ๆ ด้านทำการค้นหาที่นั่น
aaz

1

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

หนึ่งในวิธีที่ง่ายที่สุดในการเลือกจำนวนเต็มที่มีฟังก์ชันกำหนดน้ำหนักเองf(x)คือวิธีการสุ่มตัวอย่างการปฏิเสธ ต่อไปนี้สมมติว่าค่าสูงสุดที่เป็นไปได้คือf maxความซับซ้อนของเวลาสำหรับการสุ่มตัวอย่างการปฏิเสธนั้นมีค่าคงที่โดยเฉลี่ย แต่ขึ้นอยู่กับรูปร่างของการกระจายและมีกรณีที่แย่ที่สุดในการทำงานตลอดไป วิธีเลือกจำนวนเต็มใน [1, k] โดยใช้การสุ่มตัวอย่างการปฏิเสธ:

  1. เลือกจำนวนเต็มแบบสุ่มiใน [1, k]
  2. ด้วยความน่าจะเป็นผลตอบแทนf(i)/max iมิฉะนั้นไปที่ขั้นตอนที่ 1

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

  • โครงสร้างข้อมูลรวบรัดของ Bringmann – Larsen ("การสุ่มตัวอย่างแบบย่อจากการกระจายแบบไม่ต่อเนื่อง", 2012),
  • การค้นหาหลายระดับของ Yunpeng Tang ("การศึกษาเชิงประจักษ์ของวิธีการสุ่มตัวอย่างแบบสุ่มสำหรับการเปลี่ยนการกระจายแบบไม่ต่อเนื่อง", 2019) และ
  • โหลดด่วน Dice Roller (2020)

อัลกอริทึมอื่น ๆ รวมถึงวิธีนามแฝง (ได้กล่าวถึงแล้วในบทความของคุณ) อัลกอริทึม Knuth – Yao โครงสร้างข้อมูล MVN และอื่น ๆ ดูส่วนของฉัน " หมายเหตุเกี่ยวกับอัลกอริธึมตัวเลือกถ่วงน้ำหนัก " สำหรับการสำรวจ

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