जैसा कि मैंने ऊपर अपनी टिप्पणी में उल्लेख किया है, मैं आपके कोड को ओवरकॉम्प्लिकेट करने से पहले आपको इसकी सलाह देता हूं। एक त्वरित for
लूप योग पासा जटिल गणित फ़ार्मुलों और टेबल-बिल्डिंग / खोज की तुलना में समझने और संशोधित करने के लिए बहुत आसान है। हमेशा यह सुनिश्चित करने के लिए पहले प्रोफाइल बनाएं कि आप महत्वपूर्ण समस्याओं को हल कर रहे हैं। ;)
कहा कि, एक झपट्टा में परिष्कृत संभाव्यता वितरण को नमूना करने के दो मुख्य तरीके हैं:
1. संचयी संभावना वितरण
केवल एक समान रैंडम इनपुट का उपयोग करके निरंतर संभावना वितरण से नमूना करने के लिए एक साफ चाल है । यह संचयी वितरण के साथ करना है , जो फ़ंक्शन का जवाब देता है " x से अधिक मूल्य नहीं होने की संभावना क्या है ?"
यह फ़ंक्शन गैर-घटता है, 0 से शुरू होता है और अपने डोमेन पर 1 से बढ़ता है। दो छह-पक्षीय पासा के योग के लिए एक उदाहरण नीचे दिखाया गया है:
यदि आपके संचयी वितरण फ़ंक्शन में सुविधाजनक-से-उलटा व्युत्क्रम है (या आप इसे बेज़ियर कर्व्स की तरह टुकड़े के कार्यों के साथ अनुमानित कर सकते हैं), तो आप इसका उपयोग मूल संभाव्यता फ़ंक्शन से नमूना लेने के लिए कर सकते हैं।
प्रतिलोम फ़ंक्शन 0 और 1 के बीच के डोमेन को मूल यादृच्छिक प्रक्रिया के प्रत्येक आउटपुट के लिए मैप किए गए अंतराल में पार्सलिंग को संभालता है, प्रत्येक मूल संभावना के मिलान क्षेत्र के साथ। (यह निरंतर वितरण के लिए असीम रूप से सत्य है। डिस्क्स वितरण जैसे असतत वितरण के लिए हमें गोल चक्कर लगाने की आवश्यकता है)
यहाँ 2d6 का अनुकरण करने के लिए इसका उपयोग करने का एक उदाहरण दिया गया है:
int SimRoll2d6()
{
// Get a random input in the half-open interval [0, 1).
float t = Random.Range(0f, 1f);
float v;
// Piecewise inverse calculated by hand. ;)
if(t <= 0.5f)
{
v = (1f + sqrt(1f + 288f * t)) * 0.5f;
}
else
{
v = (25f - sqrt(289f - 288f * t)) * 0.5f;
}
return floor(v + 1);
}
इसकी तुलना करें:
int NaiveRollNd6(int n)
{
int sum = 0;
for(int i = 0; i < n; i++)
sum += Random.Range(1, 7); // I'm used to Range never returning its max
return sum;
}
देखें कि कोड स्पष्टता और लचीलेपन में अंतर के बारे में मेरा क्या मतलब है? अनुभवहीन तरीका अपने छोरों के साथ भोला हो सकता है, लेकिन यह छोटा और सरल है, यह क्या करता है के बारे में तुरंत स्पष्ट है, और विभिन्न मरने के आकार और संख्याओं के पैमाने पर आसान है। संचयी वितरण कोड में परिवर्तन करने के लिए कुछ गैर-तुच्छ गणित की आवश्यकता होती है, और बिना किसी स्पष्ट गलतियों के अप्रत्याशित परिणाम को तोड़ना और उत्पन्न करना आसान होगा। (मुझे उम्मीद है कि मैंने ऊपर नहीं बनाया है)
इसलिए, इससे पहले कि आप एक स्पष्ट लूप के साथ दूर करें, यह बिल्कुल निश्चित करें कि यह वास्तव में इस तरह के बलिदान के लायक एक प्रदर्शन समस्या है।
2. उपनाम विधि
संचयी वितरण विधि अच्छी तरह से काम करती है जब आप संचयी वितरण फ़ंक्शन के व्युत्क्रम को एक साधारण गणित अभिव्यक्ति के रूप में व्यक्त कर सकते हैं, लेकिन यह हमेशा आसान या संभव नहीं होता है। असतत वितरण के लिए एक विश्वसनीय विकल्प अलियास विधि कहा जाता है ।
यह आपको केवल दो स्वतंत्र, समान रूप से वितरित यादृच्छिक आदानों का उपयोग करके किसी भी मनमाने ढंग से असतत वितरण वितरण से नमूना देता है।
यह बाईं ओर नीचे एक वितरण की तरह काम करता है (चिंता मत करो कि क्षेत्रों / वजन 1 के लिए योग नहीं है, अलियास विधि के लिए हम सापेक्ष वजन की परवाह करते हैं ) और इसे मेज पर एक की तरह परिवर्तित करना दाईं ओर जहां:
- प्रत्येक परिणाम के लिए एक कॉलम होता है।
- प्रत्येक स्तंभ को अधिकतम दो भागों में विभाजित किया गया है, प्रत्येक मूल परिणामों में से एक के साथ जुड़ा हुआ है।
- प्रत्येक परिणाम का सापेक्ष क्षेत्र / वजन संरक्षित है।
( नमूना तरीकों पर इस उत्कृष्ट लेख से छवियों के आधार पर आरेख )
कोड में, हम दो स्तंभों (या दो गुणों वाली वस्तुओं की एक तालिका) के साथ इसका प्रतिनिधित्व करते हैं, प्रत्येक कॉलम से वैकल्पिक परिणाम चुनने की संभावना का प्रतिनिधित्व करते हैं, और उस वैकल्पिक परिणाम की पहचान (या "उपनाम")। फिर हम वितरण से नमूना ले सकते हैं जैसे:
int SampleFromTables(float[] probabiltyTable, int[] aliasTable)
{
int column = Random.Range(0, probabilityTable.Length);
float p = Random.Range(0f, 1f);
if(p < probabilityTable[column])
{
return column;
}
else
{
return aliasTable[column];
}
}
इसमें थोड़ा सेट अप शामिल है:
हर संभावित परिणाम की सापेक्ष संभावनाओं की गणना करें (इसलिए यदि आप 1000d6 रोल कर रहे हैं, तो हमें 1000 से 6000 तक हर राशि प्राप्त करने के तरीकों की संख्या की गणना करने की आवश्यकता है)
प्रत्येक परिणाम के लिए एक प्रविष्टि के साथ तालिकाओं की एक जोड़ी बनाएँ। पूर्ण विधि इस उत्तर के दायरे से आगे जाती है, इसलिए मैं अलियास विधि एल्गोरिथ्म के इस स्पष्टीकरण के संदर्भ में अत्यधिक अनुशंसा करता हूं ।
उन तालिकाओं को संग्रहीत करें और हर बार जब आप इस वितरण से एक नया रैंडम डाई रोल चाहते हैं, तो उन्हें वापस देखें।
यह एक स्पेस-टाइम ट्रेडऑफ है । प्रीकंप्यूटेशन स्टेप कुछ हद तक थकावट भरा होता है, और हमें अपने पास मौजूद परिणामों की संख्या के लिए मेमोरी प्रॉपर सेट करने की आवश्यकता होती है (हालाँकि 1000d6 के लिए भी, हम सिंगल-डिजिट किलोबाइट्स के बारे में बात कर रहे हैं, इसलिए नींद कम करने के लिए कुछ भी नहीं), लेकिन बदले में हमारा नमूना कोई फर्क नहीं पड़ता कि हमारा वितरण कितना जटिल है।
मुझे आशा है कि उन तरीकों में से एक या कुछ तरीकों का उपयोग किया जा सकता है (या मैंने आपको आश्वस्त किया है कि भोले विधि की सादगी उस समय के लायक है जो इसे लूप में ले जाती है);)