Мое требование — генерировать случайные байты данных. (не случайные числа) ака равномерно распределенные биты.
В связи с этим мне было интересно, каковы правильные / эффективные способы сделать это с помощью случайных средств C ++ 11/14. Я посмотрел на примеры, но они все кажется, сосредоточиться на генерации чисел (целые, плавающие и т. д.)
Текущее решение, которое я использую, заключается в следующем:
#include <vector>
#include <random>
int main()
{
std::random_device rd;
std::uniform_int_distribution<int> dist(0,255);
std::vector<char> data(1000);
for (char& d : data)
{
d = static_cast<char>(dist(rd) & 0xFF);
}
return 0;
}
Распределения берут случайные биты и превращают их в числа. Если вы действительно хотите случайные биты, то вы хотите использовать движок:
В частности, эти требования определяют алгоритмический интерфейс для типов и объектов, которые создают последовательности битов, в которых каждое возможное значение бита является одинаково вероятным.3
Один вызов объекта URNG позволяет производить и доставлять много (обычно 32 или более) битов, возвращая эти биты как единое упакованное значение целого типа без знака.4
N3847
random_device
бывает так, что доступ к равномерно распределенным битам прост:
std::random_device engine;
unsigned x = engine(); // sizeof(unsigned) * CHAR_BIT random bits
Обратите внимание, что другие движки могут не так легко получить равномерно случайные биты, как random_device
из-за возврата меньшего количества битов, чем может содержать их result_type, или даже путем эффективного возврата дробных битов.
Если вы обеспокоены тем, что unsigned
Размер определяется реализацией и так random_device
возвращает число битов, определенное реализацией, вы можете написать адаптер, который либо собирает достаточно битов, прежде чем отдать их вам, либо адаптер, который даст вам достаточно битов и кеширует остальное для вашего следующего запроса. (Вы можете также сделать это, чтобы обращаться с другими двигателями, которые показывают ранее упомянутые проблемы.)
То, что вы ищете, это std::independent_bits_engine
адаптер:
#include <vector>
#include <random>
#include <climits>
#include <algorithm>
#include <functional>
using random_bytes_engine = std::independent_bits_engine<
std::default_random_engine, CHAR_BIT, unsigned char>;
int main()
{
random_bytes_engine rbe;
std::vector<unsigned char> data(1000);
std::generate(begin(data), end(data), std::ref(rbe));
}
Обратите внимание, что принятый ответ не является строго правильным в общем случае — случайные механизмы выдают значения без знака, принадлежащие диапазону [min()
, max()
], который не обязательно охватывает все возможные значения типа результата (например, std::minstd_rand0::min() == 1
) и, таким образом, вы можете получить случайные байты, которые распределены неравномерно, если использовать движок напрямую. Однако для std::random_device
диапазон [std::numeric_limits<result_type>::min()
, std::numeric_limits<result_type>::max()
], так что этот конкретный двигатель также будет работать без адаптера.
Чтобы ответить на ваш вопрос: вы не можете.
Стандарт не позволяет std::uniform_int_distribution
быть шаблонным на char
, signed char
, или же unsigned char
, Некоторые считают, что это дефект в стандарте, но это так.
Вы можете просто шаблон std::uniform_int_distribution
на unsigned short
и установите его минимальный / максимальный диапазон на std::numeric_limits<unsigned char>::min()
а также std::numeric_limits<unsigned char>::max()
, а затем просто присвойте результат unsigned char
,
Из стандарта:
На протяжении этого подпункта 26.5 эффект создания шаблона:
[…]е) который имеет параметр типа шаблона с именем
IntType
не определено, если соответствующий аргумент шаблона не является cv-unqualified и не является одним изshort
,int
,long
,long long
,unsigned short
,unsigned int
,unsigned long
, или жеunsigned long long
,§26.5.1.1 [rand.req.genl]
Более того:
Вы должны использовать std::mt19937
на самом деле генерировать ваши случайные байты. std::random_device
может быть медленным и, вероятно, приводит к энтропии со статистическими свойствами (т.е. пригодностью для использования в криптографии), которые вам не нужны.
Тем не менее, вам нужно будет посеять std::mt19937
, Вы можете сделать это с std::random_device
и std::seed_seq
.
Обратите внимание, что если вы не используете std::seed_seq
посеять std::mt19937
, ваш std::mt19937
останется со многими, многими нулями во внутреннем состоянии, и поэтому потребуется много времени, чтобы «прогреться».
Для получения дополнительной информации о «разминке», посмотреть здесь.