need a "uniform random" generator

Mar 13, 2012 at 6:00pm
Hi -

I have a need for a random number generator that will produce consistent results on different machines. (I realize, this isn't really random, but I need it.) I would just use a file of values, except I need a ton of them (like 2^32).

Is there any way to make the built-in rand() function produce the same results on different systems? Failing that, is there some pre-built 3rd-party routine that might do the same?
Mar 13, 2012 at 6:13pm
Mersenne twister (available as part of C++11 or as a 3rd party lib in C++03) is a fixed algorithm so if you give it the same seed every time you'll get the same stream of numbers.

Barring that, if you want to be 100% sure you get consistent results and don't need a super-great RNG, you can write your own.

Here are some from an old old lib I worked on. One is simple and straightforward, and one is more complex if that isn't "serious" enough for you.

Both are consistent. Just call "Reseed" with the same value and it'll produce the same string.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// base class


class RNGBase
{
public:
    //===============================================
    virtual         ~RNGBase()  {   }

    //===============================================
    //  Generating random numbers
    virtual u32     Get() = 0;              // to be supplied by the RNG
    inline u32      Get(u32 min,u32 max)    { return (max<=min) ? min : (Get() % (max-min) + min); }

    inline double   Real()
    { return ( double(Get()) / 4294967296.0 );             }

    inline double   Real1()
    { return ( double(Get()) / 4294967295.0 );             }

    //==================================================
    // overloaded parenthesis
    inline u32      operator () ()                  { return Get();             }
    inline u32      operator () (u32 min,u32 max)   { return Get(min,max);      }

    //================================================
    //  Reseeding
    inline void     TimeSeed()              { Reseed( static_cast<u32>(time(0)) );  }
    virtual void    Reseed(u32 seed) = 0;   // to be supplied by the RNG

};


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// Simple:

/*!

    %GMSimple32RNG is an implementation of a pRNG posted by George Marsaglia on Google
Groups.  It was casually posted without a name for the algorithm or any copyright information.
It is a very simplistic and lightweight algroithm.

*/

class GMSimple32RNG : public RNGBase
{
public:
    //=====================================================
    //  ctor
    GMSimple32RNG(bool seed = true)     {       if(seed) TimeSeed();    }

    //=====================================================
    //  the RNG
    virtual u32     Get()
    {
        return state = (69069*state) + 362437;
    }

    //=====================================================
    //  Seeding the RNG
    virtual void    Reseed(u32 seed)
    {
        state = seed;
    }

private:
    u32             state;
};


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// more complex, based on ISAAC algorithm
//  header

/*!

    %IsaacRNG is an implementation of the ISAAC algorithm for producing random numbers.
The ISAAC algorithm was designed by Robert Jenkis.  For additional information on
the algorithm, see the following links:
- http://www.burtleburtle.net/bob/rand/isaac.html
- http://www.burtleburtle.net/bob/rand/isaacafa.html

    Code in this implementation was translated from 'readable.c' which was declared
public domain and is available via the above links.

*/

class IsaacRNG : public RNGBase
{
public:
    //========================================
    //  ctor
    IsaacRNG(bool seed = true)     {       if(seed) TimeSeed();    }

    //========================================
    //  Generation
    virtual u32     Get();

    //========================================
    //  Reseeding
    virtual void    Reseed(u32 seed);
    void            ReseedFull(const u32 seeds[256]);


private:
    u32             mm[256];
    u32             aa;
    u32             bb;
    u32             cc;
    u8              step;
};

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
// ISAAC .cpp file

void IsaacRNG::Reseed(u32 seed)
{
    // use the GMSimple32 RNG to fill the seeds for this RNG
    GMSimple32RNG r(false);
    r.Reseed(seed);
    u32 seeds[256];

    int i;
    for(i = 0; i < 256; ++i)
        seeds[i] = r();

    ReseedFull(seeds);
}

//=============================================================

static inline void mix(u32& a,u32& b,u32& c,u32& d,u32& e,u32& f,u32& g,u32& h)
{
    a^=b<<11;   d+=a;   b+=c;
    b^=c>>2;    e+=b;   c+=d;
    c^=d<<8;    f+=c;   d+=e;
    d^=e>>16;   g+=d;   e+=f;
    e^=f<<10;   h+=e;   f+=g;
    f^=g>>4;    a+=f;   g+=h;
    g^=h<<8;    b+=g;   h+=a;
    h^=a>>9;    c+=h;   a+=b;
}

void IsaacRNG::ReseedFull(const u32 seeds[256])
{
    aa = 0;
    bb = 0;
    cc = 0;
    step = 0;

    static const u32 GOLDENRATIO = 0x9E3779B9;

    u32 a, b, c, d, e, f, g, h;
    a=b=c=d=e=f=g=h=GOLDENRATIO;

    // scramble the golden ratio
    int i;
    for(i = 0; i < 4; ++i)
        mix(a,b,c,d,e,f,g,h);

    // shuffle up mm
    for(i = 0; i < 256; i += 8)
    {
        a += seeds[i  ];    b += seeds[i+1];    c += seeds[i+2];    d += seeds[i+3];
        e += seeds[i+4];    f += seeds[i+5];    g += seeds[i+6];    h += seeds[i+7];

        mix(a,b,c,d,e,f,g,h);

        mm[i  ] = a;    mm[i+1] = b;    mm[i+2] = c;    mm[i+3] = d;
        mm[i+4] = e;    mm[i+5] = f;    mm[i+6] = g;    mm[i+7] = h;
    }

    // do another pass to make sure the seed has full effect
    for(i = 0; i < 256; i += 8)
    {
        a += mm[i  ];   b += mm[i+1];   c += mm[i+2];   d += mm[i+3];
        e += mm[i+4];   f += mm[i+5];   g += mm[i+6];   h += mm[i+7];

        mix(a,b,c,d,e,f,g,h);

        mm[i  ] = a;    mm[i+1] = b;    mm[i+2] = c;    mm[i+3] = d;
        mm[i+4] = e;    mm[i+5] = f;    mm[i+6] = g;    mm[i+7] = h;
    }

    // count and discard the first 256 numbers
    //  not sure why, but the original algorithm did this.. so what the hell
    for(i = 0; i < 256; ++i)
        Get();
}

//=======================================================

u32 IsaacRNG::Get()
{
    if(!step)
    {
        cc += 1;
        bb += cc;
    }

    switch(step & 3)
    {
    case 0: aa ^= (aa<<13);  break;
    case 1: aa ^= (aa>> 6);  break;
    case 2: aa ^= (aa<< 2);  break;
    case 3: aa ^= (aa>>16);  break;
    }

    u32 x, y;

    x = mm[step];
    aa =            mm[ step   ^ 0x80] + aa;
    mm[step] = y =  mm[(x>>2)  & 0xFF] + aa + bb;
    bb =            mm[(y>>10) & 0xFF] + x;

    ++step;     // wraps at 8 bits
    return bb;
}
Last edited on Mar 13, 2012 at 6:15pm
Mar 14, 2012 at 12:41am
Thanks, Disch. I chose the simple one; more than good enough for this purpose. Worked like a charm, though I did have to typedef u32:

typedef uint32_t u32;

I needed the following include files:

1
2
#include <ctime>
#include <tr1/cstdint> 


I also had to create my own maximum value:

const u32 RAND_MAX_U32 = 0xffffffff;

Because the library-supplied RAND_MAX appears to differ between GNU and MinGW (!). (I may need to verify this, but it's still good to have a value that is packaged in with the program.

Thanks again.
Topic archived. No new replies allowed.