Вопрос слишком широкий для полного ответа, но позвольте мне выделить пару интересных моментов:
Почему "одинаково вероятно"
Предположим, у вас есть простой генератор случайных чисел, который генерирует числа 0, 1, ..., 10 каждое с равной вероятностью (считайте это классическим rand()
). Теперь вам нужно случайное число в диапазоне 0, 1, 2, каждое с равной вероятностью. Ваша реакция коленного рефлекса - принять rand() % 3
. Но подождите, остатки 0 и 1 встречаются чаще, чем остаток 2, так что это неверно!
Вот почему нам нужны правильные распределения , которые берут источник однородных случайных целых чисел и превращают их в желаемое распределение, как Uniform[0,2]
в примере. Лучше оставить это хорошей библиотеке!
Двигатели
Таким образом, в основе всей случайности лежит хороший генератор псевдослучайных чисел, который генерирует последовательность чисел, равномерно распределенных в определенном интервале и в идеале имеющих очень длинный период. Стандартная реализация rand()
часто не самая лучшая, и поэтому хорошо иметь выбор. Линейно-конгруэнтный и твистер Мерсенна - два хороших выбора (LG на самом деле часто используетсяrand()
тоже ); опять же, хорошо позволить библиотеке справиться с этим.
Как это устроено
Легко: сначала установите двигатель и засевайте его. Начальное число полностью определяет всю последовательность «случайных» чисел, поэтому: а) используйте /dev/urandom
каждый раз другое (например, взятое из ), и б) сохраните начальное число, если вы хотите воссоздать последовательность случайных выборов.
#include <random>
typedef std::mt19937 MyRNG; // the Mersenne Twister with a popular choice of parameters
uint32_t seed_val; // populate somehow
MyRNG rng; // e.g. keep one global instance (per thread)
void initialize()
{
rng.seed(seed_val);
}
Теперь мы можем создавать раздачи:
std::uniform_int_distribution<uint32_t> uint_dist; // by default range [0, MAX]
std::uniform_int_distribution<uint32_t> uint_dist10(0,10); // range [0,10]
std::normal_distribution<double> normal_dist(mean, stddeviation); // N(mean, stddeviation)
... И используйте движок для создания случайных чисел!
while (true)
{
std::cout << uint_dist(rng) << " "
<< uint_dist10(rng) << " "
<< normal_dist(rng) << std::endl;
}
Параллелизм
Еще одна важная причина, по которой следует предпочесть <random>
традиционное, rand()
заключается в том, что теперь очень ясно и очевидно, как сделать генерацию случайных чисел потокобезопасной: либо предоставить каждому потоку свой собственный, локальный для потока движок, засеянный на локальном потоке, либо синхронизировать доступ к объекту двигателя.
Разное
- Интересная статья на TR1 случайным образом на CodeGuru.
- В Википедии есть хорошее резюме (спасибо, @Justin).
- В принципе, каждый движок должен typedef a
result_type
, который является правильным интегральным типом для использования в качестве начального числа. Я думаю , что у меня был глючная реализация однажды что заставило меня заставить семя , std::mt19937
чтобы uint32_t
на x64, в конце концов , это должно быть исправлено , и вы можете сказать , MyRNG::result_type seed_val
и , таким образом , сделать двигатель очень легко заменить.
rand
, вам следует быстро взглянуть на Википедию, чтобы узнать о некоторых основных понятиях статистики и ГСЧ, иначе будет очень сложно объяснить вам смысл<random>
и использование различных его частей.