В моем текущем проекте на C ++ 11 мне нужно выполнить M-моделирование. Для каждой симуляции m = 1, ..., M
я случайным образом генерирую набор данных, используя объект std::mt19937
, построенный следующим образом:
std::mt19937 generator(m);
DatasetFactory dsf(generator);
Согласно https://stackoverflow.com/a/15509942/1849221 и https://stackoverflow.com/a/14924350/1849221, ГПСЧ Mersenne Twister извлекает выгоду из фазы разогрева, которая в настоящее время отсутствует в моем коде. Сообщаю для удобства предлагаемый фрагмент кода:
#include <random>
std::mt19937 get_prng() {
std::uint_least32_t seed_data[std::mt19937::state_size];
std::random_device r;
std::generate_n(seed_data, std::mt19937::state_size, std::ref(r));
std::seed_seq q(std::begin(seed_data), std::end(seed_data));
return std::mt19937{q};
}
Проблема в моем случае заключается в том, что мне нужна воспроизводимость результатов, то есть при разных исполнениях для каждой симуляции набор данных должен быть одинаковым. Вот почему в моем текущем решении я использую текущее моделирование для заполнения ГПСЧ Mersenne Twister. Мне кажется, что использование std::random_device
предотвращает совпадение данных (AFAIK, это точная цель std::random_device
).
РЕДАКТИРОВАТЬ: под разными исполнениями я имею в виду повторный запуск исполняемого файла.
Как я могу ввести в код вышеупомянутую фазу разогрева, не влияя на воспроизводимость? Спасибо.
Возможное решение # 1
Вот примерная реализация, основанная на втором предложении @SteveJessop
#include <random>
std::mt19937 get_generator(unsigned int seed) {
std::minstd_rand0 lc_generator(seed);
std::uint_least32_t seed_data[std::mt19937::state_size];
std::generate_n(seed_data, std::mt19937::state_size, std::ref(lc_generator));
std::seed_seq q(std::begin(seed_data), std::end(seed_data));
return std::mt19937{q};
}
Возможное решение # 2
Вот предварительная реализация, основанная на совместном вкладе @SteveJassop и @ AndréNeve. Функция sha256
адаптирована из https://stackoverflow.com/a/10632725/1849221
#include <openssl/sha.h>
#include <sstream>
#include <iomanip>
#include <random>
std::string sha256(const std::string str) {
unsigned char hash[SHA256_DIGEST_LENGTH];
SHA256_CTX sha256;
SHA256_Init(&sha256);
SHA256_Update(&sha256, str.c_str(), str.size());
SHA256_Final(hash, &sha256);
std::stringstream ss;
for(int i = 0; i < SHA256_DIGEST_LENGTH; i++)
ss << std::hex << std::setw(2) << std::setfill('0') << (int)hash[i];
return ss.str();
}
std::mt19937 get_generator(unsigned int seed) {
std::string seed_str = sha256(std::to_string(seed));
std::seed_seq q(seed_str.begin(), seed_str.end());
return std::mt19937{q};
}
Скомпилировать с: -I/opt/ssl/include/ -L/opt/ssl/lib/ -lcrypto
std::mt19937::state_size
на этапе инициализации, сохраняя при этом воспроизводимость. - person Ilio Catallo   schedule 18.04.2013discard(n)
для улучшения внутреннего состояния as-if, вызываяoperator()
n
-раз. - person Xeo   schedule 18.04.2013discard(n)
того же результата при использованииstd::seed_seq
такого же размера, какstd::mt19937::state_size
, для заполнения ГПСЧ? Какой подходящийn
будет использоваться? - person Ilio Catallo   schedule 18.04.2013std::hash<unsigned int>
недостаточно хорошо. Проблема с MT, которую вы пытаетесь преодолеть, заключается в том, что ему требуется много ненулевых битов начальных данных, в противном случае его внутреннее состояние в основном равно 0, и он выводит неверные данные.std::hash
- не тот хеш-код для решения этой проблемы. В лучшем случае он по-прежнему предоставляет только 64 бита начальных данных, и это еще хуже, поскольку это, скорее всего, операция идентификации. Если вы использовали, например, SHA256-хэшm
, возможно, вы занимаетесь бизнесом. - person Steve Jessop   schedule 19.04.2013std::hash
, вероятно, реализован как функция идентификации. Я постараюсь пересмотреть решение №2, чтобы использоватьopenssl/sha.h
- person Ilio Catallo   schedule 19.04.2013