Question

I am pretty interested in how Java RNG works. I've read that it's pretty easy using the getLong() method, and with getInt() and two values. I am interested in getting the seed with getInt(int n) knowing the value of n.

Since getInt(n) normally uses val = bits % n; and repeat it until bits - val + (n-1) >= 0), being bits next(31) so val == bits (mod n)

I've tested a bruteforce testing all posible pair of numbers which are congruent with the initial one, but this is very long and difficult for a computer.

Any other ideas on how to get the seed on a efficient way?

Was it helpful?

Solution

You should be able to use Reflection for this:

Random r = new Random(1);

Field f;
try {
    f = r.getClass().getDeclaredField("seed");
    f.setAccessible(true);
    AtomicLong seed = (AtomicLong) f.get(r);
    System.out.println("seed: " + seed);

    f = r.getClass().getDeclaredField("mask");
    f.setAccessible(true);
    Long mask = (Long) f.get(r);
    System.out.println("mask: " + mask);

    f = r.getClass().getDeclaredField("multiplier");
    f.setAccessible(true);
    Long multiplier = (Long) f.get(r);
    System.out.println("multiplier: " + multiplier);


    long initialSeed = (seed.longValue() ^ multiplier);
    System.out.println("restored initial seed: " + initialSeed);
} catch (NoSuchFieldException e1) {
} catch (SecurityException e2) {
} catch (IllegalAccessException e3) {
} catch (IllegalArgumentException e4) {
}   

Output on my machine:

seed: 25214903916
mask: 281474976710655
multiplier: 25214903917
restored initial seed: 1

When the seed is set, the value is scrambled:

public Random(long seed) {
    if (getClass() == Random.class)
        this.seed = new AtomicLong(initialScramble(seed));
    else {
        // subclass might have overriden setSeed
        this.seed = new AtomicLong();
        setSeed(seed);
    }
}

private static long initialScramble(long seed) {
    return (seed ^ multiplier) & mask; // (seed XOR multiplier) AND mask
}

However, the mask and multiplier are just defined as:

private static final long mask = (1L << 48) - 1;
private static final long multiplier = 0x5DEECE66DL;

As the mask is all 1s for the least significant 48 bits and the XOR is reversible, you can get back the initial seed if (and only if!) it was smaller than (1L << 48), i.e. 2^48:

The output for:

Random r = new Random((1L << 48)-1);

seed: 281449761806738
mask: 281474976710655
multiplier: 25214903917
restored initial seed: 281474976710655

and for:

Random r = new Random((1L << 48));

seed: 25214903917
mask: 281474976710655
multiplier: 25214903917
restored initial seed: 0

See also this answer on StackOverflow

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top