TL; DR: default_random_engine
не дает никаких гарантий. Если вы хотите числовое случайное число, используйте вместо этого mt19937_64
. Если вы хотите создать безопасную релевантность (ключи, IV, соли, пароли, ...), найдите CSPRNG, засеянный с random_device
, или используйте random_device
. Ниже приведен забавный анализ поведения default_random_engine
g++
.
default_random_engine
является реализацией определенных
Как уже упоминался в комментариях, default_random_engine
имеет определенную реализацию поведения, и вы должны, вероятно, не использовать его вообще. Реализация определенных означает, что реализация, например, свободно использовать (в) известный XKCD rng:
class default_random_engine
{
public:
using result_type = uint32_t;
result_type min() const
{
return 1;
}
result_type max() const
{
return 6;
}
default_random_engine(int) {}; // we don’t care about the argument
result_type operator()() const
{
return 4; // chosen by fair dice roll
// guaranteed to be random
}
};
default_random_engine
не очень хорошо работает с семенами, близких друг к другу
Однако, на самом деле оказывается, просто, что default_random_engine
(используется в GNU libstdC++) сильно предвзято относится к семенам, которые в настоящее время выпускаются time(0)
и/или в разрешении, который он отбирает с помощью uniform_int_distribution<int>(1, 6)
. Попробуйте создать сразу несколько случайных чисел в одной программе. Вы найдете такие результаты: 6 6 6 4 6 2 2 3
, 6 3 2 1 4 6 3 3
, .... Короче говоря, у default_random_engine
нет гарантий, и может быть очень плохой RNG для любой цели (по-видимому, даже учась о случайном API, потому что он не кажется случайным при создании одиночного diceroll, засеянного временными отметками эпохи UNIX!).
Итак, я запустил номера. С небольшой программой (см. Ниже) я вычислил распределение вероятности для первого числа рулонов кости, сгенерированного с default_random_engine
и mt19937_64
, равным 6, нанесенным на график в диапазоне семян. Это с g ++ (Debian 6.1.1-11) 6.1.1 20160802 и libstdC++ 6.1.1-11.
График показывает вероятность того, чтобы получить 6 с std::uniform_int_distribution<int> (1,6)
в первом нарисовать после инициализации соответствующего случайного двигателя с семенами на осях X. Кроме того, текущее время unix отображается как вертикальная черная линия (видно только в верхнем диапазоне диаграммы). Как вы можете видеть, в настоящее время мы находимся в диапазоне, где целочисленное распределение составляет очень, вероятно (100%) для вывода 6 в качестве первого номера с использованием default_random_engine
. В отличие от mt19937_64
, очень близка ожидаемая вероятность.. Размер бункера для гистограммы составляет 10000 (или примерно 2,8 часа в секундах с момента-unix-эпохи).
Кроме того, в течение всего испытательного диапазона (0 ..4e9-1), default_random_engine
фактически производит 6 как первое число с вероятностью приблизительно ⅙. Это просто очень плохо при работе с семенами, которые «близки» (± 1000) друг к другу.
Заключение
Таким образом, в некотором смысле, time(0)
является преступник, потому что она медленно меняется. А также, (стандартная библиотека, используемая) Code :: Blocks является виновником, потому что их default_random_engine
сосет. Правильный RNG не должен смещаться к определенному результату по столь большому количеству семян. Просто используйте надлежащий RNG и не пытайтесь заставить вещи работать хорошо с default_random_engine
, что не гарантируется быть переносимым между компиляторами и стандартными библиотеками.
Используйте один из двигателей с числовым звуком случайных чисел, предоставляемых стандартом C++ 11 (например, упомянутый mt19937_64), если вам нужны случайные числа для численных целей (например, имитация физики игры).
Если вам нужны случайные числа для безопасности ничего соответствующего (генерировать пароли, соли, капельницы для криптографии, ключи, ...), делать не использовать mt19937_64, но использовать CSPRNG высевают с помощью std::random_device
std::random_device
или непосредственно.
Другое примечание, поскольку учебное пособие предназначено для разработки игры: как только сеть начнет играть, вам действительно понадобится независимое от платформы поведение. В таких случаях выбор конкретного двигателя RNG с определенными параметрами имеет жизненно важное значение для достижения воспроизводимых результатов между клиентами и между клиентом и сервером.
Источник
#include <array>
#include <cstdint>
#include <iostream>
#include <random>
#include <ctime>
void search()
{
static constexpr uint32_t SEARCH_MAX = 4000000000;
static constexpr uint32_t SEARCH_BLOCKS = 8;
static constexpr uint32_t SEARCH_BIN_SIZE = 10000;
static constexpr uint32_t SEARCH_BINS = SEARCH_MAX/SEARCH_BIN_SIZE;
static constexpr uint32_t SEARCH_STEP = 1;
static constexpr uint32_t OUTPUT_STEP = 1000000;
std::vector<uint32_t> histogram(SEARCH_BINS);
for (uint32_t &member: histogram) member = 0;
std::uniform_int_distribution<int> diceRoll(1, 6);
static constexpr uint32_t START = BLOCK * (SEARCH_MAX/SEARCH_BLOCKS);
static constexpr uint32_t END = START + SEARCH_MAX/SEARCH_BLOCKS;
for (uint32_t i = START; i < END; i+=SEARCH_STEP) {
rng_engine_to_use foo(i);
int roll = diceRoll(foo);
if (roll == 6) {
histogram[i/SEARCH_BIN_SIZE] += 1;
}
if (i % OUTPUT_STEP == 0) {
std::cerr << ((float)i/SEARCH_MAX * 100) << "% \r" << std::flush;
}
}
for (uint32_t i = START/SEARCH_BIN_SIZE; i < END/SEARCH_BIN_SIZE; ++i) {
std::cout << i*SEARCH_BIN_SIZE << " " << histogram[i] << std::endl;
}
}
int main()
{
search();
}
I #define
d BLOCK быть в 0..7, чтобы построить одну версию для каждого из моих ядер, потому что mt19937_64 занимает довольно много времени, чтобы произвести все эти числа (я только впоследствии понял, что сюжет не будет читабельным, если будет построен по всему диапазону, но meh.). Я заменил rng_engine_to_use
на mt19937_64
и default_random_engine
каждый и построил построенную гистограмму (см. Выше).
'' default_random_engine'' имеет полностью реализованное поведение. Вы можете попробовать заменить его, например, '' mt19937'' или '' mt19937_64'' (см. [Соответствующая статья на cppreference.com] (http://en.cppreference.com/w/cpp/numeric/random)) и посмотрите, устраняет ли это вашу проблему. Если это не так, проверьте вывод '' time (0) '' и посмотрите, отличается ли он от выполнения. Если это не так, это ваш преступник, хотя я не могу представить, как *, что * будет происходить последовательно, если вы не запускаете программу несколько раз в секунду. –
Я исправил 'default_random_engine' с' mt19937', теперь он работает. Так почему «default_random_engine» дает мне тот же ответ. Есть ли способ решить ее? – Athul