БлогNot. Генерируем случайные числа по "карте вероятностей"

Генерируем случайные числа по "карте вероятностей"

В продолжение темы - ситуация, когда дано соответствие между набором поименованных элементов и их требуемыми вероятностями появления, только здесь нам уже не нужна предельная "экономичность" алгоритма.

Сумма всех вероятностей должна равняться единице. Используются стандартные средства STL и библиотеки алгоритмов, делается миллион тестов и полученная картина сравнивается с теоретической. Так как используется арифметика с плавающей запятой, возможны ошибки округления.

Применять, как делается в миллионах образцов, srand(time(0)) и затем rand(), которая возвращает число 0 до RAND_MAX, обычно равного всего-то 32767, сегодня, думаю, уже не надо, а лучше функции из <random>. Показано, как при их применении простейшим образом с помощью random_device проинициализировать генератор случайных чисел, чтобы цепочки чисел получались каждый раз разные.

Код проверялся в консоли Visual Studio 2019. Он приведён далее, а после кода - образец вывода программы в консоль.

#include <iostream>
#include <iomanip>
#include <vector>
#include <random>
using namespace std;

typedef vector <pair<string, double> >::const_iterator probabilitiesConstIterator;
typedef vector <pair<string, double> > probabilitiesType;

int main() {
 probabilitiesType probabilities;
 probabilities.push_back(make_pair("half", 1/2.0));
 probabilities.push_back(make_pair("fourth", 1/4.0));
 probabilities.push_back(make_pair("alfa", 1/5.0));
 probabilities.push_back(make_pair("magic", 1 / 20.0));
 
 vector <string> generatedNames;
 vector <int> decider; //"решалка"
 int seed = 65536; //число, по модулю которого нормируем
 for (int i = 0; i < probabilities.size(); i++) {
  if (i == 0) {
   decider.push_back(seed * (probabilities[i].second));
  }
  else {
   int number = 0;
   for (int j = 0; j < i; j++) {
    number += seed * (probabilities[j].second);
   }
   number += seed * probabilities[i].second;
   decider.push_back(number);
  }
 }
 int testsCount = 1e6; //тестов
 random_device rd;
 default_random_engine generator(rd());
 uniform_int_distribution <int> distribution(0, seed-1);

 for (int i = 0; i < testsCount; i++) {
  int randnumber = distribution(generator) % seed;
  int j = 0;
  while (randnumber > decider[j]) j++;
  generatedNames.push_back((probabilities[j]).first);
 }
 cout << "letter  frequency real  frequency expected\n";
 for (probabilitiesConstIterator i = probabilities.begin(); i != probabilities.end(); i++) {
  cout << left << setw(8) << i->first;
  int found = count(generatedNames.begin(), generatedNames.end(), i->first);
  cout << left << setw(16) << found / (double)testsCount
       << left << setw(16) << i->second << endl;
 }
 return 0;
}
letter  frequency real  frequency expected
half    0.499881        0.5
fourth  0.249828        0.25
alfa    0.200352        0.2
magic   0.049939        0.05

теги: алгоритм c++ random

13.06.2020, 00:42; рейтинг: 126