ฉันจะหลีกเลี่ยง "เกินไป" โชคดี / โชคไม่ดีในการสร้างเลขสุ่มได้อย่างไร


30

ขณะนี้ฉันกำลังจัดการกับระบบการต่อสู้แบบผู้เล่นหลายคนซึ่งความเสียหายที่ผู้เล่นทำนั้นจะถูกคูณด้วยปัจจัยสุ่มระหว่าง 0.8 และ 1.2

ในทางทฤษฎีการสุ่ม RNG อย่างแท้จริงในที่สุดอาจให้ผลจำนวนเดียวกันหลายครั้ง (ดูTetris ขึ้นเขียง ) ซึ่งอาจส่งผลในการแข่งขันที่ผู้เล่นจะได้เสมอทำให้เกิดความเสียหายสูงมากในขณะที่คนอื่น ๆ มักจะทำให้เกิดความเสียหายที่ต่ำมาก

ฉันจะทำอย่างไรเพื่อให้แน่ใจว่าสิ่งนี้จะไม่เกิดขึ้น RNG บางตัวดีกว่าตัวอื่นที่หลีกเลี่ยงการทำซ้ำหรือไม่?


ฉันไม่เห็นวิธีการทำงาน แน่นอนคุณจะได้รับลำดับของ x1, x2, x3, x4 .. ที่ x ทั้งหมดมีขนาดใหญ่ มันไม่ได้เป็นแบบสุ่มเลยเหรอ?
The Duck Communist

คำตอบ:


26

คุณสามารถแก้ไขได้เช่นเดียวกับที่ Tetris ทำโดยทำรายการผลเสียหายและการสับที่ตั้งไว้ล่วงหน้า

สมมติว่าคุณรู้ว่าผู้เล่นจะสร้างความเสียหาย 0.8x ถึง 1.2 เท่ากับการกระจายเชิงเส้น รับรายการ [0.8, 0.9, 1.0, 1.1, 1.2] สุ่มแบบสุ่มเพื่อให้คุณได้เช่น [1.2, 1.0, 0.8, 0.9, 1.1]

ครั้งแรกที่ผู้เล่นทำดาเมจได้พวกเขาจะทำความเสียหาย 1.2 เท่า จากนั้น 1x จากนั้นเป็นต้นไปถึง 1.1x เฉพาะเมื่ออาเรย์นั้นว่างเปล่าคุณควรสร้างและสับเปลี่ยนอาเรย์ใหม่

ในทางปฏิบัติคุณอาจต้องการทำเช่นนี้กับ 4 อาร์เรย์พร้อมกัน (เช่นเริ่มต้นด้วย [0.8,0.8,0.8,0.8,0.9,0.9,0.9,0.9,0.9,0.9,0.9, ... ]) ไม่เช่นนั้นระยะเวลาในการเรียงลำดับจะต่ำพอที่ผู้เล่นจะสามารถทราบได้ว่าการตีครั้งต่อไปของพวกเขานั้น "ดี" หรือไม่ (แม้ว่าจะสามารถเพิ่มกลยุทธ์ในการต่อสู้ได้เช่นเดียวกับในตาราง Hoimi ของ Dragon Quest IXซึ่งผู้คนต่างคิดหาวิธีตรวจสอบโดยดูตัวเลขการรักษาและปรับแต่งจนกว่าคุณจะรับประกันว่าจะหายาก)


3
หากต้องการทำให้การสุ่มเพิ่มขึ้นอีกนิดคุณสามารถมีรายการครึ่งหนึ่งเป็นตัวเลขสุ่มและอีกครึ่งหนึ่งคำนวณเป็น (2-x) เพื่อให้ได้ค่าเฉลี่ยที่ถูกต้อง
อดัม

2
@ อดัม: วิธีการนี้ใช้ได้ผลกับตัวอย่างนี้เท่านั้น หากคุณจัดการกับชิ้น Tetris แทนที่จะสร้างความเสียหายทวีคูณบล็อก 2 - S คืออะไร

6
คำปกติสำหรับการจัดเรียงนี้ของระบบคือ "สุ่มโดยไม่มีการแทนที่" มันคล้ายกับการใช้ไพ่แทนลูกเต๋าจริงๆ
Kylotan

ยิ่งไปกว่านั้นคุณสามารถทำตัวเลขครึ่งจริงๆสุ่มและเพียงครึ่งหนึ่งของพวกเขาอยู่ภายใต้กฎนี้
o0 '

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

5

ฉันเขียนโค้ดเพื่อทำสิ่งนี้จริงๆ ส่วนสำคัญของมันคือการใช้สถิติเพื่อแก้ไขลายโชคร้าย วิธีที่คุณสามารถทำได้คือติดตามว่ามีเหตุการณ์เกิดขึ้นกี่ครั้งและใช้เพื่อตั้งค่าหมายเลขที่สร้างโดย PRNG

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

ใช้ตัวอย่าง PRNG ต่อไปนี้ (โดยที่เรา proc ถ้าตัวอย่างคือ> = 0.5):

Values: 0.1, 0.5, 0.9, 0.4, 0.8
Events: 0  , 1  , 1  , 0  , 1
Percentage: 60%

ขอให้สังเกตว่าแต่ละค่าก่อให้เกิด 1/5 ของผลลัพธ์สุดท้าย ลองดูอีกวิธี:

Values: 0.1, 0.5
Events: 0  , 1

ขอให้สังเกตว่า0มีส่วนร่วมถึง 50% ของมูลค่าและ1มีส่วนช่วย 50% ของมูลค่า ถ่ายเพิ่มเติมเล็กน้อย:

Values: [0.1, 0.5], 0.9
Events: [0  , 1  ], 1

ตอนนี้ค่าแรกมีส่วนร่วม 66% ของมูลค่าและ 33% สุดท้าย โดยทั่วไปเราสามารถแยกส่วนนี้เป็นกระบวนการต่อไปนี้:

result = // 0 or 1 depending on the result of the event that was just generated
new_samples = samples + 1

average = (average * samples / new_samples) + (result * 1 / new_samples)
// Essentially:
average = (average * samples / new_samples) + (result / new_samples)

// You might want to limit this to, say, 100.
// Leaving it to carry on increasing can lead to unfairness
// if the game draws on forever.
samples = new_samples

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

average = 0.1
desired = 0.5
corrected_chance = 83%

average = 0.2
desired = 0.5
corrected_chance = 71%

average = 0.5
desired = 0.5
corrected_change = 50%

ตอนนี้สิ่งที่ 'เกิดขึ้นกับฉัน' ก็คือในตัวอย่างแรก 83% เป็นเพียง "0.5 จาก 0.6" (อีกนัยหนึ่งคือ "0.5 จาก 0.5 บวก 0.1") ในแง่ของเหตุการณ์แบบสุ่มซึ่งหมายถึง:

procced = (sample * 0.6) > 0.1
// or
procced = (sample * 0.6) <= 0.5

ดังนั้นเพื่อสร้างเหตุการณ์คุณโดยทั่วไปจะใช้รหัสต่อไปนี้:

total = average + desired
sample = rng_sample() * total // where the RNG provides a value between 0 and 1
procced = sample <= desired

และดังนั้นคุณจะได้รับรหัสที่ฉันใส่ในส่วนสำคัญ ฉันค่อนข้างมั่นใจว่าทั้งหมดนี้สามารถใช้ในสถานการณ์กรณีความเสียหายแบบสุ่ม แต่ฉันไม่ได้ใช้เวลาในการคิดออก

คำเตือน:นี่คือสถิติที่ปลูกในบ้านทั้งหมดฉันไม่มีการศึกษาในสาขานี้ การทดสอบหน่วยของฉันผ่านการทดสอบแล้ว


ดูเหมือนข้อผิดพลาดในตัวอย่างแรกของคุณเนื่องจากทั้งค่า 0.1 และ 0.9 ส่งผลให้เกิดเหตุการณ์ 0 แต่โดยทั่วไปคุณกำลังอธิบายถึงการรักษาค่าเฉลี่ยเคลื่อนที่สะสม ( en.wikipedia.org/wiki/Moving_average#Cumulative_moving_average ) และแก้ไขตามนั้น ความเสี่ยงอย่างหนึ่งคือว่าผลลัพธ์แต่ละรายการจะมีความสัมพันธ์แบบผกผันอย่างมีนัยสำคัญกับผลลัพธ์ก่อนหน้าแม้ว่าความสัมพันธ์นี้จะลดลงเมื่อเวลาผ่านไป
Kylotan

1
ฉันจะถูกล่อลวงให้เปลี่ยนแปลงสิ่งนี้เพื่อใช้ระบบ 'leaky integrator' แทน: เริ่มต้นด้วยค่าเริ่มต้นเฉลี่ยที่ 0.5 และแทนที่จะนับตัวอย่างเลือกค่าคงที่ตามอำเภอใจ (เช่น 10, 20, 50 หรือ 100) ซึ่งไม่ได้เพิ่มขึ้น . อย่างน้อยก็มีความสัมพันธ์ระหว่าง 2 ค่าที่ตามมาเป็นค่าคงที่ตลอดการใช้งานของเครื่องกำเนิด คุณสามารถปรับแต่งค่าคงที่ได้ - ค่าที่มากขึ้นหมายถึงการแก้ไขที่ช้าลงและการสุ่มที่ชัดเจนยิ่งขึ้น
Kylotan

@Kylotan ขอบคุณขอบคุณที่ให้ชื่อ ฉันไม่แน่ใจว่าคุณหมายถึงอะไรกับความคิดเห็นที่สองของคุณ - อาจให้คำตอบใหม่
Jonathan Dickinson

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

@jSepia - แน่นอนคุณยังคงได้รับความยุติธรรม / ความไม่เป็นธรรม แต่พวกเขาจะถูกติดตามโดยปกติ เช่นในการทดสอบหน่วยของฉันฉันถูกบังคับ 100 non-procs และได้พบกับ ~ 60 procs เมื่อฉันทำตัวอย่างจริง ภายใต้สถานการณ์ที่ไม่มีการอนุญาต (ถ้าคุณดูรหัส) 50% proc มักจะเห็นว่าแย่ที่สุดคือรัน 2/3 ในทิศทางใดทิศทางหนึ่ง แต่ผู้เล่นคนหนึ่งสามารถวิ่งได้ซึ่งจะช่วยให้พวกเขาเอาชนะผู้เล่นอื่นได้ ถ้าคุณต้องการอคติอย่างยิ่งต่องานที่เป็นธรรม: total = (average / 2) + desired.
Jonathan Dickinson

3

สิ่งที่คุณถามหาจริงๆแล้วตรงกันข้ามกับ PRNG ส่วนใหญ่ซึ่งเป็นการกระจายแบบไม่เป็นเชิงเส้น เพียงแค่ใส่การส่งคืนแบบลอจิกในกฎของคุณลดลงบางอย่างสมมติว่าทุกอย่างที่มากกว่า 1.0x เป็น "การวิพากษ์วิจารณ์ที่สำคัญ" ของบางชนิดเพียงแค่บอกว่าแต่ละรอบโอกาสที่จะได้ crit ขึ้นโดย X จนกว่าคุณจะได้ จุดที่พวกเขาตั้งค่าใหม่เป็น Y จากนั้นคุณทำสองม้วนในแต่ละรอบหนึ่งรอบเพื่อกำหนด crit หรือไม่และอีกครั้งสำหรับขนาดที่แท้จริง


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

3
จริง ๆ แล้วฉันรู้ MMO ที่ทำอะไรเช่นนี้ แต่โอกาสที่จะเพิ่ม crit จริงทุกครั้งที่คุณได้รับจนกว่าคุณจะไม่ได้รับมันจะรีเซ็ตเป็นค่าที่ต่ำมาก สิ่งนี้นำไปสู่ริ้วรอยที่หายากซึ่งเป็นที่พอใจของผู้เล่น
coderanger

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

2
การแก้ไขสิ่งนี้ไม่จำเป็นต้องมีการแจกแจงแบบไม่เชิงเส้นเพียงแค่ต้องการเซตย่อยแบบต่อเนื่องแบบสั้น ๆ ของการแจกแจงที่มีคุณสมบัติเช่นเดียวกับการแจกแจงเอง

นี่คือวิธีที่ Blizzard ทำเกมอย่างน้อยที่สุดตั้งแต่ Warcraft 3
dreta

2

Sid Meier พูดอย่างยอดเยี่ยมใน GDC 2010 เกี่ยวกับหัวข้อนี้และเกมอารยธรรม ฉันจะพยายามค้นหาและวางลิงก์ในภายหลัง ในสาระสำคัญ - การรับรู้แบบสุ่มไม่เหมือนกับการสุ่มที่แท้จริง เพื่อทำให้สิ่งต่าง ๆ เป็นไปอย่างยุติธรรมคุณต้องวิเคราะห์ผลลัพธ์ก่อนหน้าและให้ความสนใจกับจิตวิทยาของผู้เล่น

หลีกเลี่ยงริ้วรอยแห่งโชคร้ายที่ค่าใช้จ่ายทั้งหมด (ถ้าสองรอบก่อนหน้านี้โชคไม่ดีคนต่อไปควรจะรับประกันว่าจะโชคดี) ผู้เล่นควรโชคดีกว่าฝ่ายตรงข้าม AI เสมอ


0

ใช้การเบี่ยงเบนอคติ

01rb0

การกระจายโดยรวมจะมีอคติโดยสูตรต่อไปนี้:

rexp(b)

b1b0

ใช้ตัวเลขนี้และปรับขนาดให้เหมาะสมกับช่วงที่ต้องการ

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

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