用于集群环境的伪随机数发生器

时间:2011-06-16 04:20:28

标签: random parallel-processing prng mersenne-twister

如何在群集上生成独立的伪随机数,例如蒙特卡罗模拟?我可以拥有许多计算节点(例如100),我需要在每个节点上生成数百万个数字。我需要保证一个节点上的PRN序列不会与另一个节点上的PRN序列重叠。

  • 我可以在根节点上生成所有PRN,然后将它们发送到其他节点。但这太慢了。
  • 我可以在每个节点上跳到序列中的已知距离。但对于Mersenne-Twister还是其他任何好的PRNG都有这样的算法,可以用合理的时间和内存来完成吗?
  • 我可以在每个节点上使用不同的生成器。但是有没有像Mersenne-Twister这样的优秀发电机?怎么可能呢?
  • 还有其他吗?

5 个答案:

答案 0 :(得分:11)

您永远不应使用从相同原始流获取的可能重叠的随机流。如果您尚未测试生成的交错流,则不了解其统计质量。

幸运的是, 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将运行时将更改。

除了DC附带的自述文件外,请查看DC存档中的文件example/new_example2.c。它显示了获取独立序列的调用示例给定不同的输入标识符,这基本上是您必须识别集群作业的。

最后,如果您打算详细了解如何在并行或分布式环境中使用PRNG,我建议您阅读这些科学文章:

随机高性能计算随机流的实际分布 ,David RC Hill,高性能计算与仿真国际会议(HPCS),2010

答案 1 :(得分:1)

好的,回答#2; - )

我要说...保持简单。只需使用“短”种子来填充MT(假设这个种子是2 32 位,因为没有更好的限制)。这假设短种子生成“充分分布”的MT起始状态(例如,在我的其他答案中的代码中init_genrand,希望如此)。这并不保证一个平均分布的起始状态,而是“足够好”,见下文。

每个节点将使用它自己预先选择的种子序列(传输的随机种子列表或类似number_nodes * node_number * iteration的公式)。重要的是,最初的“短”种子永远不会在节点间重复使用

然后,每个节点将使用使用此种子n初始化的MT PRNG n <<< MT_period / max_value_of_short_seed TT800 is 2800-1 and MT19937 is 219937-1,因此n仍然可以非常大< / em>数字)。在n次之后,节点将移动到所选列表中的下一个种子。

虽然我(我也不能)提供“保证”,即任何节点都不会同时(或根本没有)重复序列,这里是AMD says about Using Different Seends :(显然是初始播种)算法起作用。)

  
    

在这里描述的创建多个流的四种方法中,这是最不令人满意的 ...例如,如果初始值相距不够远,则从不同起点生成的序列可能会重叠。如果使用的发电机的周期很长,则减少了重叠序列的可能性。 虽然不能保证序列的独立性,但由于其周期极长,使用具有随机起始值的Mersenne Twister不太可能导致问题,特别是如果所需序列的数量很少...

  

快乐的编码。

答案 2 :(得分:1)

  

我可以在每个节点上跳到序列中的已知距离。但是   有Mersenne-Twister或任何其他好的算法   PRNG,可以在合理的时间和记忆下完成吗?

是的,请参阅http://theo.phys.sci.hiroshima-u.ac.jp/~ishikawa/PRNG/mt_stream_en.html。这是获得独立随机数流的绝佳解决方案。通过使跳转大于每个流所需的随机数来创建每个流的开始,流不会重叠。

答案 3 :(得分:0)

免责声明:我不确定MT在从任意“uint”(或x,其中x是较小的任意但唯一值)种子开始时,在循环重叠方面有什么保证,但这可能值得研究,好像有一个保证,那么在一个不同的“uint”种子上启动每个节点可能就足够了,而这个帖子的其余部分在很大程度上没有实际意义。 (The cycle length/period of MT is staggering并且除去UINT_MAX仍会留下难以理解的 - 除了纸上 - 号码。)


嗯,这是我的评论回答......

我喜欢使用预先生成的一组状态来处理#2;然后用给定的起始状态初始化每个节点中的MT。

当然,只有初始状态必须保留,一旦生成,这些状态就可以

  1. 如果符合要求,则无限期地重复使用,或者;
  2. 接下来的状态可以在外部快速框上生成为什么模拟正在运行;或
  3. 节点可以报告最终状态(如果是可靠的消息传递,如果序列在节点之间以相同的速率使用,并且满足要求等)
  4. 考虑到MT 快速生成,我不推荐上面的#3,因为它只是复杂并且附加了许多字符串。选项#1很简单,但可能不够动态。

    选项#2似乎是非常好的可能性。服务器(“快速机器”,不一定是节点)只需要传输下一个“未使用的序列块”的起始状态(比如,10亿个周期) - 节点将在询问之前使用发生器10亿个周期换一个新的街区。这将使其成为帖子中#1的混合,非常罕见的消息传递。

    在我的系统上,Core2 Duo,我可以使用下面提供的代码在17秒内生成 10亿个随机数(它在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;
        }
    }
    

    快乐的编码。

答案 4 :(得分:0)

TRNG是一个随机数生成器,专门针对并行集群环境而构建(特别是它是为德国的TINA超级计算机而构建的)。因此,创建独立的随机数流并生成非标准分布非常容易。有一个如何在这里设置它的教程: http://www.lindonslog.com/programming/parallel-random-number-generation-trng/