सही छोरों के बिना पासा रोल के बहुत सारे अनुकरण?


14

ठीक है अगर आपका गेम बहुत सारे पासा को रोल करता है तो आप एक लूप में एक यादृच्छिक संख्या जनरेटर को कॉल कर सकते हैं। लेकिन पासा के किसी भी सेट को अक्सर रोल करने के लिए आपको एक वितरण वक्र / हिस्टोग्राम मिलेगा। तो मेरा सवाल यह है कि मैं एक अच्छी सरल गणना कर सकता हूं जो मुझे चला सकता है जो मुझे एक संख्या देगा जो उस वितरण को फिट करता है?

उदा 2D6 - स्कोर -% संभावना

2 - 2.77%

3 - 5.55%

4 - 8.33%

5 - 11.11%

6 - 13.88%

7 - 16.66%

8 - 13.88%

9 - 11.11%

10 - 8.33%

11 - 5.55%

12 - 2.77%

इसलिए उपरोक्त जानने से आप एक एकल d100 को रोल कर सकते हैं और एक सटीक 2D6 मान प्राप्त कर सकते हैं। लेकिन एक बार जब हम 10D6, 50D6, 100D6, 1000D6 से शुरू करते हैं, तो इससे बहुत प्रसंस्करण समय बच सकता है। तो वहाँ एक ट्यूटोरियल / विधि / एल्गोरिथ्म होना चाहिए जो इस तेजी से कर सकता है? यह शायद शेयर बाजारों, कैसीनो, रणनीति के खेल, बौना किले आदि के लिए आसान है। यदि आप पूरी रणनीतिक लड़ाई के परिणामों का अनुकरण कर सकते हैं जो इस फ़ंक्शन और कुछ बुनियादी गणित के लिए कुछ कॉल के साथ खेलने के लिए घंटों का समय लेगा?


5
यहां तक ​​कि 1000 डी 6 पर, लूप एक आधुनिक पीसी पर पर्याप्त तेज होगा जिसे आप इसे नोटिस करने की संभावना नहीं रखते हैं, इसलिए यह समय से पहले अनुकूलन हो सकता है। अपारदर्शी सूत्र के साथ स्पष्ट लूप को बदलने से पहले हमेशा प्रोफाइलिंग की कोशिश करें। उस ने कहा, एल्गोरिथम विकल्प हैं। क्या आप अपने उदाहरणों में पासा की तरह असतत संभावना में रुचि रखते हैं, या क्या उन्हें निरंतर संभाव्यता वितरण के रूप में मॉडल करना स्वीकार्य है (इसलिए 2.5 जैसा भिन्नात्मक परिणाम संभव हो सकता है)?
DMGregory

DMGregory सही है, 1000d6 की गणना प्रोसेसर हॉग से ज्यादा नहीं है। हालाँकि, एक द्विपद वितरण कहा जाता है, जो (कुछ चतुर काम के साथ) आप में रुचि रखने वाले परिणाम प्राप्त करेंगे। इसके अलावा, यदि आप कभी भी एक मनमाना रोल नियम के लिए संभावनाओं को ढूंढना चाहते हैं, तो TRoll का प्रयास करें जिसमें एक मामूली भाषा है यह निर्धारित करने के लिए कि पासे का एक सेट कैसे रोल करना है और यह हर संभव परिणाम के लिए सभी संभावनाओं की गणना करेगा।
Draco18s अब SE 18

एक पॉइज़न वितरण का उपयोग करें: पी।
लुइस मूसली

1
पासा के किसी भी सेट को अक्सर रोल करने के लिए आपको संभवतः एक वितरण वक्र / हिस्टोग्राम मिलेगा। यह एक महत्वपूर्ण अंतर है। एक पासा एक पंक्ति में एक मिलियन 6s को रोल कर सकता है, यह संभावना नहीं है, लेकिन यह हो सकता है
रिचर्ड टिंगल

क्या आप विस्तृत कर सकते हैं? वितरण वक्र / हिस्टोग्राम में "पंक्ति में 6 मिलियन" भी शामिल होगा।
अमितप

जवाबों:


16

जैसा कि मैंने ऊपर अपनी टिप्पणी में उल्लेख किया है, मैं आपके कोड को ओवरकॉम्प्लिकेट करने से पहले आपको इसकी सलाह देता हूं। एक त्वरित forलूप योग पासा जटिल गणित फ़ार्मुलों और टेबल-बिल्डिंग / खोज की तुलना में समझने और संशोधित करने के लिए बहुत आसान है। हमेशा यह सुनिश्चित करने के लिए पहले प्रोफाइल बनाएं कि आप महत्वपूर्ण समस्याओं को हल कर रहे हैं। ;)

कहा कि, एक झपट्टा में परिष्कृत संभाव्यता वितरण को नमूना करने के दो मुख्य तरीके हैं:


1. संचयी संभावना वितरण

केवल एक समान रैंडम इनपुट का उपयोग करके निरंतर संभावना वितरण से नमूना करने के लिए एक साफ चाल है । यह संचयी वितरण के साथ करना है , जो फ़ंक्शन का जवाब देता है " x से अधिक मूल्य नहीं होने की संभावना क्या है ?"

यह फ़ंक्शन गैर-घटता है, 0 से शुरू होता है और अपने डोमेन पर 1 से बढ़ता है। दो छह-पक्षीय पासा के योग के लिए एक उदाहरण नीचे दिखाया गया है:

2d6 के लिए संभाव्यता, संचयी वितरण और व्युत्क्रम के ग्राफ

यदि आपके संचयी वितरण फ़ंक्शन में सुविधाजनक-से-उलटा व्युत्क्रम है (या आप इसे बेज़ियर कर्व्स की तरह टुकड़े के कार्यों के साथ अनुमानित कर सकते हैं), तो आप इसका उपयोग मूल संभाव्यता फ़ंक्शन से नमूना लेने के लिए कर सकते हैं।

प्रतिलोम फ़ंक्शन 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];
    }
}

इसमें थोड़ा सेट अप शामिल है:

  1. हर संभावित परिणाम की सापेक्ष संभावनाओं की गणना करें (इसलिए यदि आप 1000d6 रोल कर रहे हैं, तो हमें 1000 से 6000 तक हर राशि प्राप्त करने के तरीकों की संख्या की गणना करने की आवश्यकता है)

  2. प्रत्येक परिणाम के लिए एक प्रविष्टि के साथ तालिकाओं की एक जोड़ी बनाएँ। पूर्ण विधि इस उत्तर के दायरे से आगे जाती है, इसलिए मैं अलियास विधि एल्गोरिथ्म के इस स्पष्टीकरण के संदर्भ में अत्यधिक अनुशंसा करता हूं

  3. उन तालिकाओं को संग्रहीत करें और हर बार जब आप इस वितरण से एक नया रैंडम डाई रोल चाहते हैं, तो उन्हें वापस देखें।

यह एक स्पेस-टाइम ट्रेडऑफ है । प्रीकंप्यूटेशन स्टेप कुछ हद तक थकावट भरा होता है, और हमें अपने पास मौजूद परिणामों की संख्या के लिए मेमोरी प्रॉपर सेट करने की आवश्यकता होती है (हालाँकि 1000d6 के लिए भी, हम सिंगल-डिजिट किलोबाइट्स के बारे में बात कर रहे हैं, इसलिए नींद कम करने के लिए कुछ भी नहीं), लेकिन बदले में हमारा नमूना कोई फर्क नहीं पड़ता कि हमारा वितरण कितना जटिल है।


मुझे आशा है कि उन तरीकों में से एक या कुछ तरीकों का उपयोग किया जा सकता है (या मैंने आपको आश्वस्त किया है कि भोले विधि की सादगी उस समय के लायक है जो इसे लूप में ले जाती है);)


1
बहुत बढ़िया जवाब। मुझे भोली दृष्टिकोण पसंद है। त्रुटियों के लिए बहुत कम जगह और समझने में आसान।
बुमज़ैक

FYI करें यह प्रश्न रेडिट पर एक यादृच्छिक प्रश्न से कॉपी-पेस्ट है।
Vaillancourt

Ccompleteness के लिए, मुझे लगता है कि यह लाल धागा है जो @AlexandreVaillancourt के बारे में बात कर रहा है। मुख्य रूप से दिए गए उत्तर लूपिंग संस्करण को रखने का सुझाव देते हैं (कुछ सबूतों के साथ कि इसकी समय लागत उचित है) या सामान्य / गाऊसी वितरण का उपयोग करके बड़ी संख्या में पासा अनुमानित करना।
DMGregory

उपनाम विधि के लिए +1, ऐसा लगता है कि बहुत कम लोग इसके बारे में जानते हैं, और यह वास्तव में इन प्रकार की संभाव्यता पसंद स्थितियों में से बहुत से आदर्श समाधान है और गौसियन समाधान का उल्लेख करने के लिए +1, जो संभवतः "बेहतर" है। अगर हम केवल प्रदर्शन और अंतरिक्ष बचत की परवाह करते हैं तो जवाब दें।
whn

0

दुर्भाग्य से उत्तर यह है कि इस पद्धति के परिणामस्वरूप प्रदर्शन में वृद्धि नहीं होगी।

मेरा मानना ​​है कि इस सवाल में कुछ गलतफहमी हो सकती है कि एक यादृच्छिक संख्या कैसे उत्पन्न होती है। नीचे दिए गए उदाहरण को लें [Java]:

Random r = new Random();
int n = 20;
int min = 1; //arbitrary
int max = 6; //arbitrary
for(int i = 0; i < n; i++){
    int randomNumber = (r.nextInt(max - min + 1) + min)); //silly maths
    System.out.println("Here's a random number: " + randomNumber);
}

यह कोड 1 और 6 (समावेशी) के बीच यादृच्छिक संख्याओं को प्रिंट करते हुए 20 बार लूप करेगा। जब हम इस कोड के प्रदर्शन के बारे में बात करते हैं, तो रैंडम ऑब्जेक्ट बनाने के लिए कुछ समय लगता है (जिसमें कंप्यूटर की आंतरिक घड़ी के आधार पर छद्म-यादृच्छिक पूर्णांकों की एक सरणी बनाना शामिल होता है), और फिर 20 निरंतर समय प्रत्येक nextInt () कॉल पर लुक-अप। चूंकि प्रत्येक 'रोल' एक निरंतर समय संचालन है, इसलिए यह बहुत सस्ते समय-वार को रोल करता है। यह भी ध्यान दें कि अधिकतम से मिनट की सीमा कोई मायने नहीं रखती (दूसरे शब्दों में, यह कंप्यूटर के लिए एक d6 को रोल करना उतना ही आसान है जितना कि इसके लिए d10000 को रोल करना है)। समय की जटिलता के संदर्भ में बोलते हुए, समाधान का प्रदर्शन केवल ओ (एन) है जहां एन पासा की संख्या है।

वैकल्पिक रूप से, हम किसी एकल d100 (या उस मामले के लिए d10000) रोल के साथ d6 रोल की किसी भी संख्या का अनुमान लगा सकते हैं। इस पद्धति का उपयोग करते हुए, हमें सबसे पहले s [[पासा से चेहरे की संख्या] की गणना करने की आवश्यकता है * n [पासे की संख्या] प्रतिशत से पहले हम रोल करते हैं (तकनीकी रूप से यह s * n - n + 1 प्रतिशत है, और हमें लगभग पूरी तरह से विभाजित करने में सक्षम होना चाहिए। आधे में, यह सममित है; ध्यान दें कि 2d6 रोल के अनुकरण के लिए आपके उदाहरण में, आपने 11 प्रतिशत की गणना की और 6 अद्वितीय थे)। रोल करने के बाद हम यह पता लगाने के लिए एक बाइनरी खोज का उपयोग कर सकते हैं कि हमारा रोल किस रेंज में गिर गया। समय की जटिलता के संदर्भ में, यह समाधान एक O (s * n) समाधान का मूल्यांकन करता है, जहां s पक्षों की संख्या है और n पास dice की संख्या है। जैसा कि हम देख सकते हैं, यह पिछले पैराग्राफ में प्रस्तावित ओ (एन) समाधान की तुलना में धीमा है।

वहां से हटाते हुए, कहते हैं कि आपने 1000d20 रोल का अनुकरण करने के लिए इन दोनों कार्यक्रमों का निर्माण किया। पहले बस 1,000 बार लुढ़का होगा। दूसरे कार्यक्रम को पहले कुछ और करने से पहले 19,001 प्रतिशत (1,000 से 20,000 की संभावित सीमा के लिए) निर्धारित करने की आवश्यकता होगी। इसलिए जब तक आप एक अजीब प्रणाली पर नहीं होते हैं जहां मेमोरी लुक-अप फ्लोटिंग-पॉइंट ऑपरेशंस की तुलना में गंभीर रूप से अधिक महंगे होते हैं, प्रत्येक रोल के लिए नेक्स्ट () कॉल का उपयोग करने का तरीका लगता है।


2
ऊपर दिया गया विश्लेषण बिल्कुल सही नहीं है। यदि हम Alias ​​Method के अनुसार प्रायिकता और उर्फ़ तालिकाओं को उत्पन्न करने के लिए कुछ समय ऊपर-सामने सेट करते हैं , तो हम निरंतर समय (2 यादृच्छिक संख्या और एक तालिका लुकअप) में एक अनियंत्रित असतत संभाव्यता वितरण से नमूना ले सकते हैं। तो 5 पासा के रोल या 500 पासा के रोल का अनुकरण एक ही बार काम करता है, एक बार तालिकाओं को तैयार करने के बाद। यह समान रूप से हर नमूने के लिए बड़ी संख्या में पासा से अधिक तेज़ है, हालांकि यह समस्या का बेहतर समाधान नहीं करता है। ;)
DMGregory

0

यदि आप पासा संयोजनों को संग्रहीत करना चाहते हैं, तो अच्छी खबर यह है कि एक समाधान है, बुरा यह है कि हमारे कंप्यूटर इस तरह की समस्याओं के संबंध में किसी तरह सीमित हैं।

अच्छी खबर:

इस समस्या का एक निर्धारित दृष्टिकोण है:

1 / पासा के अपने समूह के सभी संयोजनों की गणना करें

2 / प्रत्येक संयोजन के लिए संभावना निर्धारित करें

3 / इस सूची में खोज के बजाय परिणाम फेंकने के लिए

बुरी ख़बरें:

पुनरावृत्ति के साथ संयोजन की संख्या निम्नलिखित सूत्रों द्वारा दी गई है

Γn=(n+-1)=(n+-1)!! (n-1)!

( फ्रेंच विकिपीडिया से ):

दोहराव के साथ संयोजन

इसका मतलब है कि, उदाहरण के लिए, 150 डाइस के साथ, आपके पास 698'526'906 संयोजन हैं। मान लें कि आप संभावना को 32 बिट फ्लोट के रूप में संग्रहीत करते हैं, आपको 2,6 जीबी मेमोरी की आवश्यकता होगी, और आपको अभी भी इंडेक्स के लिए मेमोरी की आवश्यकता को जोड़ना होगा ...

कंप्यूटिंग शब्दों में, संयोजन संख्या को संकल्पों द्वारा गणना की जा सकती है, जो कि आसान है लेकिन मेमोरी की बाधाओं को हल नहीं करता है।

अंत में, अधिक संख्या में डाइस के लिए, मैं डाइस को फेंकने की सलाह दूंगा और प्रत्येक संयोजन से जुड़ी संभावनाओं को पूर्ववर्ती करने के बजाय परिणाम का निरीक्षण करूंगा।

संपादित करें

हालांकि, जैसा कि आप केवल पांसे के योग में रुचि रखते हैं, आप संभावनाओं को बहुत कम संसाधनों के साथ संग्रहीत कर सकते हैं।

आप कनवल्शन का उपयोग करके प्रत्येक पासा की सटीक संभावनाओं की गणना कर सकते हैं।

सामान्य सूत्र है एफमैं()=Σnएफ1(n)एफमैं-1(-n)

फिर 1/6 से प्रत्येक परिणाम को 1 पासे के साथ शुरू करते हुए, आप किसी भी संख्या में पासा के लिए सभी सही संभावनाओं का निर्माण कर सकते हैं।

यहाँ एक मोटा जावा कोड मैंने चित्रण के लिए लिखा है (वास्तव में अनुकूलित नहीं):

public class DiceProba {

private float[][] probas;
private int currentCalc;

public int getCurrentCalc() {
    return currentCalc;
}

public float[][] getProbas() {
    return probas;
}

public void calcProb(int faces, int diceNr) {

    if (diceNr < 0) {
        currentCalc = 0;
        return;
    }

    // Initialize
    float baseProba = 1.0f / ((float) faces);
    probas = new float[diceNr][];
    probas[0] = new float[faces + 1];
    probas[0][0] = 0.0f;
    for (int i = 1; i <= faces; ++i)
        probas[0][i] = baseProba;

    for (int i = 1; i < diceNr; ++i) {

        int maxValue = (i + 1) * faces + 1;
        probas[i] = new float[maxValue];

        for (int j = 0; j < maxValue; ++j) {

            probas[i][j] = 0;
            for (int k = 0; k <= j; ++k) {
                probas[i][j] += probability(faces, k, 0) * probability(faces, j - k, i - 1);
            }

        }

    }

    currentCalc = diceNr;

}

private float probability(int faces, int number, int diceNr) {

    if (number < 0 || number > ((diceNr + 1) * faces))
        return 0.0f;

    return probas[diceNr][number];

}

}

कैलकप्रोब () को अपने इच्छित मापदंडों के साथ कॉल करें और फिर परिणाम के लिए प्रोब टेबल पर जाएं (पहला सूचकांक: 0 के लिए 1 डाइस, 1 के लिए दो डाइस ...)।

मैंने इसे अपने लैपटॉप पर 1'000D6 के साथ चेक किया, 1 से 1'000 तक की सभी संभावनाओं और सभी संभावित योगों की गणना करने में 10 सेकंड का समय लगा।

प्रीकंप्यूटिंग और कुशल भंडारण के साथ, आपके पास उच्च संख्या के लिए त्वरित उत्तर हो सकते हैं।

आशा करता हूँ की ये काम करेगा।


3
चूंकि ओपी केवल पासा के योग के मूल्य की तलाश कर रहा है, यह दहनशील गणित लागू नहीं होता है, और संभावना तालिका प्रविष्टियों की संख्या पासा के आकार और पासा की संख्या के साथ रैखिक रूप से बढ़ती है।
DMGregory

तुम सही हो ! मैंने अपना उत्तर संपादित कर दिया है। जब हम हमेशा चतुर होते हैं;)
elenfoiro78

मुझे लगता है कि आप एक विभाजन और विजय दृष्टिकोण का उपयोग करके दक्षता में थोड़ा सुधार कर सकते हैं। हम 20d6 के लिए संभाव्यता तालिका की गणना स्वयं के साथ 10d6 के लिए तालिका को हल करके कर सकते हैं। 10d6 हम स्वयं के साथ 5d6 तालिका को हल करके पा सकते हैं। 5d6 हम 2d6 और 3d6 तालिकाओं को हल करके पा सकते हैं। इस तरह से आगे बढ़ने से हम 1-20 से अधिकांश टेबल साइज बनाना छोड़ सकते हैं, और दिलचस्प लोगों पर अपना ध्यान केंद्रित कर सकते हैं।
DMGregory

1
और समरूपता का उपयोग करें!
एलेनोफिरो78
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.