Генератор псевдослучайных чисел для кластерной среды - PullRequest
10 голосов
/ 16 июня 2011

Как я могу генерировать независимые псевдослучайные числа в кластере, например, для симуляции Монте-Карло?У меня может быть много вычислительных узлов (например, 100), и мне нужно генерировать миллионы чисел на каждом узле.Мне нужна гарантия, что последовательность PRN на одном узле не будет перекрывать последовательность PRN на другом узле.

  • Я могу сгенерировать все PRN на корневом узле, а затем отправить их на другие узлы.Но это было бы слишком медленно.
  • Я мог бы прыгнуть на известное расстояние в последовательности на каждом узле.Но существует ли такой алгоритм для Mersenne-Twister или для любого другого хорошего PRNG, который может быть выполнен с разумным количеством времени и памяти?
  • Я мог бы использовать разные генераторы на каждом узле.Но возможно ли это с хорошими генераторами, такими как Mersenne-Twister?Как это могло быть сделано?
  • Любое другое, хотя?

Ответы [ 5 ]

11 голосов
/ 16 июня 2011

Никогда не следует использовать потенциально перекрывающиеся случайные потоки, полученные из одного и того же исходного потока. Если вы не тестировали полученный чередующийся поток, вы не представляете его статистическое качество.

К счастью, Mersenne Twister (MT) поможет вам в вашей задаче распространения. Используя его специальный алгоритм, называемый Dynamic Creator (далее DC), вы можете создать независимых генераторов случайных чисел , которые будут генерировать очень независимые случайные потоки.

Каждый поток будет создан на узле, который будет его использовать. По сути, думайте о DC как о конструкторе в объектно-ориентированной парадигме, который создает разные экземпляры MT. Каждый отдельный экземпляр предназначен для создания очень независимых случайных последовательностей.

Вы можете найти DC здесь: http://www.math.sci.hiroshima -u.ac.jp / ~ m-mat / MT / DC / dc.html
Его довольно просто использовать, и вы сможете фиксировать различные параметры, такие как количество различных экземпляров MT, которые вы хотите получить, или период этих MT. В зависимости от входного параметра, время работы DC будет меняться.

Помимо README, поставляемого вместе с DC, посмотрите файл example/new_example2.c в архиве DC. В нем показан пример вызовов для получения независимых последовательностей с другим входным идентификатором , который, в основном, и используется для определения заданий кластера.

Наконец, если вы хотите узнать больше о том, как использовать PRNG в параллельных или распределенных средах, я предлагаю вам прочитать следующие научные статьи:

Практическое распределение случайных потоков для стохастических высокопроизводительных вычислений , Дэвид Р. Хилл, Международная конференция по высокопроизводительным вычислениям и моделированию (HPCS) , 2010

1 голос
/ 01 марта 2013

Я могу прыгнуть на известное расстояние в последовательности на каждом узле.Но существует ли такой алгоритм для Mersenne-Twister или для любого другого хорошего PRNG, который может быть выполнен с разумным количеством времени и памяти?

Да, см. http://theo.phys.sci.hiroshima -u.ac.jp / ~ Ishikawa / ПСЧ / mt_stream_en.html .Это отличное решение для получения независимых потоков случайных чисел.Делая переходы, которые больше, чем количество случайных чисел, необходимых из каждого потока для создания начала каждого потока, потоки не будут перекрываться.

1 голос
/ 16 июня 2011

Хорошо, ответ # 2; -)

Я собираюсь сказать ... будь проще. Просто используйте «короткое» начальное число для заполнения MT (представьте, что это начальное число составляет 2 32 битов из-за отсутствия лучшего ограничения). Это предполагает, что короткое начальное число генерирует «достаточно распределенные» начальные состояния MT (например, init_genrand в коде в моем другом ответе, надеюсь). Это не гарантирует равномерно распределенное начальное состояние, а скорее достаточно хорошее, см. Ниже.

Каждый узел будет использовать свою собственную последовательность начальных чисел, которые предварительно выбраны (либо список случайных начальных чисел, которые передаются, либо формула, подобная number_nodes * node_number * iteration). Важно то, что начальное «короткое» начальное число никогда не будет повторно использоваться на узлах .

Затем каждый узел будет использовать MT PRNG, инициализированный этим начальным числом n раз, где n <<< MT_period / max_value_of_short_seed ( TT800 равно 2 800 -1, а MT19937 равно 2 19937 -1 , поэтому n может быть очень большим числом). После n раз, узел переходит к следующему начальному числу в выбранном списке.

Хотя я не даю (и не могу) предоставить «гарантию», что ни один узел не будет иметь дублирующуюся последовательность одновременно (или вообще), вот что AMD говорит об использовании различных сеансов : (Очевидно, что играет роль начальный алгоритм заполнения).

Из четырех описанных здесь методов создания нескольких потоков это наименее удовлетворительное ... Например, последовательности, сгенерированные из разных начальных точек, могут перекрываться, если начальные значения находятся недостаточно далеко друг от друга. Возможность перекрывающихся последовательностей уменьшается, если период используемого генератора велик. Несмотря на то, что нет гарантии независимости последовательностей, из-за чрезвычайно большого периода использования Twister Mersenne со случайными начальными значениями вряд ли приведет к проблемам , особенно если количество требуемых последовательностей мало. ..

Удачного кодирования.

0 голосов
/ 10 августа 2013

TRNG - это генератор случайных чисел, созданный специально для параллельных кластерных сред (особенно для суперкомпьютера TINA в Германии) Следовательно, очень легко создавать независимые потоки случайных чисел, а также генерировать нестандартные распределения. Здесь есть руководство по настройке: http://www.lindonslog.com/programming/parallel-random-number-generation-trng/

0 голосов
/ 16 июня 2011

Отказ от ответственности: я не уверен, что гарантия MT в терминах перекрытия циклов при запуске с произвольного начального числа «uint» (или x, где x - меньшее произвольное, но уникальное значение), но это может стоить изучить, как если есть гарантия, то это может быть достаточно, чтобы просто начать каждый узел на другие «UINT» семени и остальная часть этого поста становится в значительной степени спорной. ( Длина цикла / период MT составляет , поражая , а деление UINT_MAX по-прежнему оставляет непонятным - кроме как на бумаге - число.)


Ну, вот мои комментарии, чтобы ответить ...

Мне нравится подход № 2 с предварительно сгенерированным набором состояний; MT в каждом узле затем инициализируется с заданным начальным состоянием.

Разумеется, должны быть сохранены только начальные состояния, и как только они будут созданы, эти состояния могут

  1. Повторно использовать на неопределенный срок, если выполняются требования, или;
  2. Следующие состояния могут быть сгенерированы вперед на внешнем быстром блоке, почему выполняется симуляция или;
  3. Узлы могут сообщать о конечном состоянии (если надежный обмен сообщениями и если последовательность используется с одинаковой скоростью среди узлов и соответствует требованиям и т. Д.)

Учитывая, что 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;
    }
}

Удачного кодирования.

...