生成具有给定概率的随机数(C++)

Generating random numbers with given probabilities in C++

本文关键字:随机数 C++ 概率      更新时间:2023-10-16

当得到 0 的概率为 10%,1 为 7% 并且获得其余数字的概率相等时,如何使用rand()函数在 C++ 中生成一个介于 0 和 100 之间的随机数?

rand()函数可能是完成这项工作的错误工具。自 C++11 年以来C++有了新的(伪(随机数生成工具: http://en.cppreference.com/w/cpp/numeric/random 这些工具中有一个 RandomNumberDistribution 的概念,其中一个分布由 std::d iscrete_distribution 提供。std::d iscrete_distribution 允许您定义分布中数字的权重,以便不同的数字可以具有不同的预定义概率。

#include <iostream>
#include <map>
#include <random>
#include <cmath>
int main()
{
std::random_device r;
std::default_random_engine e1(r());
// Distribution that defines different weights (17, 10, etc.) for numbers.
std::discrete_distribution<int> discrete_dist({17, 10, 5, 3, 1});
// The map keeps track of number of occurences for each value.
std::map<int, int> histogram;
for (int n = 0; n < 100; ++n) {
++histogram[std::round(discrete_dist(e1))];
}
std::cout << "Distribution:n";
for (auto p : histogram) {
std::cout << p.first << ' ' << p.second << 'n';
}
}

输出可以是这样的:

Distribution:
0 51
1 23
2 13
3 9
4 4

您可以使用 std::uniform_real_distribution 以及间隔表来执行此类查找。

我们可以做的是生成一个从 0.0 到 1.0 的数字,并使用一种查找表将其映射到整数空间中。

例如:

#include <random>
#include <vector>
#include <iostream>
#include <cassert>
#include <algorithm>
#include <map>
struct TableEntry
{
double upper_bound = 0.0f;
int value = 0;
//for std::is_sorted, std::Upper_bound;
bool operator<(TableEntry const& other) const
{
return this->upper_bound < other.upper_bound;
}
};
struct Generator
{
Generator():
gen(std::random_device()()), //seed the random number generator
dis(0.0, 1.0) //we want a value between - and 1
{
break_table.push_back({0.1, 0}); //10 percent probability of yielding 0
break_table.push_back({0.17, 1}); // (0.17 - 0.1) == 7% chance of yielding 1
//fill in the rest of the table uniformly
double step = (1.0 - 0.17)/98; //98 values remaining, distribute equally
for(int i = 2; i < 100; i++)
{
break_table.push_back({0.17 + (i-1)*step, i});
}
assert(std::is_sorted(begin(break_table), end(break_table)));
assert(abs(break_table.back().upper_bound - 1.0) < 0.00001);
}
//call the object as a function to get a value
int operator()()
{
double bval = dis(gen); //bval is in interval [0, 1);
// std::upper_bound returns an iterator to the first value bigger than bval
auto break_entry = std::upper_bound(begin(break_table), end(break_table), TableEntry{bval, 0});
//special case: we're off the end of the scale
if(break_entry == end(break_table)) return break_table.back().value;
else return break_entry->value;
};
private:
std::vector<TableEntry> break_table;
std::mt19937 gen;
std::uniform_real_distribution<double> dis;
};


int main()
{
Generator g;
//verify the probabilities
const size_t N = 10'000'000;
std::map<int, size_t> number_counts; // keep track of occurences of each number
for(size_t i = 0; i < N; i++)
{
int v = g();
number_counts[v]++;
}
for(auto const& kv: number_counts)
{
double p = static_cast<double>(kv.second) / N;
std::cout << kv.first << " : " << p * 100.0 << "%n";
}
return 0;
}

运行此代码后,我得到以下发行版:

0 : 10.0069%
1 : 7.01544%
2 : 0.84899%
3 : 0.84358%
4 : 0.8462%
5 : 0.84468%
6 : 0.85324%
7 : 0.85023%
8 : 0.85003%
9 : 0.84862%
...
99 : 0.84219%

。看起来还不错。