भारहीन यादृच्छिक संख्या


101

मैं एक भारित यादृच्छिक संख्याओं को लागू करने की कोशिश कर रहा हूं। मैं वर्तमान में सिर्फ दीवार के खिलाफ अपना सिर पीट रहा हूं और यह पता नहीं लगा सकता।

मेरी परियोजना में (होल्डम हैंड-रेंज, सब्जेक्टिव ऑल-इन इक्विटी एनालिसिस), मैं बूस्ट का यादृच्छिक-उपयोग कर रहा हूं। तो, मान लें कि मैं 1 और 3 के बीच एक यादृच्छिक संख्या चुनना चाहता हूं (इसलिए 1, 2 या 3)। बूस्ट का मर्सिएन ट्विस्टर जनरेटर इसके लिए एक आकर्षण की तरह काम करता है। हालाँकि, मैं चाहता हूँ कि इस तरह उदाहरण के लिए भार उठाया जाए:

1 (weight: 90)
2 (weight: 56)
3 (weight:  4)

क्या बूस्ट में इसके लिए कुछ प्रकार की कार्यक्षमता है?

जवाबों:


179

किसी आइटम को यादृच्छिक रूप से चुनने के लिए एक सीधा-साधा एल्गोरिथ्म है, जहाँ आइटमों का अलग-अलग वजन होता है:

1) सभी भारों के योग की गणना करें

2) एक यादृच्छिक संख्या चुनें जो 0 या अधिक है और भार के योग से कम है

3) एक समय में एक आइटम के माध्यम से जाना, अपने यादृच्छिक संख्या से उनके वजन घटाना, जब तक आप आइटम जहां यादृच्छिक संख्या उस आइटम के वजन से कम है

छद्म कोड इसे दिखाता है:

int sum_of_weight = 0;
for(int i=0; i<num_choices; i++) {
   sum_of_weight += choice_weight[i];
}
int rnd = random(sum_of_weight);
for(int i=0; i<num_choices; i++) {
  if(rnd < choice_weight[i])
    return i;
  rnd -= choice_weight[i];
}
assert(!"should never get here");

यह आपके बूस्ट कंटेनर और इस तरह के अनुकूल होने के लिए सीधा होना चाहिए।


यदि आपका वज़न शायद ही कभी बदला जाता है, लेकिन आप अक्सर यादृच्छिक पर एक को चुनते हैं, और जब तक आपका कंटेनर ऑब्जेक्ट्स को पॉइंटर्स स्टोर कर रहा है या कुछ दर्जन से अधिक आइटम लंबा है (मूल रूप से, आपको यह जानने के लिए प्रोफाइल करना होगा कि क्या यह मदद करता है या बाधा डालता है) , तो एक अनुकूलन है:

प्रत्येक आइटम में संचयी भार राशि को संग्रहीत करके आप आइटम को पिक वजन के अनुरूप लेने के लिए एक द्विआधारी खोज का उपयोग कर सकते हैं ।


यदि आप सूची में मौजूद वस्तुओं की संख्या नहीं जानते हैं, तो जलाशय के नमूने नामक एक बहुत साफ एल्गोरिथ्म है जिसे भारित करने के लिए अनुकूलित किया जा सकता है।


3
एक अनुकूलन के रूप में आप संचयी भार का उपयोग कर सकते हैं और एक द्विआधारी खोज का उपयोग कर सकते हैं। लेकिन केवल तीन अलग-अलग मूल्यों के लिए यह संभवतः ओवरकिल है।
सेलिबिट्ज़

2
मुझे लगता है जब आप कहते हैं "क्रम में" आप जानबूझकर पसंद_वेट सरणी पर एक पूर्व-प्रकार के कदम को छोड़ रहे हैं, हाँ?
SilentDirge

2
@ ऑरिस, सरणी को सॉर्ट करने की कोई आवश्यकता नहीं है। मैंने अपनी भाषा स्पष्ट करने की कोशिश की है।
विल

1
@Will: हाँ, लेकिन एक ही नाम का एल्गोरिथ्म है। sirkan.iit.bme.hu/~szirmay/c29.pdf और en.wikipedia.org/wiki/Photon_mapping A Monte Carlo method called Russian roulette is used to choose one of these actions जब यह इसके लिए googling में बाल्टी में आता है। "रूसी रूले एल्गोरिथ्म"। आप तर्क दे सकते हैं कि इन सभी लोगों का नाम गलत है।
v.oddou

3
भविष्य के पाठकों के लिए ध्यान दें: आपके यादृच्छिक संख्या से उनके वजन को घटाने वाला हिस्सा अनदेखी करना आसान है, लेकिन एल्गोरिथ्म के लिए महत्वपूर्ण है (मैं उनकी टिप्पणी में @kobik के समान जाल में गिर गया)।
फ्रैंक श्मिट

48

एक पुराने प्रश्न का अद्यतन उत्तर। आप इसे आसानी से C ++ 11 में सिर्फ std :: lib के साथ कर सकते हैं:

#include <iostream>
#include <random>
#include <iterator>
#include <ctime>
#include <type_traits>
#include <cassert>

int main()
{
    // Set up distribution
    double interval[] = {1,   2,   3,   4};
    double weights[] =  {  .90, .56, .04};
    std::piecewise_constant_distribution<> dist(std::begin(interval),
                                                std::end(interval),
                                                std::begin(weights));
    // Choose generator
    std::mt19937 gen(std::time(0));  // seed as wanted
    // Demonstrate with N randomly generated numbers
    const unsigned N = 1000000;
    // Collect number of times each random number is generated
    double avg[std::extent<decltype(weights)>::value] = {0};
    for (unsigned i = 0; i < N; ++i)
    {
        // Generate random number using gen, distributed according to dist
        unsigned r = static_cast<unsigned>(dist(gen));
        // Sanity check
        assert(interval[0] <= r && r <= *(std::end(interval)-2));
        // Save r for statistical test of distribution
        avg[r - 1]++;
    }
    // Compute averages for distribution
    for (double* i = std::begin(avg); i < std::end(avg); ++i)
        *i /= N;
    // Display distribution
    for (unsigned i = 1; i <= std::extent<decltype(avg)>::value; ++i)
        std::cout << "avg[" << i << "] = " << avg[i-1] << '\n';
}

मेरे सिस्टम पर आउटपुट:

avg[1] = 0.600115
avg[2] = 0.373341
avg[3] = 0.026544

ध्यान दें कि ऊपर दिए गए अधिकांश कोड आउटपुट को प्रदर्शित करने और विश्लेषण करने के लिए समर्पित हैं। वास्तविक पीढ़ी कोड की कुछ पंक्तियाँ है। आउटपुट दर्शाता है कि अनुरोधित "संभावनाएं" प्राप्त की गई हैं। आपको अनुरोधित आउटपुट को 1.5 से विभाजित करना होगा क्योंकि अनुरोधों को जोड़ा जाता है।


इस उदाहरण के संकलन पर सिर्फ एक अनुस्मारक नोट: C ++ 11 अर्थात की आवश्यकता है। उपयोग -std = c ++ 0x संकलक ध्वज, gcc 4.6 के बाद से उपलब्ध है।
Pete855217

3
समस्या को हल करने वाले आवश्यक हिस्सों को चुनने की देखभाल करें?
जॉनी

2
यह सबसे अच्छा जवाब है, लेकिन मुझे लगता है कि std::discrete_distributionइसके बजाय और std::piecewise_constant_distributionभी बेहतर होता।
दान

1
@ दान, हाँ, यह करने के लिए एक और उत्कृष्ट तरीका होगा। यदि आप इसे कोड करते हैं और इसके साथ जवाब देते हैं, तो मैं इसके लिए वोट करूंगा। मुझे लगता है कि मेरे पास जो कोड है उससे काफी सुंदर हो सकता है। आपको केवल जनरेट किए गए आउटपुट में एक जोड़ना होगा। और वितरण के लिए इनपुट सरल होगा। इस क्षेत्र में उत्तरों की तुलना / विपरीत सेट पाठकों के लिए मूल्यवान हो सकता है।
हावर्ड हिनांट

15

यदि आपका वजन अधिक धीरे-धीरे बदल जाता है, तो वे C ++ 11 discrete_distributionसबसे आसान होने जा रहे हैं:

#include <random>
#include <vector>
std::vector<double> weights{90,56,4};
std::discrete_distribution<int> dist(std::begin(weights), std::end(weights));
std::mt19937 gen;
gen.seed(time(0));//if you want different results from different runs
int N = 100000;
std::vector<int> samples(N);
for(auto & i: samples)
    i = dist(gen);
//do something with your samples...

हालाँकि, ध्यान दें कि c ++ 11 discrete_distributionसभी संचयी योगों को आरंभीकरण पर गणना करता है। आमतौर पर, आप चाहते हैं कि क्योंकि यह एक समय ओ (एन) लागत के लिए नमूना समय को गति देता है। लेकिन तेजी से बदलते वितरण के लिए यह एक भारी गणना (और स्मृति) खर्च करेगा। उदाहरण के लिए यदि वेट ने दर्शाया कि कितने आइटम हैं और हर बार जब आप एक ड्रॉ करते हैं, तो आप इसे हटा देते हैं, आप शायद एक कस्टम एल्गोरिदम चाहते हैं।

विल का जवाब https://stackoverflow.com/a/1761646/837451 इस ओवरहेड से बचता है लेकिन C ++ 11 की तुलना में ड्रॉ करने के लिए धीमा होगा क्योंकि यह बाइनरी सर्च का उपयोग नहीं कर सकता है।

यह देखने के लिए कि यह ऐसा करता है, आप संबंधित लाइनें ( /usr/include/c++/5/bits/random.tccमेरे Ubuntu 16.04 + GCC 5.3 स्थापित पर) देख सकते हैं :

  template<typename _IntType>
    void
    discrete_distribution<_IntType>::param_type::
    _M_initialize()
    {
      if (_M_prob.size() < 2)
        {
          _M_prob.clear();
          return;
        }

      const double __sum = std::accumulate(_M_prob.begin(),
                                           _M_prob.end(), 0.0);
      // Now normalize the probabilites.
      __detail::__normalize(_M_prob.begin(), _M_prob.end(), _M_prob.begin(),
                            __sum);
      // Accumulate partial sums.
      _M_cp.reserve(_M_prob.size());
      std::partial_sum(_M_prob.begin(), _M_prob.end(),
                       std::back_inserter(_M_cp));
      // Make sure the last cumulative probability is one.
      _M_cp[_M_cp.size() - 1] = 1.0;
    }

10

जब मुझे वजन करने की आवश्यकता होती है तो मैं वजन के लिए एक यादृच्छिक संख्या का उपयोग कर रहा हूं।

उदाहरण के लिए: मुझे निम्न वज़न के साथ 1 से 3 तक यादृच्छिक संख्याएँ उत्पन्न करने की आवश्यकता है:

  • एक यादृच्छिक संख्या का 10% 1 हो सकता है
  • एक यादृच्छिक संख्या का 30% 2 हो सकता है
  • एक यादृच्छिक संख्या का 60% 3 हो सकता है

तब मैं उपयोग करता हूं:

weight = rand() % 10;

switch( weight ) {

    case 0:
        randomNumber = 1;
        break;
    case 1:
    case 2:
    case 3:
        randomNumber = 2;
        break;
    case 4:
    case 5:
    case 6:
    case 7:
    case 8:
    case 9:
        randomNumber = 3;
        break;
}

इसके साथ, यादृच्छिक रूप से इसमें 10% संभावनाएं 1, 30% 2 और 60% 3 होनी चाहिए।

आप अपनी आवश्यकताओं के रूप में इसके साथ खेल सकते हैं।

आशा है कि मैं आपकी मदद कर सकता हूं, गुड लक!


यह वितरण को गतिशील रूप से समायोजित करने का नियम है।
जोश सी

2
हैकी लेकिन मुझे यह पसंद है। एक त्वरित प्रोटोटाइप के लिए अच्छा है जहाँ आप कुछ मोटा भार चाहते हैं।
आकर्षित किया

1
यह केवल तर्कसंगत वजन के लिए काम करता है। आपको 1 / pi वजन के साथ ऐसा करने में कठिन समय होगा;)
यूसुफ बुडिन

1
@JosephBudin फिर, आप कभी भी एक तर्कहीन वजन नहीं कर पाएंगे। फ्लोट वेट के लिए ~ ~ 4.3 बिलियन केस स्विच को ठीक करना चाहिए। : D
जेसन C

1
सही @JasonC, समस्या अब छोटी है, लेकिन अभी भी एक समस्या है;)
जोसेफ बुडिन

3

उन सभी वस्तुओं का एक थैला (या std :: वेक्टर) बनाएँ, जिन्हें उठाया जा सकता है।
सुनिश्चित करें कि प्रत्येक आइटम की संख्या आपके भार के अनुपात में है।

उदाहरण:

  • 1 60%
  • 2 35%
  • 3 5%

तो 60 1, 35 2 और 5 3 के साथ 100 वस्तुओं के साथ एक बैग है।
अब बेतरतीब ढंग से बैग को छाँटें (std :: random_shuffle)

बैग से तत्वों को क्रमिक रूप से तब तक उठाएं जब तक वह खाली न हो।
एक बार खाली बैग को फिर से रेंडमाइज करें और फिर से शुरू करें।


6
यदि आपके पास लाल और नीले रंग के पत्थर का एक बैग है और आप उसमें से एक लाल संगमरमर का चयन करते हैं और इसे प्रतिस्थापित नहीं करते हैं तो क्या यह एक और लाल संगमरमर का चयन करने की संभावना है? उसी तरह, आपका कथन "बैग से तत्वों को क्रमिक रूप से तब तक उठाएं जब तक वह खाली न हो" उद्देश्य से पूरी तरह से अलग वितरण पैदा करता है।
ldog

@ बुजुर्ग: मुझे आपका तर्क समझ में आ रहा है, लेकिन हम उस सच्चे यादृच्छिकता की तलाश नहीं कर रहे हैं जिसे हम किसी विशेष वितरण के लिए देख रहे हैं। यह तकनीक सही वितरण की गारंटी देती है।
मार्टिन यॉर्क

4
मेरा कहना यह है कि मेरे पिछले तर्क से, आप वितरण को सही ढंग से प्रस्तुत नहीं करते हैं। सरल काउंटर उदाहरण पर विचार करें, कहते हैं कि आपके पास 1,2,2समय की 1 1/3 और 2 2/3 के उत्पादन के रूप में 3 की एक सरणी है । सरणी को रैंडम करें, पहले चुनें, 2 कहने दें, अब अगला तत्व जो आप चुनते हैं वह 1 1/2 समय और 2 1/2 समय का वितरण है। प्रेमी?
ldog

0

[0,1) पर एक यादृच्छिक संख्या चुनें, जो एक बढ़ावा देने वाले आरएनजी के लिए डिफ़ॉल्ट ऑपरेटर () होना चाहिए। आइटम को संचयी संभाव्यता घनत्व फ़ंक्शन> = उस संख्या के साथ चुनें:

template <class It,class P>
It choose_p(It begin,It end,P const& p)
{
    if (begin==end) return end;
    double sum=0.;
    for (It i=begin;i!=end;++i)
        sum+=p(*i);
    double choice=sum*random01();
    for (It i=begin;;) {
        choice -= p(*i);
        It r=i;
        ++i;
        if (choice<0 || i==end) return r;
    }
    return begin; //unreachable
}

जहां random01 () एक डबल> = 0 और <1 देता है। ध्यान दें कि उपरोक्त को 1 की राशि के लिए संभावनाओं की आवश्यकता नहीं है; यह आपके लिए उन्हें सामान्य बनाता है।

p सिर्फ एक ऐसा कार्य है जो संग्रह में एक आइटम के लिए एक संभावना प्रदान करता है [आरंभ, अंत)। आप इसे छोड़ सकते हैं (या किसी पहचान का उपयोग कर सकते हैं) यदि आपके पास बस संभावनाओं का एक क्रम है।


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