Генерація випадкових чисел у C ++ 11: як генерувати, як це працює? [зачинено]


102

Нещодавно я натрапив на новий спосіб генерування випадкових чисел у C ++ 11, але не міг перетравити документи, які я читав про нього (що це за двигун , математичний термін, як розподіл , "де всі цілі числа виробляються з однаковою ймовірністю ").

Тож будь-хто, будь ласка, може пояснити

  • хто вони?
  • що вони означають?
  • як генерувати?
  • як вони працюють?
  • тощо

Ви можете назвати все це в одне поширене запитання про генерацію випадкових чисел.


6
Запитувати про RNG, не знаючи, що таке розподіл - це як запитати про парсерів виразів, не знаючи, що таке вираз ... Бібліотека RNG в C ++ 11 призначена для людей, які знають певну статистику і мають більші потреби, ніж плоский розподіл, породжений randВи повинні швидко ознайомитись з wikipedia для деяких основних статистичних концепцій та RNG, інакше вам буде дуже важко пояснити обґрунтування <random>та використання різних його фрагментів.
Маттео Італія

26
@Matteo: Навряд чи. Дитина може зрозуміти поняття, що матриця виробляє випадкові числа, не розуміючи, що таке розподіл.
Бенджамін Ліндлі

3
@ Бенджамін: і на цьому його розуміння зупиняється - це лише перший крок (двигуни), і навіть не розуміючи, чому важливо, щоб вони генерували рівний розподіл. Вся решта бібліотеки залишається загадкою без розуміння розподілів та інших концепцій статистики.
Маттео Італія

Відповіді:


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

Вкотре Керрек б'є мене на удар набагато кращою відповіддю, ніж той, над яким я працював. +1
Джастін ᚅᚔᚈᚄᚒᚔ

@Justin: Я впевнений, що я пропустив багато речей, не соромтесь додавати додаткові аспекти до цієї теми! :-)
Керрек СБ

13
Щодо частини "заселити якось", я думаю std::random_device, варто згадати, а не/dev/urandom
Cubbi

2
Приклад std::random_deviceможна знайти тут .
WKS

1
Код у статті Вікіпедії - баггі. випадкові та випадкові2 однакові. З коментарів у фрагменті коду зрозуміло, що автор не розуміє, як використовувати функції в <випадково>.
user515430

3

Генератор випадкових чисел - це рівняння, яке, давши число, дасть вам нове число. Зазвичай ви або надаєте перше число або його витягнути з чогось типу системного часу.

Кожен раз, коли ви запитуєте нове число, воно використовує попереднє число для виконання рівняння.

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

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

2 генерується FAR частіше, ніж будь-яке інше число, тому воно швидше виробляється, ніж інші числа. Якби всі цифри були однаковими, ви мали б 20% шансів отримати кожне число кожного разу. Якщо сказати іншим способом, вищезазначений розподіл дуже нерівномірний, оскільки 2 є вигідним. Розподіл з усіма 20% буде рівномірним.

Як правило, якщо ви хочете справжнього випадкового числа, ви отримаєте дані з чогось типу погоди чи іншого природного джерела, а не з генератора випадкових чисел.


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

5
Я впевнений, що ОП запитує конкретну інформацію про засоби, передбачені в заголовку C ++ <випадковий>. Ця відповідь навіть не стосується програмування, не кажучи вже про C ++.
Бенджамін Ліндлі

1
@Martin: безпека не обов'язково вимагає джерела справді випадкових чисел. AES в режимі лічильника (для одного прикладу) може зробити дуже непогано, навіть якщо це детерміновано. Це вимагає розумної кількості ентропії в ключі, але не будь-якої справжньої випадковості.
Джеррі Труну

@Benjamin Lindley: Nevermind. Просто перечитав і зрозумів, що я помилявся.
N_A
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.