c++ 加权随机数

omqzjyyz  于 2023-01-18  发布在  其他
关注(0)|答案(9)|浏览(248)

我正在尝试实现一个加权随机数。我目前只是把我的头撞到墙上,不能弄清楚这一点。
在我的项目中(Hold'em hand-range,主观全入权益分析),我使用Boost的random -函数。假设我想在1和3之间选择一个随机数(1、2或3)。Boost的mersenne twister生成器在这方面很有魅力。但是,我希望选择的权重如下:
Boost是否具有某种功能?

a11xaf1n

a11xaf1n1#

有一个简单的算法用于随机挑选物品,其中物品具有各自的权重:
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");

这应该是直接适应您的升压容器等。
如果你的权重很少改变,但你经常随机选择一个,并且只要你的容器存储指向对象的指针,或者长度超过几十个项目(基本上,你必须分析以知道这是帮助还是阻碍),那么就有一个优化:
通过在每个项目中存储累计重量总和,您可以使用binary search来挑选与挑选重量对应的项目。
如果你不知道列表中的项目数,那么有一个非常简洁的算法,叫做reservoir sampling,它可以用来加权。

nhhxz33t

nhhxz33t2#

更新了一个老问题的答案。您可以在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,因为这是请求的总和。

2j4z5cfb

2j4z5cfb3#

如果权重的变化比绘制的要慢,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...

注意,c11 discrete_distribution在初始化时计算所有的累积和,通常,你需要这样做是因为它加快了一次O的采样时间(N)成本。但对于快速变化的分布,这将招致繁重的计算(和内存)成本。例如,如果权重表示有多少项目,每次你画一个,你删除它,你可能会想要一个自定义的算法。
Will的答案https://stackoverflow.com/a/1761646/837451避免了这种开销,但是由于它不能使用二进制搜索,所以提取速度比C
11慢。
要了解它是如何做到这一点的,您可以查看相关行(在我的Ubuntu 16.04 + GCC 5.3安装中为/usr/include/c++/5/bits/random.tcc):

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;
    }
brqmpdu1

brqmpdu14#

当我需要给数字加权的时候,我会用一个随机数作为权重。
例如:我需要生成从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。
你可以根据需要玩它。
希望我能帮到你,祝你好运!

wmtdaxz3

wmtdaxz35#

为所有可以拾取的项目构建一个包(或std::vector)。
确保每个项目的数量与您的权重成比例。
示例:

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

因此,有一个袋子有100件物品,其中60件1,35件2和5件3。
现在对行李进行随机排序(std::random_shuffle)
按顺序从袋子中取出元件,直到袋子空了。
空袋后,重新随机分配袋子并重新开始。

r6hnlfcb

r6hnlfcb6#

在[0,1)上选择一个随机数,它应该是提升RNG的默认运算符()。选择累积概率密度函数〉=该数的项目:

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
}

其中random 01()返回一个双精度值〉=0且〈1。注意,上面的例子并不要求概率之和为1;它会让你正常化。
p只是一个为集合[开始,end]中的某个项指定概率的函数,如果只有概率序列,可以省略它(或使用恒等式)。

5w9g7ksd

5w9g7ksd7#

这是我对"加权随机数"的理解,我最近一直在使用它。(代码用Python编写,但也可以用其他语言实现)
假设你想随机挑选一个人,但他们被选中的机会并不相等。你可以给每个人一个"权重"或"机会"值:

choices = [("Ade", 60), ("Tope", 50), ("Maryamu", 30)]

您可以使用它们的权重来计算每个选项的得分,然后找到得分最高的选项

highest = [None, 0]
for p in choices:
    score = math.floor(random.random() * p[1])
    if score > highest[1]:
        highest[0] = p
        highest[1] = score

print(highest)

对于Ade来说,他们能得到的最高分是60分,最高分是50分,以此类推,这意味着Ade比其他人有更高的机会得到最高分。
您可以使用任何范围的权重,差异越大,分布的偏斜程度越大。例如,如果Ade的权重为1000,则几乎总是选择他们。

测试

一个二个一个一个
问题
看起来选民越多,结果就越容易预测。
希望这能给人一个想法...

oaxa6hgo

oaxa6hgo8#

我刚刚通过“will“实现了给定的解决方案

#include <iostream>
#include <map>

using namespace std;

template < class T >
class WeightedRandomSample
{
public:
    void SetWeigthMap( map< T , unsigned int >& WeightMap )
    {
        m_pMap = &WeightMap;
    }
    
    T GetRandomSample()
    {
        unsigned int sum_of_weight = GetSumOfWeights();
        unsigned int rnd = (rand() % sum_of_weight);
        map<T , unsigned int>& w_map = *m_pMap;
        typename map<T , unsigned int>::iterator it;
        for(it = w_map.begin() ; it != w_map.end() ; ++it )
        {
            unsigned int w = it->second;
            if(rnd < w)
                return (it->first);
            rnd -= w;
        }
        //assert(!"should never get here");
        T* t = NULL;
        return *(t);
    }
    
    unsigned int GetSumOfWeights()
    {
        if(m_pMap == NULL)
            return 0;
        unsigned int sum = 0;
        map<T , unsigned int>& w_map = *m_pMap;
        typename map<T , unsigned int>::iterator it;
        
        for(it = w_map.begin() ; it != w_map.end() ; ++it )
        {
            sum += it->second;
        }
        return sum;
    }

    
protected:
    map< T , unsigned int>* m_pMap = NULL;
    
};

typedef pair<int , int> PAIR_INT_INT;
typedef map<PAIR_INT_INT ,unsigned int> mul_table_weighted_map;

int main()
{
    
    mul_table_weighted_map m;
    m[PAIR_INT_INT(2,3)] = 10;
    m[PAIR_INT_INT(4,5)] = 20;
    m[PAIR_INT_INT(2,5)] = 10;
    
    WeightedRandomSample<PAIR_INT_INT> WRS;
    WRS.SetWeigthMap(m);
    unsigned int sum_of_weight = WRS.GetSumOfWeights();
    cout <<"Sum of weights : " << sum_of_weight << endl;
    
    unsigned int number_of_test = 10000;
    cout << "testing " << number_of_test << " ..." << endl;
    map<PAIR_INT_INT , unsigned int> check_map;
    for(int i = 0 ; i < number_of_test ; i++)
    {
        PAIR_INT_INT res = WRS.GetRandomSample();
        check_map[res]++;
        //cout << i+1 << ": random = " << res.first << " * " << res.second << endl;
    }
    cout << "results: " << endl;
    
    for(auto t : check_map)
    {
        PAIR_INT_INT p = t.first;
        unsigned int expected = (number_of_test * m[p]) / sum_of_weight;
        cout << " pair " << p.first << " * " << p.second 
            << ", counted = " << t.second
            << ", expected = " << expected
            << endl;
    }

    return 0;
}
x4shl7ld

x4shl7ld9#

例如,在用于该指数的权重向量中生成随机指数可以这样完成:

#include <bits/stdc++.h> 
using namespace std;

int getWeightedRandomNumber(vector<int> weights){
  vector<int> vec;
  for(int i=0; i<weights.size(); i++){
    for(int j=0; j<weights[i]; j++){
      vec.push_back(i);
    }
  }
  random_shuffle(vec.begin(), vec.end());
  return vec.front();
}

int main() 
{
  vector<int> v{2,4,5,100,1,2,4,4};
  for(int i=0; i<100; i++){
    cout<<getWeightedRandomNumber(v)<<endl;
  }
  
}

由于我们使用(no of elements) = almost (current no of elements) * (mean weight)构造另一个向量,因此这种方法现在可能适用于处理大数据。

相关问题