Генерация псевдослучайных 16-битных целых - PullRequest
0 голосов
/ 09 января 2019

Мне нужно сгенерировать 16-битные псевдослучайные целые числа, и мне интересно, каков наилучший выбор.

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

std::random_device rd;
auto seed_data = std::array<int, std::mt19937::state_size> {};
std::generate(std::begin(seed_data), std::end(seed_data), std::ref(rd));
std::seed_seq seq(std::begin(seed_data), std::end(seed_data));
std::mt19937 generator(seq);
std::uniform_int_distribution<short> dis(std::numeric_limits<short>::min(), 
                                         std::numeric_limits<short>::max());

short n = dis(generator);

Проблема, которую я вижу здесь, состоит в том, что std::mt19937 производит 32-разрядные целые числа без знака, поскольку оно определено так:

using mt19937 = mersenne_twister_engine<unsigned int, 
                                        32, 624, 397, 
                                        31, 0x9908b0df,
                                        11, 0xffffffff, 
                                        7, 0x9d2c5680, 
                                        15, 0xefc60000, 
                                        18, 1812433253>;

Это означает, что статическое приведение выполнено, и только наименее значимая часть этих 32-разрядных целых чисел используется распределением. Поэтому мне интересно, насколько хороши эти серии псевдослучайных шорт, и у меня нет математических знаний, чтобы ответить на этот вопрос.

Я ожидаю, что лучшим решением было бы использование собственного определенного mersenne_twister_engine движка для 16-битных целых чисел. Однако я не нашел ни одного упомянутого набора для аргументов шаблона (требования могут быть найдены здесь например). Есть ли?

ОБНОВЛЕНИЕ : Я обновил пример кода с правильной инициализацией для распространения.

Ответы [ 2 ]

0 голосов
/ 11 января 2019

Может быть неправильное представление, учитывая эту цитату из вопроса ОП (выделено мной):

Проблема, которую я вижу здесь, состоит в том, что std :: mt19937 производит 32-разрядные целые числа без знака […]. Это означает, что статическое приведение выполнено и только наименее значимая часть этих 32-разрядных целых чисел используется распределением .

Это не так.

Ниже приведены цитаты из https://en.cppreference.com/w/cpp/numeric/random

Библиотека случайных чисел предоставляет классы, которые генерируют случайные и псевдослучайные числа. Эти классы включают в себя:

  • Унифицированные генераторы случайных битов (URBG), […];
  • Распределения случайных чисел (например, равномерное, нормальное или распределение Пуассона), которые преобразуют выходные данные URBG в различные статистические распределения

URBG и распределения предназначены для совместного использования для получения случайных значений.

Итак, генератор случайных битов, как mt19937 или random_device

- это объект функции, возвращающий целочисленные значения без знака, так что каждое значение в диапазоне возможных результатов имеет (в идеале) равную вероятность возврата.

В то время как распределение случайных чисел, как uniform_int_distribution

постобрабатывает выходные данные URBG таким образом , что полученные выходные данные распределяются в соответствии с определенной статистической функцией плотности вероятности.

То, как это делается, использует все биты от источника для получения вывода. В качестве примера мы можем взглянуть на реализацию std::uniform_distribution в libstdc++ (начиная со строки 824), которая может быть примерно упрощена как

template <typename Type>
class uniform_distribution
{
    Type a_ = 0, b_ = std::numeric_limits<Type>::max();
public:
    uniform_distribution(Type a, Type b) : a_{a}, b_{b} {}
    template<typename URBG>
    Type operator() (URBG &gen)
    {
        using urbg_type = std::make_unsigned_t<typename URBG::result_type>;
        using u_type    = std::make_unsigned_t<Type>;
        using max_type  = std::conditional_t<(sizeof(urbg_type) > sizeof(u_type))
                                            , urbg_type, u_type>;

        urbg_type urbg_min = gen.min();
        urbg_type urbg_max = gen.max();
        urbg_type urbg_range = urbg_max - urbg_min;

        max_type urange = b_ - a_;
        max_type udenom = urbg_range <= urange ? 1 : urbg_range / (urange + 1);

        Type ret;
        // Note that the calculation may require more than one call to the generator
        do
            ret = (urbg_type(gen()) - urbg_min ) / udenom;
            // which is 'ret = gen / 65535' with OP's parameters
            // not a simple cast or bit shift
        while (ret > b_ - a_);
        return ret + a_;
    }
};

Это можно проверить ЗДЕСЬ .

0 голосов
/ 09 января 2019

Твой путь действительно правильный.

Математические аргументы являются сложными (я попытаюсь выкопать статью), но использование наименее значимых битов Mersenne Twister, реализованного в стандартной библиотеке C ++, является правильным решением.

Если у вас есть какие-либо сомнения относительно качества последовательности, проведите ее через несгибаемые испытания.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...