Отказ от ответственности: я не уверен, что гарантия MT в терминах перекрытия циклов при запуске с произвольного начального числа «uint» (или x, где x - меньшее произвольное, но уникальное значение), но это может стоить изучить, как если есть гарантия, то это может быть достаточно, чтобы просто начать каждый узел на другие «UINT» семени и остальная часть этого поста становится в значительной степени спорной. ( Длина цикла / период MT составляет , поражая , а деление UINT_MAX по-прежнему оставляет непонятным - кроме как на бумаге - число.)
Ну, вот мои комментарии, чтобы ответить ...
Мне нравится подход № 2 с предварительно сгенерированным набором состояний; MT в каждом узле затем инициализируется с заданным начальным состоянием.
Разумеется, должны быть сохранены только начальные состояния, и как только они будут созданы, эти состояния могут
- Повторно использовать на неопределенный срок, если выполняются требования, или;
- Следующие состояния могут быть сгенерированы вперед на внешнем быстром блоке, почему выполняется симуляция или;
- Узлы могут сообщать о конечном состоянии (если надежный обмен сообщениями и если последовательность используется с одинаковой скоростью среди узлов и соответствует требованиям и т. Д.)
Учитывая, что MT генерирует быстро для генерации , я бы не рекомендовал # 3 сверху, так как он сложный и имеет несколько прикрепленных строк. Вариант № 1 прост, но может быть недостаточно динамичным.
Вариант № 2 кажется очень хорошей возможностью. Серверу («быстрой машине», не обязательно узлу) нужно только передать начальное состояние следующего «неиспользуемого блока последовательности» (скажем, один миллиард циклов) - узел будет использовать генератор в течение одного миллиарда циклов, прежде чем запрашивать для нового блока. Это сделало бы гибрид № 1 в посте с очень редкими сообщениями.
В моей системе, Core2 Duo, я могу генерировать один миллиард случайных чисел за 17 секунд, используя приведенный ниже код (он работает в LINQPad ). Я не уверен, что это за вариант MT.
void Main()
{
var mt = new MersenneTwister();
var start = DateTime.UtcNow;
var ct = 1000000000;
int n = 0;
for (var i = 0; i < ct; i++) {
n = mt.genrand_int32();
}
var end = DateTime.UtcNow;
(end - start).TotalSeconds.Dump();
}
// From ... and modified (stripped) to work in LINQPad.
// http://mathnet-numerics.googlecode.com/svn-history/r190/trunk/src/Numerics/Random/MersenneTwister.cs
// See link for license and copyright information.
public class MersenneTwister
{
private const uint _lower_mask = 0x7fffffff;
private const int _m = 397;
private const uint _matrix_a = 0x9908b0df;
private const int _n = 624;
private const double _reciprocal = 1.0/4294967295.0;
private const uint _upper_mask = 0x80000000;
private static readonly uint[] _mag01 = {0x0U, _matrix_a};
private readonly uint[] _mt = new uint[624];
private int mti = _n + 1;
public MersenneTwister() : this((int) DateTime.Now.Ticks)
{
}
public MersenneTwister(int seed)
{
init_genrand((uint)seed);
}
private void init_genrand(uint s)
{
_mt[0] = s & 0xffffffff;
for (mti = 1; mti < _n; mti++)
{
_mt[mti] = (1812433253*(_mt[mti - 1] ^ (_mt[mti - 1] >> 30)) + (uint) mti);
_mt[mti] &= 0xffffffff;
}
}
public uint genrand_int32()
{
uint y;
if (mti >= _n)
{
int kk;
if (mti == _n + 1) /* if init_genrand() has not been called, */
init_genrand(5489); /* 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 & 0x1];
}
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 & 0x1];
}
y = (_mt[_n - 1] & _upper_mask) | (_mt[0] & _lower_mask);
_mt[_n - 1] = _mt[_m - 1] ^ (y >> 1) ^ _mag01[y & 0x1];
mti = 0;
}
y = _mt[mti++];
/* Tempering */
y ^= (y >> 11);
y ^= (y << 7) & 0x9d2c5680;
y ^= (y << 15) & 0xefc60000;
y ^= (y >> 18);
return y;
}
}
Удачного кодирования.