C++ 如何简洁、便携、彻底地为mt19937 PRNG植入种子?
我似乎看到了许多答案,其中有人建议使用C++ 如何简洁、便携、彻底地为mt19937 PRNG植入种子?,c++,c++11,random,C++,C++11,Random,我似乎看到了许多答案,其中有人建议使用生成随机数,通常与以下代码一起使用: std::random_device rd; std::mt19937 gen(rd()); std::uniform_int_distribution<> dis(0, 5); dis(gen); 我们可能会用旧的方式来论证time(NULL)提供了低熵,time(NULL)是可预测的,并且最终结果是不一致的 但所有这一切都适用于新的方式:它只是有一个更闪亮的饰面 rd()返回单个无符号int。它至
生成随机数,通常与以下代码一起使用:
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> dis(0, 5);
dis(gen);
我们可能会用旧的方式来论证time(NULL)
提供了低熵,time(NULL)
是可预测的,并且最终结果是不一致的
但所有这一切都适用于新的方式:它只是有一个更闪亮的饰面
返回单个rd()
。它至少有16位,可能有32位。这还不足以播种MT的19937位状态无符号int
- 使用
(使用32位种子设定并查看第一个输出)无法提供良好的输出分布。7和13永远不能是第一个输出。两颗种子产生0。12粒种子产生1226181350粒种子。()std::mt19937 gen(rd());gen()
std::random_设备可以,有时也可以作为一个带有固定种子的简单PRNG来实现。因此,它可能会在每次运行时产生相同的序列。()这比
更糟糕时间(NULL)
- 必须为mt19937/mt19937\u 64完全播种
- 不能仅依靠
或std::random_设备
作为熵源时间(NULL)
- 不应依赖Boost或其他库
- 应该适合在一个小的行数,这样它会看起来很好的副本粘贴到一个答案
- 我目前的想法是,
的输出可以与std::random_device
、派生值和硬编码常量(可以在分发过程中设置)混合在一起(可能通过异或),以尽最大努力获得熵time(NULL)
很好地说明了std::random\u device可以做什么,也可以不做什么std::random\u device::entropy()
mt19937的state\u size
属性来决定初始化时提供多少种子:
using Generator = std::mt19937;
inline
auto const& random_data()
{
thread_local static std::array<typename Generator::result_type, Generator::state_size> data;
thread_local static std::random_device rd;
std::generate(std::begin(data), std::end(data), std::ref(rd));
return data;
}
inline
Generator& random_generator()
{
auto const& data = random_data();
thread_local static std::seed_seq seeds(std::begin(data), std::end(data));
thread_local static Generator gen{seeds};
return gen;
}
template<typename Number>
Number random_number(Number from, Number to)
{
using Distribution = typename std::conditional
<
std::is_integral<Number>::value,
std::uniform_int_distribution<Number>,
std::uniform_real_distribution<Number>
>::type;
thread_local static Distribution dist;
return dist(random_generator(), typename Distribution::param_type{from, to});
}
使用生成器=std::mt19937;
内联
自动常量和随机_数据()
{
线程\本地静态std::数组数据;
线程\本地静态标准::随机\设备rd;
std::generate(std::begin(数据)、std::end(数据)、std::ref(rd));
返回数据;
}
内联
生成器和随机_生成器()
{
自动常量和数据=随机_数据();
线程\本地静态std::seed \ seq seed(std::begin(数据)、std::end(数据));
线程_本地静态生成器gen{seeds};
返回发电机;
}
模板
数字随机数(数字从,数字到)
{
使用Distribution=typename std::conditional
<
std::is_integral::value,
标准:均匀分布,
标准:均匀实分布
>::类型;
螺纹_局部静态分布区;
return dist(random_generator(),typename分布::param_type{from,to});
}
我认为还有改进的余地,因为std::random_device::result_type
在大小和范围上可能与std::mt19937::result_type
有所不同,因此应该真正加以考虑
关于的注释。
根据C++11(/14/17)
标准:
26.5.6随机类设备[rand.device]
2如果实现限制阻止生成非确定性随机数,则实现可采用随机数引擎
这意味着,如果由于某些限制而无法生成非确定性值,则实现只能生成确定性值
众所周知,Windows上的MinGW
编译器不从其std::random_设备
提供非确定性值,尽管这些值很容易从操作系统获得。因此,我认为这是一个bug,不太可能是跨实现和平台的常见现象。 我认为std::random_设备的最大缺陷是,如果没有可用的CSPRNG,则允许确定性回退。这本身就是不使用std::random_device
为PRNG种子的一个很好的理由,因为产生的字节可能是确定性的。不幸的是,它没有提供一个API来发现何时发生这种情况,或者请求失败,而不是提供低质量的随机数
也就是说,没有完全可移植的解决方案:但是,有一种体面的、最低限度的方法。您可以在CSPRNG(以下定义为sysrandom
)周围使用最小包装器来为PRNG种子
窗户
您可以依赖CSPRNG的CryptGenRandom
。例如,您可以使用以下代码:
bool acquire_context(HCRYPTPROV *ctx)
{
if (!CryptAcquireContext(ctx, nullptr, nullptr, PROV_RSA_FULL, 0)) {
return CryptAcquireContext(ctx, nullptr, nullptr, PROV_RSA_FULL, CRYPT_NEWKEYSET);
}
return true;
}
size_t sysrandom(void* dst, size_t dstlen)
{
HCRYPTPROV ctx;
if (!acquire_context(&ctx)) {
throw std::runtime_error("Unable to initialize Win32 crypt library.");
}
BYTE* buffer = reinterpret_cast<BYTE*>(dst);
if(!CryptGenRandom(ctx, dstlen, buffer)) {
throw std::runtime_error("Unable to generate random bytes.");
}
if (!CryptReleaseContext(ctx, 0)) {
throw std::runtime_error("Unable to release Win32 crypt library.");
}
return dstlen;
}
与Boost的比较
我们可以在快速查看以下内容后看到boost::random_设备(真正的CSPRNG)的相似之处。Boost在Windows上使用MS_DEF_PROV
,这是PROV_RSA_FULL
的提供程序类型。唯一缺少的是验证加密上下文,这可以通过CRYPT\u VERIFYCONTEXT
完成。在*Nix上,Boost使用/dev/uradom
。也就是说,这个解决方案是可移植的、经过良好测试的、易于使用的
Linux专门化
如果你愿意为了安全而牺牲简洁,那么e
bool acquire_context(HCRYPTPROV *ctx)
{
if (!CryptAcquireContext(ctx, nullptr, nullptr, PROV_RSA_FULL, 0)) {
return CryptAcquireContext(ctx, nullptr, nullptr, PROV_RSA_FULL, CRYPT_NEWKEYSET);
}
return true;
}
size_t sysrandom(void* dst, size_t dstlen)
{
HCRYPTPROV ctx;
if (!acquire_context(&ctx)) {
throw std::runtime_error("Unable to initialize Win32 crypt library.");
}
BYTE* buffer = reinterpret_cast<BYTE*>(dst);
if(!CryptGenRandom(ctx, dstlen, buffer)) {
throw std::runtime_error("Unable to generate random bytes.");
}
if (!CryptReleaseContext(ctx, 0)) {
throw std::runtime_error("Unable to release Win32 crypt library.");
}
return dstlen;
}
size_t sysrandom(void* dst, size_t dstlen)
{
char* buffer = reinterpret_cast<char*>(dst);
std::ifstream stream("/dev/urandom", std::ios_base::binary | std::ios_base::in);
stream.read(buffer, dstlen);
return dstlen;
}
std::uint_least32_t seed;
sysrandom(&seed, sizeof(seed));
std::mt19937 gen(seed);
#if defined(__linux__) || defined(linux) || defined(__linux)
# // Check the kernel version. `getrandom` is only Linux 3.17 and above.
# include <linux/version.h>
# if LINUX_VERSION_CODE >= KERNEL_VERSION(3,17,0)
# define HAVE_GETRANDOM
# endif
#endif
// also requires glibc 2.25 for the libc wrapper
#if defined(HAVE_GETRANDOM)
# include <sys/syscall.h>
# include <linux/random.h>
size_t sysrandom(void* dst, size_t dstlen)
{
int bytes = syscall(SYS_getrandom, dst, dstlen, 0);
if (bytes != dstlen) {
throw std::runtime_error("Unable to read N bytes from CSPRNG.");
}
return dstlen;
}
#elif defined(_WIN32)
// Windows sysrandom here.
#else
// POSIX sysrandom here.
#endif
#if defined(__OpenBSD__)
# define HAVE_GETENTROPY
#endif
#if defined(HAVE_GETENTROPY)
# include <unistd.h>
size_t sysrandom(void* dst, size_t dstlen)
{
int bytes = getentropy(dst, dstlen);
if (bytes != dstlen) {
throw std::runtime_error("Unable to read N bytes from CSPRNG.");
}
return dstlen;
}
#endif
size_t sysrandom(void* dst, size_t dstlen)
{
int fd = open("/dev/urandom", O_RDONLY);
if (fd == -1) {
throw std::runtime_error("Unable to open /dev/urandom.");
}
if (read(fd, dst, dstlen) != dstlen) {
close(fd);
throw std::runtime_error("Unable to read N bytes from CSPRNG.");
}
close(fd);
return dstlen;
}
#include <random>
#include <chrono>
#include <cstdint>
#include <algorithm>
#include <functional>
#include <iostream>
uint32_t LilEntropy(){
//Gather many potential forms of entropy and XOR them
const uint32_t my_seed = 1273498732; //Change during distribution
static uint32_t i = 0;
static std::random_device rd;
const auto hrclock = std::chrono::high_resolution_clock::now().time_since_epoch().count();
const auto sclock = std::chrono::system_clock::now().time_since_epoch().count();
auto *heap = malloc(1);
const auto mash = my_seed + rd() + hrclock + sclock + (i++) +
reinterpret_cast<intptr_t>(heap) + reinterpret_cast<intptr_t>(&hrclock) +
reinterpret_cast<intptr_t>(&i) + reinterpret_cast<intptr_t>(&malloc) +
reinterpret_cast<intptr_t>(&LilEntropy);
free(heap);
return mash;
}
//Fully seed the mt19937 engine using as much entropy as we can get our
//hands on
void SeedGenerator(std::mt19937 &mt){
std::uint_least32_t seed_data[std::mt19937::state_size];
std::generate_n(seed_data, std::mt19937::state_size, std::ref(LilEntropy));
std::seed_seq q(std::begin(seed_data), std::end(seed_data));
mt.seed(q);
}
int main(){
std::mt19937 mt;
SeedGenerator(mt);
for(int i=0;i<100;i++)
std::cout<<mt()<<std::endl;
}
size_t sysrandom(void* dst, size_t dstlen); //from Alexander Huszagh answer above
void foo(){
std::array<std::mt19937::UIntType, std::mt19937::state_size> state;
sysrandom(state.begin(), state.length*sizeof(std::mt19937::UIntType));
std::seed_seq s(state.begin(), state.end());
std::mt19937 g;
g.seed(s);
}
#include <cstdint> //`uint32_t`
#include <functional> //`std::hash`
#include <random> //`std::mt19937`
#include <iostream> //`std::cout`
static std::mt19937 rng;
static void seed(uint32_t seed) {
rng.seed(static_cast<std::mt19937::result_type>(seed));
}
static void seed() {
uint32_t t = static_cast<uint32_t>( time(nullptr) );
std::hash<uint32_t> hasher; size_t hashed=hasher(t);
seed( static_cast<uint32_t>(hashed) );
}
int main(int /*argc*/, char* /*argv*/[]) {
seed();
std::uniform_int_distribution<> dis(0, 5);
std::cout << dis(rng);
}