Генерация случайных чисел в C ++ 11: как генерировать, как это работает? [закрыто]


102

Недавно я наткнулся на новый способ генерации случайных чисел в C ++ 11, но не смог переварить статьи, которые я читал об этом (что это за движок , такой математический термин, как распределение , «где все полученные целые числа одинаково вероятны »).

Так может кто-нибудь объяснить

  • кто они такие?
  • что они означают?
  • как сгенерировать?
  • как они работают?
  • и т.д

Вы можете назвать все это в одном FAQ о генерации случайных чисел.


6
Спрашивать о ГСЧ, не зная, что такое распределение, все равно что спрашивать о парсерах выражений, не зная, что это за выражение ... Библиотека ГСЧ в C ++ 11 предназначена для людей, которые знают некоторую статистику и имеют большие потребности, чем плоское распределение, созданное с помощью rand, вам следует быстро взглянуть на Википедию, чтобы узнать о некоторых основных понятиях статистики и ГСЧ, иначе будет очень сложно объяснить вам смысл <random>и использование различных его частей.
Маттео Италия

26
@ Маттео: Вряд ли. Ребенок может понять, что игральная кость производит случайные числа, не понимая, что такое распределение.
Бенджамин Линдли

3
@Benjamin: и на этом его понимание заканчивается, что является лишь самым первым шагом (движки), и даже без понимания того, почему так важно, чтобы они генерировали плоское распределение. Вся остальная часть библиотеки остается загадкой без понимания распределений и других концепций статистики.
Matteo Italia

Ответы:


142

Вопрос слишком широкий для полного ответа, но позвольте мне выделить пару интересных моментов:

Почему "одинаково вероятно"

Предположим, у вас есть простой генератор случайных чисел, который генерирует числа 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и , таким образом , сделать двигатель очень легко заменить.

И снова Керрек опередил меня и дал гораздо лучший ответ, чем тот, над которым я работал. +1
Джастин ᚅᚔᚈᚄᚒᚔ

@Justin: Я уверен, что пропустил массу вещей, не стесняйтесь добавлять дополнительные аспекты к этой теме! :-)
Kerrek SB

13
Что касается части «заселить как-нибудь», я думаю std::random_device, стоит упомянуть, а не/dev/urandom
Cubbi

2
Пример std::random_deviceможно найти здесь .
WKS

1
Код в статье Википедии содержит ошибки. random и random2 идентичны. Из комментариев в фрагменте кода видно, что автор не понимает, как использовать функции в <random>.
user515430 08

3

Генератор случайных чисел - это уравнение, которое при заданном числе даст вам новое число. Обычно вы либо указываете первое число, либо извлекаете его из системного времени.

Каждый раз, когда вы запрашиваете новый номер, он использует предыдущий номер для выполнения уравнения.

Генератор случайных чисел не считается очень хорошим, если он имеет тенденцию генерировать одно и то же число чаще, чем другие числа. то есть, если вам нужно случайное число от 1 до 5, и у вас было такое распределение чисел:

  • 1: 1%
  • 2: 80%
  • 3: 5%
  • 4: 5%
  • 5: 9%

2 генерируется FAR чаще, чем любое другое число, поэтому вероятность его создания выше, чем других чисел. Если бы все числа были одинаковыми, у вас был бы 20% шанс каждый раз получать каждое число. Другими словами, приведенное выше распределение очень неравномерно, потому что 2 является предпочтительным. Распределение со всеми 20% было бы даже.

Как правило, если вам нужно истинное случайное число, вы извлекаете данные из чего-то вроде погоды или другого естественного источника, а не из генератора случайных чисел.


8
Большинство генераторов случайных чисел действительно генерируют хорошие равномерные распределения. Они просто не случайны; проблема в том, что они вычисляются, и, таким образом, вы можете угадать следующее число, учитывая достаточное число в последовательности (этот факт делает их плохими для безопасности, когда требуются действительно случайные числа). Для игр и прочего все должно быть в порядке.
Мартин Йорк

5
Я почти уверен, что OP запрашивает конкретную информацию о возможностях, представленных в заголовке C ++ <random>. Этот ответ даже не касается программирования, не говоря уже о C ++.
Бенджамин Линдли,

1
@Martin: Безопасность не обязательно требует источника действительно случайных чисел. AES в режиме счетчика (например) может работать довольно хорошо, даже если он детерминирован. Это требует разумного количества энтропии в ключе, но не истинной случайности.
Джерри Коффин,

@ Бенджамин Линдли: Забудь. Просто перечитал и понял, что ошибался.
N_A
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.