จะปรับปรุงการสร้างตัวเลขสุ่มในบริบทของฉันได้อย่างไร


11

ในเกมของฉันมีคำที่ด้านบนของหน้าจอตัวอักษรมีฝนตกจากด้านบนและผู้ใช้ต้องสัมผัสตัวอักษรเพื่อให้คำสมบูรณ์

ขณะนี้ฉันกำลังสร้างตัวอักษรแบบสุ่ม (จริง ๆ แล้วตัวเลขและตัวเลขสุ่มเป็นดัชนีสำหรับอาร์เรย์ของตัวอักษรเช่น: 0 = a, 1 = b) แต่ปัญหาคือว่ามันใช้เวลามากเกินไปที่จะได้รับตัวอักษรที่ต้องการทั้งหมด คำ.

สิ่งที่ฉันต้องการคือตัวเลขสุ่มที่ฉันกำลังสร้างควรสร้างตัวอักษรที่ต้องการบ่อยขึ้นเพื่อให้ผู้เล่นไม่ต้องใช้เวลาทั้งวันในการกรอกหนึ่งคำ

ฉันได้ลองวิธีต่อไปนี้แล้ว:

  1. ตรวจจับตัวอักษรทั้งหมดในคำ (คำมีความยาว 6 ตัวอักษรเสมอ) สร้างอาร์เรย์ของดัชนีความยาว 6 กำหนดดัชนีแต่ละชุดของอาร์เรย์ให้เป็นตัวเลขสุ่มจากตัวอักษร -2 ถึงตัวอักษร + 2 และในที่สุดเลือกสุ่มหนึ่งดัชนี จากอาร์เรย์ที่จะแสดง

  2. มีตัวแปรตัวเลือกที่มีค่าอยู่ในช่วง [0..2] ซึ่งสร้างแบบสุ่มหากตัวเลือก == 0 จากนั้นตรวจจับตัวอักษรที่ทำให้คำนั้นและสุ่มเลือกตัวอักษรหนึ่งตัวอื่น ๆ จะได้รับตัวอักษรแบบสุ่มจาก az

วิธีการทั้งสองนี้ไม่ได้ช่วยอะไรฉัน ฉันจะมีความสุขมากถ้าคุณสามารถช่วยฉันได้

ขอบคุณสำหรับการอ่านนี้ฉันหวังว่าคุณจะเข้าใจคำถามและฉันกำลังรอคำตอบ


2
"ทั้งสองวิธีนี้ไม่ได้ช่วยอะไรฉัน"ทำไมล่ะ อะไรไม่ได้ผลกับวิธีการเหล่านี้?
Vaillancourt

ฉันไม่รู้ว่าทำไม แต่ยังใช้เวลานานเกินไปเช่น 1 นาทีเพื่อให้ได้ตัวอักษรทั้งหมดที่ต้องการ
Daniyal Azram

@DaniyalAzram คุณควรเพิ่มความถี่มากขึ้นหากตัวอักษรเหล่านี้ไม่ปรากฏบ่อยพอเนื่องจากดูเหมือนว่านั่นเป็นปัญหาของคุณ
JFA

คำตอบ:


21

คุณไม่ต้องการกระจายแบบสุ่ม ฉันชี้ให้เห็นอย่างชัดเจนเพราะสิ่งที่เราพิจารณาว่า "แบบสุ่ม" สำหรับการออกแบบมักจะไม่ใช่แบบแผนที่แท้จริง

ทีนี้เมื่อคำนึงถึงสิ่งนั้นแล้วเราจะเพิ่มค่า tweaking - นี่คือสิ่งที่คุณจะทำด้วยความรู้สึกจนกระทั่งการออกแบบรู้สึก "ถูกต้อง"

ChooseLetter() {
    const float WordLetterProbability = 0.5f;
    if (Random.NextFloat01() < WordLetterProbability) {
        // Pick a random letter in the word
    }
    else {
        // Pick a random letter *not* in the word
    }
}

ความน่าจะเป็นเป็นตัวควบคุมว่ามีแนวโน้มที่การเรียกไปที่ ChooseLetter จะให้ตัวอักษรคำแก่คุณหรือไม่ที่ 0.5 คุณจะได้รับตัวอักษรคำประมาณทุกครั้ง ที่ 0.25 หนึ่งในสี่จะเป็นตัวอักษรคำ ฯลฯ

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

const float BaseWordLetterProbability = 0.5f;
const float WordLetterProbabilityIncrease = 0.25f;
float WordLetterProbability = BaseWordLetterProbability;
ChooseLetter() {
    if (Random.NextFloat01() < WordLetterProbability) {
        // Pick a random letter in the word
        WordLetterProbability = BaseWordLetterProbability;
    }
    else {
        // Pick a random letter *not* in the word
        WordLetterProbability += WordLetterProbabilityIncrease;
    }
}

ตกลงดังนั้นตอนนี้เราไม่เคยไปเกินสองตัวอักษรโดยไม่มีตัวอักษรคำ (เพราะหลังจากพลาดไปสองครั้งเรามีความน่าจะเป็น 1.0 ที่จะได้รับจดหมายคำ)

สุดท้ายเราต้องพิจารณาด้วยว่าการเลือกตัวอักษรในคำนั้นเป็นอย่างไร เพื่อให้ผู้เล่นได้รับจดหมายที่พวกเขาต้องการจริงเราต้องลบจดหมายออกจากชุดเมื่อพวกเขาได้รับมา

ตัวอย่างเช่นหากคำว่า "ทดสอบ" และผู้เล่นมี 's' อยู่แล้วเราไม่ต้องการให้พวกเขาอีกเพราะพวกเขาไม่ต้องการ!

จากที่นี่ส่วนที่เหลือก็ปรับให้เข้ากับการออกแบบของคุณ


ว้าว! คุณคิดยังไงกับเรื่องนี้อีก: p ฉันจะทดสอบวิธีการของคุณในเช้าวันพรุ่งนี้และฉันคิดว่ามันจะได้ผลฉันจะให้ข้อเสนอแนะเพิ่มเติมเมื่อฉันทดสอบ mehod นี้ ขอบคุณมากสำหรับคำตอบนั้น
Daniyal Azram

4
สถิติ! ในฐานะที่เป็นผู้ออกแบบเกมหรือโปรแกรมเมอร์สถิติมีความคุ้มค่าในการเรียนรู้ อย่างน้อยที่สุดการเข้าใจความน่าจะเป็น (และวิธีการรวมเข้าด้วยกัน) มีประโยชน์อย่างยิ่ง
ACEfanatic02

1
มาแนะนำสิ่งเดียวกัน คำตอบที่ดี โปรดจำไว้ว่าวิธีแก้ปัญหาเช่นนี้อาจเป็นวิธีที่จะแนะนำระดับความยากลำบาก ง่าย = 0.5, ปานกลาง = 0.25, ยาก = 0.10 ฯลฯ
Tasos

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


21

คุณสามารถมีน้ำหนักน่าจะเป็นของตัวอักษรทั้งหมดของคุณตามความถี่ที่พวกเขาเกิดขึ้นในภาษาที่คำพูดของคุณอยู่ใน. แนวทางที่ดีคือชุดหวัด ตัวอย่างเช่นเวอร์ชันภาษาอังกฤษมี 12 E แต่มีเพียง Z เดียวและหนึ่ง Q

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

const String letters = "AAAAAAAAABBCCDDDDEEEEEEEEEEEEFFGGGHHIIIIIIIIIJ/*...and so on...*/"

char randomLetter = letters[randomIntegerBetween(0, letters.length - 1)];

2
+1 นี่เป็นแนวคิดที่ดี แต่ฉันคิดว่ามีการใช้งานที่หรูหรากว่า
Evorlor

1
ที่เกี่ยวข้อง: codereview.stackexchange.com/q/94202/61072
Evorlor

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

4
สิ่งนี้จะไม่แก้ปัญหา ปัญหาคือผู้ใช้ต้องการตัวอักษรเฉพาะเพื่อจบเกม พูดว่าพวกเขาต้องการ Z ถ้าเช่นนั้นจะเป็นอย่างไร นี่จะทำให้ตัวอักษรที่หายากได้ยากขึ้นเพื่อทำให้ผู้ใช้ผิดหวังยิ่งขึ้น
AmazingDreams

@AmazingDreams ชี้ให้เห็นถึงสิ่งที่ดี แต่เราสามารถแก้ไขได้เล็กน้อยดังนั้นฉันจะกำหนดน้ำหนักให้กับตัวอักษรที่ต้องการเพิ่มขึ้นและให้น้ำหนักกับผู้อื่นน้อยลง ฉันต้องบอกว่านี่เป็นแนวคิดที่ดีในการติดตาม
Daniyal Azram

4

นี่คือวิธีหนึ่งในการปรับปรุงโดยใช้พารามิเตอร์เดียวkที่คุณสามารถปรับแต่งได้

แทนที่จะเลือกตัวอักษรสุ่ม:

  1. เลือกตัวอักษรแบบสุ่ม A
  2. เลือกหมายเลขสุ่ม X
  3. ถ้าX > k และ Aไม่อยู่[list of remaining needed letters]ลองอีกครั้งในวันที่ 1

ขนาดเล็กkคือยิ่งตัวอักษรสุดท้ายAเป็นสิ่งที่จำเป็นจริง ๆ

ในการปรับแต่งอัลกอริทึมให้เล่นด้วยค่าใดก็ได้ k , เช่น k = 0.5หากคุณพบว่าเกมนี้ยากเกินไปให้ลองทำ0.4แทนเป็นต้นจนกว่าคุณจะพบว่ามีคุณค่าพอสมควร สิ่งนี้ยังทำให้คุณตั้งค่าความยากลำบากได้โดยตรงซึ่งตัวอย่างเช่นคุณอาจต้องการเพิ่มขึ้นเมื่อผู้เล่นก้าวหน้าในเกม


ขอบคุณสำหรับคำตอบ แต่นี่ดูเหมือนกับคำตอบของ ACEfanatic02
Daniyal Azram

3

วิธีง่ายๆในการรับประกันว่าตัวอักษรที่ต้องการจะปรากฏขึ้นภายในเวลาที่กำหนดคือการใช้ตัวอักษรในคำและส่วนที่เหลือของตัวอักษร (อาจจะซ้ำแล้วซ้ำอีก) จากนั้นสุ่มสุ่มอาร์เรย์ (c ++ มี std :: random_shuffle ในไลบรารีมาตรฐานหากคุณใช้ภาษาอื่นมันไม่ยากที่จะนำมาใช้)

หากคุณต้องการให้ตัวอักษรในคำปรากฏขึ้นเร็วขึ้นให้ใส่ตัวอักษรคำในอาร์เรย์มากขึ้น


0

หากคุณใช้ C ++ คุณสามารถใช้การกระจายที่มีอยู่แล้ว http://en.cppreference.com/w/cpp/numeric/random/piecewise_constant_distribution

นอกจากนี้ยังมีการตัดจำหน่ายเวลาที่ซับซ้อนอย่างต่อเนื่องซึ่งดีกว่าวิธีการที่ไร้เดียงสา ตัวอย่าง:

#include <iostream>
#include <string>
#include <map>
#include <random>
#include <numeric>

int main()
{
    constexpr double containedLetterWeight = 3.0;
    constexpr int iterations = 10000;
    std::string word = "DISTRIBUTION";

    std::mt19937 rng(123);

    std::vector<double> values('Z' - 'A' + 2);
    std::iota(values.begin(), values.end(), 0.0);

    std::vector<double> weights('Z' - 'A' + 1, 1.0);
    for(const auto& c : word) weights[c - 'A'] = containedLetterWeight;

    std::piecewise_constant_distribution<> dist(values.begin(), values.end(), weights.begin());

    std::map<char, int> results;
    for(int n = 0; n < iterations; ++n)
    {
        ++results[static_cast<char>(dist(rng)) + 'A'];
    }
    for(const auto& p : results)
    {
        std::cout << p.first << ' ' << static_cast<float>(p.second) / iterations << '\n';
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.