ওজনযুক্ত এলোমেলো সংখ্যা


104

আমি একটি ভারী র্যান্ডম সংখ্যা বাস্তবায়নের চেষ্টা করছি। আমি বর্তমানে কেবল আমার মাথাটি প্রাচীরের বিপরীতে বেঁধে দিচ্ছি এবং এটি বের করতে পারছি না।

আমার প্রকল্পে (হোল্ড'ম হ্যান্ড রেঞ্জ, সাবজেক্টিভ অল-ইন ইক্যুইটি বিশ্লেষণ), আমি বুস্টের এলোমেলো-ফাংশন ব্যবহার করছি। সুতরাং, আসুন আমি 1 এবং 3 এর মধ্যে একটি এলোমেলো সংখ্যা বাছাই করতে চাই (সুতরাং 1, 2 বা 3)। বুস্টের মেরস্নেন টুইস্টার জেনারেটর এটির জন্য কবিতার মতো কাজ করে। যাইহোক, আমি চাই উদাহরণটি উদাহরণস্বরূপ উদাহরণস্বরূপ:

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

বুস্টের কি এর জন্য কোনও ধরণের কার্যকারিতা রয়েছে?

উত্তর:


182

এলোমেলোভাবে কোনও আইটেম বাছাইয়ের জন্য একটি সরল অ্যালগরিদম রয়েছে, যেখানে আইটেমগুলির পৃথক ওজন থাকে:

1) সমস্ত ওজনের যোগফল গণনা করুন

২) একটি এলোমেলো সংখ্যা বেছে নিন যা 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");

আপনার বুস্ট পাত্রে এবং এই জাতীয়গুলির সাথে খাপ খাইয়ে নিতে এটি সোজা হওয়া উচিত।


যদি আপনার ওজন খুব কমই পরিবর্তিত হয় তবে আপনি প্রায়শই এলোমেলোভাবে একটি বেছে নিয়েছেন এবং যতক্ষণ না আপনার ধারক বস্তুগুলিতে পয়েন্টার জমা রাখছে বা কয়েক ডজন আইটেমের বেশি লম্বা হবে (মূলত, এটি আপনাকে সহায়তা করে বা বাধা দেয় কিনা তা জানতে আপনাকে প্রোফাইল করতে হবে) , তারপরে একটি অপ্টিমাইজেশন রয়েছে:

প্রতিটি আইটেমে সংযোজিত ওজন যোগ করে সংরক্ষণ করে আপনি পিক ওজনের সাথে সম্পর্কিত আইটেমটি বাছাই করতে বাইনারি অনুসন্ধান ব্যবহার করতে পারেন ।


আপনি যদি তালিকার আইটেমের সংখ্যা জানেন না, তবে জলাশয়ের স্যাম্পলিং নামে একটি খুব ঝরঝরে অ্যালগরিদম রয়েছে যা ভারী হওয়ার জন্য অভিযোজিত হতে পারে।


4
একটি অপ্টিমাইজেশন হিসাবে আপনি ক্রমযুক্ত ওজন ব্যবহার করতে এবং একটি বাইনারি অনুসন্ধান ব্যবহার করতে পারেন। তবে কেবল তিনটি পৃথক মানের জন্য এটি সম্ভবত ওভারকিল।
বিক্রয়বিটজে

4
আমি ধরে নিয়েছি যখন আপনি "ক্রম" বলবেন তখন আপনি পছন্দ-ওজনের অ্যারেতে পূর্বনির্ধারিত পদক্ষেপটি উদ্দেশ্যমূলকভাবে বাদ দিচ্ছেন, হ্যাঁ?
সাইলেন্টডাইর্জ

4
অরিস, অ্যারে বাছাই করার দরকার নেই। আমি আমার ভাষা পরিষ্কার করার চেষ্টা করেছি।
উইল

4
@ উইল: হ্যাঁ, তবে একই নামের একটি অ্যালগরিদম রয়েছে। 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

4
ভবিষ্যতের পাঠকদের জন্য দ্রষ্টব্য: আপনার এলোমেলো সংখ্যা থেকে ওজন বিয়োগ করা অংশটি উপেক্ষা করা সহজ, তবে অ্যালগরিদমের পক্ষে অত্যন্ত গুরুত্বপূর্ণ (আমি তাদের মন্তব্যে @ কোবিকের মতো ফাঁদে পড়েছি)।
ফ্র্যাঙ্ক স্মিট

48

একটি পুরানো প্রশ্নের আপডেট উত্তর। আপনি সহজেই এসটিডি :: লাইব দিয়ে সি ++ 11 এ এটি করতে পারেন:

#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 দ্বারা ভাগ করতে হবে কারণ অনুরোধগুলি এতে যোগ করে।


এই উদাহরণটি সংকলনের জন্য কেবল একটি অনুস্মারক নোট: সি ++ 11 অর্থাত্ প্রয়োজন। জিসিসি ৪.6 থেকে পাওয়া যাবে -std = c ++ 0x সংকলক পতাকা ব্যবহার করুন।
পিট 855217

4
সমস্যাটি সমাধান করার জন্য প্রয়োজনীয় প্রয়োজনীয় অংশগুলি বেছে নেওয়ার জন্য যত্নশীল?
জনি

4
এটি সেরা উত্তর, তবে আমি মনে করি std::discrete_distributionপরিবর্তে std::piecewise_constant_distributionএটি আরও ভাল হত।
ড্যান

4
@ ড্যান, হ্যাঁ, এটি করার আরও একটি দুর্দান্ত উপায়। আপনি যদি এটি কোড করে থাকেন এবং এর সাথে উত্তর দেন, আমি এটির পক্ষে ভোট দেব। আমি মনে করি কোডটি আমার উপরে যা আছে তার থেকে বেশ অনুরূপ হতে পারে। আপনাকে কেবল উত্পন্ন আউটপুটটিতে একটি যুক্ত করতে হবে। এবং বিতরণ ইনপুট সহজ হবে। এই ক্ষেত্রে উত্তরগুলির তুলনা / বিপরীতে সেট পাঠকদের পক্ষে মূল্যবান হতে পারে।
হাওয়ার্ড হিন্যান্ট

15

যদি আপনার ওজনগুলি আঁকার তুলনায় আরও ধীরে ধীরে পরিবর্তন হয় তবে সি ++ 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...

তবে নোট করুন, সি ++ 11 discrete_distributionসূচনাতে সমস্ত ক্রমসংখ্যার অঙ্কগুলি গণনা করে। সাধারণত, আপনি এটি চান কারণ এটি এক সময়ের ও (এন) ব্যয়ের জন্য স্যাম্পলিংয়ের সময়ের গতি বাড়ায়। তবে দ্রুত পরিবর্তনের জন্য এটির জন্য একটি ভারী গণনা (এবং স্মৃতি) ব্যয় করতে হবে। উদাহরণস্বরূপ, যদি ওজন সেখানে কতগুলি আইটেম রয়েছে তা উপস্থাপন করে এবং প্রতিবার আপনি একটি আঁকেন, আপনি এটি সরিয়ে ফেলেন, আপনি সম্ভবত একটি কাস্টম অ্যালগরিদম চাইবেন।

উইলের উত্তর https://stackoverflow.com/a/1761646/837451 এই ওভারহেড এড়িয়ে চলে তবে সি ++ 11 এর থেকে আঁকতে ধীর হবে কারণ এটি বাইনারি অনুসন্ধান ব্যবহার করতে পারে না।

এটি এটি করে তা দেখতে আপনি প্রাসঙ্গিক লাইনগুলি দেখতে পাবেন ( /usr/include/c++/5/bits/random.tccআমার উবুন্টু 16.04 + জিসিসি 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 এবং 3% হতে 60%।

আপনি আপনার প্রয়োজন হিসাবে এটি খেলতে পারেন।

আশা করি আমি আপনাকে সাহায্য করতে পারি, শুভকামনা!


এটি গতিশীলভাবে বিতরণকে সামঞ্জস্য করে rules
জোশ সি

4
হ্যাকি তবে আমি এটি পছন্দ করি। আপনি কিছুটা ভারী করতে চান এমন দ্রুত প্রোটোটাইপের জন্য দুর্দান্ত।
ড্রয়িশ করুন

4
এটি কেবল যুক্তিযুক্ত ওজনের জন্য কাজ করে। আপনার 1 / পাই ওজনের সাহায্যে এটি করতে খুব কঠিন সময় হবে;)
জোসেফ বুডিন

4
@ জোসেফবুদিন আবার, আপনি কখনই অযৌক্তিক ওজন রাখতে সক্ষম হবেন না। একটি ~ 4.3 বিলিয়ন কেস সুইচটি ভাসমান ওজনের জন্য ঠিক জরিমানা করা উচিত। : ডি
জেসন সি

4
ডান @ জেসনসি, সমস্যাটি এখন অসীম ছোট তবে এখনও একটি সমস্যা;)
জোসেফ বুডিন

3

বাছাই করা যায় এমন সমস্ত আইটেমের একটি ব্যাগ (বা স্ট্যান্ড :: ভেক্টর) তৈরি করুন।
নিশ্চিত করুন যে প্রতিটি আইটেমের সংখ্যা আপনার ওজনের সাথে সমানুপাতিক।

উদাহরণ:

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

সুতরাং 60 1, 35 2 এবং 5 3 এর সাথে 100 আইটেম সহ একটি ব্যাগ রাখুন।
এখন এলোমেলোভাবে ব্যাগটি বাছাই করুন (স্ট্যান্ডার্ড :: এলোমেলো_স্যাফল)

এটি খালি না হওয়া পর্যন্ত ধারাবাহিকভাবে উপাদান থেকে উপাদানগুলি চয়ন করুন।
খালি একবার ব্যাগটিকে এলোমেলো করে আবার শুরু করুন।


6
যদি আপনি লাল এবং নীল মার্বেল একটি ব্যাগ আছে এবং আপনি এটি থেকে একটি লাল মার্বেল নির্বাচন করুন ও না প্রতিস্থাপন এটা অন্য লাল মার্বেল এখনও একই নির্বাচন সম্ভাবনা রয়েছে? একইভাবে, আপনার বিবৃতি "ব্যাগটি ক্রমানুসারে খালি হওয়া অবধি উপাদানগুলি চয়ন করুন" উদ্দেশ্য থেকে সম্পূর্ণ ভিন্ন বিতরণ তৈরি করে।
ldog

@ ওয়েলডোগ: আমি আপনার যুক্তি বুঝতে পেরেছি তবে আমরা একটি সত্য বন্টন খুঁজছি সত্যিকারের এলোমেলোতার সন্ধান করছি না। এই কৌশলটি সঠিক বিতরণের গ্যারান্টি দেয়।
মার্টিন ইয়র্ক

4
আমার বক্তব্যটি হ'ল আপনি আমার যুক্তি অনুসারে বিতরণটি সঠিকভাবে উত্পাদন করেন না। সাধারণ পাল্টা উদাহরণ বিবেচনা করুন, বলুন যে আপনি 1,2,2সময়ের 1/3 এবং 2 2/3 উত্পাদন হিসাবে আপনার 3 এর অ্যারে রয়েছে put অ্যারেটিকে এলোমেলো করে দিন, প্রথমে বাছুন, একটি 2 বলুন, এখন আপনি যে পরবর্তী উপাদানটি বেছে নিন তা 1 1/2 সময় এবং 2/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
}

যেখানে র্যান্ডম01 () ডাবল> = 0 এবং <1 প্রদান করে। নোট করুন যে উপরেরটির সম্ভাব্যতাগুলি 1 এর যোগফলের প্রয়োজন হয় না; এটি আপনার জন্য এগুলি স্বাভাবিক করে তোলে।

p সংগ্রহের একটি আইটেমকে [শুরু, শেষ) কেবলমাত্র একটি সম্ভাব্যতা নির্দিষ্ট করার জন্য একটি ফাংশন। আপনার যদি সম্ভাবনার ক্রম থাকে তবে আপনি এটিকে বাদ দিতে পারেন (বা কোনও পরিচয় ব্যবহার করুন)।


আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.