가중 난수 랜덤 함수를

가중치가있는 난수를 구현하려고합니다. 나는 현재 벽에 머리를 부딪 히고 있으며 이것을 알아낼 수 없습니다.

내 프로젝트 (Hold’em hand-ranges, 주관적인 올인 에퀴티 분석)에서는 Boost의 랜덤 함수를 사용하고 있습니다. 따라서 1과 3 사이의 임의의 숫자를 선택한다고 가정 해 봅시다 (1, 2 또는 3). Boost의 메르 센 트위스터 생성기는 이것에 대한 매력처럼 작동합니다. 그러나 다음과 같이 선택에 가중치를 부여하고 싶습니다.

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

Boost에 이것에 대한 일종의 기능이 있습니까?



답변

무작위로 항목을 선택하는 간단한 알고리즘이 있으며 항목에는 개별 가중치가 있습니다.

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");

이것은 부스트 ​​컨테이너 등에 쉽게 적용 할 수 있어야합니다.


가중치가 거의 변경되지 않지만 종종 무작위로 하나를 선택하고 컨테이너가 객체에 대한 포인터를 저장하거나 수십 개의 항목을 초과하는 한 (기본적으로 이것이 도움이되는지 방해 하는지를 알기 위해 프로파일 링해야합니다) , 최적화가 있습니다.

각 항목에 누적 가중치 합계를 저장하면 이진 검색 을 사용 하여 선택 가중치에 해당하는 항목을 선택할 수 있습니다 .


목록의 항목 수를 모르는 경우 가중치를 적용 할 수있는 저수지 샘플링 이라는 매우 깔끔한 알고리즘 이 있습니다.


답변

이전 질문에 대한 답변을 업데이트했습니다. std :: lib만으로 C ++ 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로 나누어야합니다.


답변

가중치가 그려지는 것보다 더 느리게 변경되면 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은 초기화시 모든 누적 합계를 계산합니다. 일반적으로 한 번의 O (N) 비용으로 샘플링 시간을 단축하기 때문에이를 원합니다. 그러나 급변하는 배포의 경우 계산 (및 메모리) 비용이 많이 듭니다. 예를 들어 가중치가 항목 수를 나타내며 그릴 때마다 항목을 제거하면 사용자 지정 알고리즘이 필요할 것입니다.

Will의 답변 https://stackoverflow.com/a/1761646/837451 은이 오버 헤드를 피하지만 이진 검색을 사용할 수 없기 때문에 C ++ 11보다 가져 오는 속도가 느립니다.

이 작업을 수행하는지 확인하려면 관련 줄을 볼 수 있습니다 ( /usr/include/c++/5/bits/random.tccUbuntu 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;
    }

답변

가중치를 부여해야 할 때 내가하는 일은 가중치에 임의의 숫자를 사용하는 것입니다.

예를 들어 다음과 같은 가중치로 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;
}

이것으로 무작위로 1이 될 확률의 10 %, 2가 될 30 %, 3이 될 확률이 60 %입니다.

필요에 따라 사용할 수 있습니다.

내가 당신을 도울 수 있기를 바랍니다, 행운을 빕니다!


답변

집을 수있는 모든 항목의 가방 (또는 std :: vector)을 만드십시오.
각 항목의 수가 가중치에 비례하는지 확인하십시오.

예:

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

그러니 1이 60 개, 2가 35 개, 3이 5 개가 들어있는 100 개의 항목이있는 가방을 만드세요.
이제 무작위로 가방을 정렬합니다 (std :: random_shuffle).

가방이 비워 질 때까지 순차적으로 요소를 선택합니다.
비워지면 가방을 다시 무작위로 추출하고 다시 시작하십시오.


답변

부스트 RNG의 기본 operator () 여야하는 [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 ()은 double> = 0 및 <1을 반환합니다. 위의 경우 확률의 합이 1이 될 필요는 없습니다. 그것은 당신을 위해 그들을 정상화합니다.

p는 모음 [begin, end)의 항목에 확률을 할당하는 함수입니다. 일련의 확률이있는 경우 생략 (또는 ID 사용) 할 수 있습니다.


답변

몇 가지 간단한 가중치 무작위 알고리즘을 구현했습니다 .