There is a two-dimensional array. It contains some values and probability as a percentage of their "loss". How to implement a function that will select a number from an array with the probability specified in it?
1 answer
Here is my decision. Only instead of a two-dimensional array, I made an array of pairs, so it’s simpler and clearer, the input c_probs contains pairs where the first value is the number to be thrown, the second is its probability, the probabilities may not be normalized ie the sum is not exactly 1. The solution is simply to calculate the distribution array, or more simply, the cumulative probability, i.e. c_distr[i + 1] = c_probs[i].second + c_distr[i] . Then just randomly throws the prob probability from the segment [0, c_distr[last]] and binary search (std :: upper_bound is used for simplicity) is index i for which c_distr[i - 1] <= prob < c_distr[i] , as a result the resulting number thrown will be c_probs[i - 1].first .
Here is the text of the program in C ++, you can run online :
#include <iostream> #include <random> #include <functional> #include <vector> #include <utility> #include <algorithm> using namespace std; enum { c_num_tests = 50, }; int main() { // Значение вероятностей чисел. vector< pair<double, double> > const c_probs = {{5.5, 0.1}, {7.1, 0.3}, {1.3, 0.05},}; // Массив кумулятивных вероятностей или попросту распределение. vector<double> c_distr(c_probs.size() + 1); for (size_t i = 0; i < c_distr.size() - 1; ++i) c_distr[i + 1] = c_probs[i].second + c_distr[i]; // Стандартный генератор чисел. std::random_device r_dev; std::default_random_engine engine(r_dev()); std::uniform_real_distribution<double> distribution(0, c_distr.back()); auto rng = std::bind(distribution, engine); // Генерируем точечные значения вероятности и определяем в какой индекс c_distr они попали. for (size_t i_test = 0; i_test < c_num_tests; ++i_test) { double prob = rng(); // Просто находит такой индекс i, что c_distr[i - 1] <= prob < c_distr[i]. int i = std::upper_bound(c_distr.begin(), c_distr.end(), prob) - c_distr.begin(); // Выводим просто число с индексом i - 1. cout << c_probs[i - 1].first << " "; } cout << endl; return 0; } As suggested by Vladimir Gamalyan, you can use the ready-made class std::discrete_distribution , it just does what I did manually above, so with it a simpler solution, you can run online :
#include <random> #include <vector> #include <utility> #include <iostream> #include <functional> using namespace std; enum { c_num_tests = 50, }; int main() { // Значение вероятностей чисел. vector< pair<double, double> > const c_probs = {{5.5, 0.1}, {7.1, 0.3}, {1.3, 0.05},}; // Сохраняем только веса. vector<double> c_weights(c_probs.size()); for (size_t i = 0; i < c_probs.size(); ++i) c_weights[i] = c_probs[i].second; // Стандартный генератор чисел. std::random_device r_dev; std::default_random_engine engine(r_dev()); std::discrete_distribution<size_t> distribution(c_weights.begin(), c_weights.end()); auto rng = std::bind(distribution, engine); // Генерируем числа с заданными весами. for (size_t i = 0; i < c_num_tests; ++i) { cout << c_probs[rng()].first << " "; } cout << endl; return 0; } - oneYou can also use std :: discrete_distribution to not write the sample yourself. - Vladimir Gamalyan
- @VladimirGamalyan Very good offer, I didn’t even know that there is a
discrete_distribution, it fits just right, I basically just implemented its analogue manually and implemented it. - Arty OneSoul
p[n]is the probability of a falling out of the numbern. Can there be a complete condition for an exact task? - Arty OneSoul