Search code examples
c++randommodular-arithmeticlcg

Analysis of Linear congruential generator is wrong?


So in an attempt to better understand MSVC++'s implementation of rand, I re-implemented it and attempted to better understand it (and LCGs in general I guess).

My implementation (which matches MSVC++'s almost exactly) is as follows:

// vc++ impl. of random
// Xn+1 = (aXn + i) mod m
// a = 214013, i = 2531011, m = 32768
unsigned int seed = 0;
unsigned int random()
{
    seed = seed * 214013L + 2531011L;
    // return (seed/(1<<16)) % 32768; (equiv to below)
    return seed>>16 & 0x7FFF;
}

To find the difference in the newly generated seeds from 2 seeds, I figured it would just be (214013*h) % 2^32 where h is the difference between the 2 initial seeds. Using this same logic I figured the difference between 2 randomly generated numbers given the initial seed as x and the next seed as x+h, I took this different in seed, divided it by 2^16 (or shifted it right 16 bits), and got rid of the most significant bit.

The values this yields seem to be correct except in some cases, for example when x = 100 and h = 5000.

Here's the entire code:

#include <iostream>
#include <cstdlib>

// vc++ impl. of random
// Xn+1 = (aXn + i) mod m
// a = 214013, i = 2531011, m = 32768
unsigned int seed = 0;
unsigned int random()
{
    seed = seed * 214013L + 2531011L;
    return seed>>16 & 0x7FFF;
}

int main()
{
    // f(x) = (214013x + 2531011) mod 2^32 [LCG]
    // g(x) = floor(f(x)/2^16) mod 2^15 [RND]
    // h(x) = f(x + h) - f(x) ?= 214013*h mod 2^32
    // j(x) = g(x + h) - g(x) ?= 214013*h/2^16 mod 2^15

    // x: initial seed
    // h: displaecment to next seed (next seed: x + h)
    // a, b: first and second randomly generated values using C rand
    // c, d: first and second randomly generated values using random
    // newSeedA, newSeedB: seed generated from LCG after x and x + h respectively
    // diffExp: experimental difference in random values
    // diffCalc: calculated/theoretical difference in random vlaues
    unsigned int x = 100, h = 50000;
    unsigned int a, b, c, d;
    unsigned int newSeedA, newSeedB;
    int diffExp, diffCalc;

    srand(x);
    seed = x;
    a = rand();
    c = random();
    newSeedA = seed;

    srand(x + h);
    seed = x + h;
    b = rand();
    d = random();
    newSeedB = seed;

    diffExp = (d - c) % 32768;
    diffCalc = (214013*h)>>16 & 0x7FFF;


    std::cout << "RANDOM VALUES\n";
    std::cout << "  VC++ rand: " << a << ", " << b << "\n";
    std::cout << "Custom rand: " << c << ", " << d << "\n";
    std::cout << "\n";

    std::cout << "DIFFERENCE IN SEED\n";
    std::cout << "Experimental Difference: " << (newSeedB - newSeedA) << "\n";
    std::cout << "  Calculated Difference: " << (static_cast<unsigned int>(214013)*h) << "\n";
    std::cout << "\n";

    std::cout << "DIFFERENCE IN VALUES\n";
    std::cout << "Experimental Difference: " << diffExp << "\n";
    std::cout << "  Calculated Difference: " << diffCalc << "\n";
    std::cout << "\n";
    return 0;
}

However with these values, the estimated difference between the 2 randomly generated values is 1 less than the actual difference. Am I doing something obviously wrong?


Solution

  • The difference between the new seeds is indeed 214013*h.

    That gives the seeds s and s + 214013*h, the difference between the resulting random outputs would be (before simplification) diff = ((s + 214013*h >> 16) & 0x7fff) - ((s >> 16) & 0x7fff). The question is then essentially, is this expression independent of s.

    It is not. For example, even taking h = 1, diff can be either 3 (eg s = 0) or 4 (eg s = 0x0000bc03).