c++ c++11 random mersenne-twister

¿Qué Mersenne Twister ofrece C++ 11?



random c++ (3)

Tengo problemas para determinar qué variante de Mersenne Twister C ++ 11 ofrece. En cuanto al papel ACM de Matsumoto y Nishimura en Mersenne twister: un generador de números pseudoaleatorios uniformemente distribuido dimensionalmente 623 , los autores proporcionan el algoritmo, una implementación del algoritmo, y lo llaman MT19937 .

Sin embargo, cuando pruebo el generador del mismo nombre de C ++ 11 con el programa pequeño a continuación, no puedo reproducir la secuencia creada por Matsumoto y MT19937 de Nishimura. Las secuencias difieren de la primera palabra de 32 bits producida.

¿Qué Mersenne Twister ofrece C ++ 11?

El siguiente programa se ejecutó en Fedora 22 usando GCC, -std=c++11 y stdlibc++ GNU.

std::mt19937 prng(102013); for (unsigned int i = 0; i <= 625; i++) { cout << std::hex << prng(); if(i+1 != 625) cout << ","; if(i && i%8 == 0) cout << endl; }


Debo señalar que C ++ 11 en realidad ofrece muchos torneros Mersenne a través de la clase de plantilla:

template <class UIntType, size_t word_size, size_t state_size, size_t shift_size, size_t mask_bits, UIntType xor_mask, size_t tempering_u, UIntType tempering_d, size_t tempering_s, UIntType tempering_b, size_t tempering_t, UIntType tempering_c, size_t tempering_l, UIntType initialization_multiplier> class mersenne_twister_engine;

Si alguien tiene la valentía de explorar estas palancas y perillas ... Por supuesto, hay dos instancias estándar de estos:

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

y la versión de 64 bits:

using mt19937_64 = mersenne_twister_engine<uint_fast64_t, 64, 312, 156, 31, 0xb5026f5aa96619e9, 29, 0x5555555555555555, 17, 0x71d67fffeda60000, 37, 0xfff7eee000000000, 43, 6364136223846793005>;

He pensado que sería bueno proporcionar una caja de herramientas para verificar la calidad de los RNG para que la gente pueda probar nuevas instancias.

Aquí hay una comparación de parms de plantilla:

32,624,397,31, 0x9908b0df,11, 0xffffffff,7 , 0x9d2c5680,15, 0xefc60000,18,1812433253 <- std::mt19937 64,312,156,31,0xb5026f5aa96619e9,29,0x5555555555555555,17,0x71d67fffeda60000,37,0xfff7eee000000000,43,6364136223846793005 <- std::mt19937_64 w ,n ,m ,r ,a ,u ,d ,s ,b ,t ,c ,l ,f 32,624,397,31, 0x9908b0df,11, ,7 , 0x9d2c5680,15, 0xefc60000,18, <- paper

Gracias a @NathanOliver.


Mirando el MT19937 desde el vinculado al papel y el MT19937 definido por el estándar, parece que son los mismos, pero se agregó una capa adicional de revenido y un multiplicador de inicialización

Si miramos los valores definidos por [rand.predef] 26.5.5 (3) frente a los parámetros definidos por el papel que tenemos

32,624,397,31,0x9908b0df,11,0xffffffff,7,0x9d2c5680,15,0xefc60000,18,1812433253 <- standard w ,n ,m ,r ,a ,u ,d ,s,b ,t ,c ,l ,f 32,624,397,31,0x9908b0df,11, ,7,0x9d2c5680,15,0xefc60000,18, <- paper

De aquí viene la diferencia. También según el estándar, la iteración número 10.000 de std::mt19937 es 399268537


Parece que C ++ 11 proporciona a Mersenne Twister una inicialización mejorada

Acabo de extraer la implementación C original y comparé con C ++.

#include <iostream> #include <cstdio> #include <random> #define N 624 #define M 397 #define MATRIX_A 0x9908b0dfUL /* constant vector a */ #define UPPER_MASK 0x80000000UL /* most significant w-r bits */ #define LOWER_MASK 0x7fffffffUL /* least significant r bits */ static unsigned long mt[N]; /* the array for the state vector */ static int mti=N+1; /* mti==N+1 means mt[N] is not initialized */ void init_genrand(unsigned long s) { mt[0]= s & 0xffffffffUL; for (mti=1; mti<N; mti++) { mt[mti] = (1812433253UL * (mt[mti-1] ^ (mt[mti-1] >> 30)) + mti); mt[mti] &= 0xffffffffUL; } } unsigned long genrand_int32() { unsigned long y; static unsigned long mag01[2]={0x0UL, MATRIX_A}; if (mti >= N) { /* generate N words at one time */ int kk; if (mti == N+1) /* if init_genrand() has not been called, */ init_genrand(5489UL); /* a default initial seed is used */ for (kk=0;kk<N-M;kk++) { y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK); mt[kk] = mt[kk+M] ^ (y >> 1) ^ mag01[y & 0x1UL]; } for (;kk<N-1;kk++) { y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK); mt[kk] = mt[kk+(M-N)] ^ (y >> 1) ^ mag01[y & 0x1UL]; } y = (mt[N-1]&UPPER_MASK)|(mt[0]&LOWER_MASK); mt[N-1] = mt[M-1] ^ (y >> 1) ^ mag01[y & 0x1UL]; mti = 0; } y = mt[mti++]; y ^= (y >> 11); y ^= (y << 7) & 0x9d2c5680UL; y ^= (y << 15) & 0xefc60000UL; y ^= (y >> 18); return y; } int main() { init_genrand(102013); std::mt19937 prng(102013); for (size_t i = 0; i < 10000; ++i) { if (genrand_int32() != prng()) { std::cout << "ERROR" << std::endl; return 1; } } std::cout << "OK" << std::endl; return 0; }