Питання занадто широке, щоб отримати повну відповідь, але дозвольте мені вибрати пару цікавих моментів:
Чому "однаково ймовірно"
Припустимо, у вас є простий генератор випадкових чисел, який генерує числа 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 у кодегурі.
- У Вікіпедії є хороший підсумок (спасибі, @Justin).
- В принципі, кожен двигун повинен набратиdef a
result_type
, що є правильним інтегральним типом, який потрібно використовувати для насіння. Я думаю , що у мене був глючная реалізація одного разу що змусило мене змусити насіння , std::mt19937
щоб uint32_t
на x64, в кінці кінців , це має бути виправлено , і ви можете сказати , MyRNG::result_type seed_val
і , таким чином , зробити двигун дуже легко замінити.
rand
Ви повинні швидко ознайомитись з wikipedia для деяких основних статистичних концепцій та RNG, інакше вам буде дуже важко пояснити обґрунтування<random>
та використання різних його фрагментів.