Почему бы просто не использовать random_device?

Я немного запутался в случайной библиотеке С++ 11.

Что я понимаю: нам нужны две отдельные концепции:

  • случайный движок (который может быть псевдо (нужно семя) или реальным)
  • : он отображает числа, полученные от двигателя, на определенный интервал, используя конкретное распределение.

Я не понимаю, почему не просто использовать это:

std::random_device rd;
std::uniform_int_distribution<int> dist(1, 5);

// get random numbers with:
dist(rd);

Насколько я могу сказать, это хорошо работает.

Вместо этого, это то, что я нашел на большинстве примеров/сайтов/статей:

std::random_device rd;
std::mt19937 e{rd()}; // or std::default_random_engine e{rd()};
std::uniform_int_distribution<int> dist{1, 5};

// get random numbers with:
dist(e);

Я не говорю о специальном использовании, например. криптография, просто ваши основные статьи о запуске.

Мое подозрение состоит в том, что std::mt19937 (или std::default_random_engine) принимает семя, его можно будет легче отладить, предоставив одно и то же семя во время сеанса отладки.

Кроме того, почему не просто:

std::mt19937 e{std::random_device{}()};

Ответ 1

Кроме того, почему не просто:

std::mt19937 e{std::random_device{}()};

Это может быть хорошо, если вы только сделаете это один раз, но если вы будете делать это много раз, лучше отслеживать ваш std::random_device, а не создавать/уничтожать его без необходимости.

Может быть полезно посмотреть исходный код libС++ для реализации std::random_device, что довольно просто. Это просто тонкая обертка над std::fopen("/dev/urandom"). Поэтому каждый раз, когда вы создаете std::random_device, вы получаете другой дескриптор файловой системы и оплачиваете все связанные с этим расходы.

В Windows, как я понимаю, std::random_device представляет собой некоторый вызов API-интерфейса microsoft crypto, поэтому вы будете инициализировать и уничтожать некоторый интерфейс библиотеки криптографических программ каждый раз, когда вы это делаете.

Это зависит от вашего приложения, но для общих целей я бы не думал об этом накладных расходах, как всегда, пренебрежимо мало. Иногда это так, и тогда это здорово.

Я думаю, это связано с вашим первым вопросом:

Вместо этого, это то, что я нашел на большинстве примеров/сайтов/статей:

 std::random_device rd;
 std::mt19937 e{rd()}; // or std::default_random_engine e{rd()};
 std::uniform_int_distribution<int> dist{1, 5};

По крайней мере, я думаю об этом:

  • std::mt19937 - очень простой и надежный случайный генератор. Реализация обязана стандартом, и, по крайней мере, в boost, он везде использовал один и тот же код, полученный из оригинальной статьи mt19937. Этот код очень стабилен и кросс-платформенный. Вы можете быть уверены, что инициализация, запрос из него и т.д. Собирается скомпилировать аналогичный код на любой платформе, на которой вы его скомпилируете, и что вы получите такую ​​же производительность.

  • std::random_device напротив, довольно непрозрачный. Вы действительно не знаете точно, что это такое, что он собирается делать, или насколько он будет эффективен. Вы даже не знаете, действительно ли он может быть получен - это может вызвать исключение, когда вы пытаетесь его создать. Вы знаете, что для этого не требуется семя. Обычно вы не должны извлекать из него тонны и тонны данных, просто используйте их для генерации семян. Иногда он действует как хороший интерфейс для криптографических API, но на самом деле этого не требуется, и, к сожалению, иногда это не так. Он может соответствовать /dev/random в unix, он может соответствовать /dev/urandom/. Он может соответствовать некоторому криптографическому API MSVC (визуальная студия), или он может быть просто фиксированной константой (mingw). Если вы перекрестно скомпилируете какой-нибудь телефон, кто знает, что он будет делать. (И даже если вы получаете /dev/random, у вас все еще есть проблема, что производительность может быть непротиворечивой - может показаться, что она отлично работает, пока не закончится энтропийный пул, а затем она работает медленно, как собака.)

Как я думаю об этом, std::random_device должен быть похож на улучшенную версию посева с time(NULL) - на низкий бар, потому что time(NULL) - это довольно дрянное семя, все рассмотренное. Я обычно использую его, где я бы использовал time(NULL) для генерации семени в течение дня. Я действительно не считаю все это полезным вне этого.

Ответ 2

Эта статья - хороший момент для начала.

Я собираюсь синтезировать только несколько точек:

  • У него неизвестная стоимость.

    Насколько дорого стоит прочитать номер с этого "устройства"? Это неуказано. Например, это может быть чтение из /dev/random в системе Linux, которая может блокировать длительный период ожидания энтропии (что само по себе является проблемой по разным причинам).

Для моего личного опыта я уведомил, что std::random_device обычно медленнее, чем простой псевдослучайный алгоритм. В целом это может быть неправдой, но обычно это происходит. Это связано с тем, что это может включать физические устройства или другое оборудование, чем простой процессор.

  • На самом деле это может быть детерминированным.

    С++ 11 std:: random_device не требуется быть недетерминированным! Реализации могут и реализуют его как простой RNG с фиксированным семенем, поэтому он производит одинаковый вывод для каждого запуска программы.