views:

525

answers:

4

I am trying to make a Java implementation of the Park-Miller-Carta PRNG random number generator (maybe faster?)

Below is the implementation of the Random function in ActionScript 3 from here.

return (_currentSeed = (_currentSeed * 16807) % 2147483647) / 0x7FFFFFFF
                                                          + 0.000000000233;

I am not having much luck getting this to work in Java:

int seed = 20; //for example.

public double random() {
    seed = (seed * 16807) % 2147483647;
    return seed / 0x7FFFFFFF + 0.000000000233;
}

This always returns 2.33E-10. Any ideas what I am doing wrong in Java? (the AS3 code returns 0.0001565276181885122, then 0.6307557630963248 for the first two responses with a seed of 20).

+1  A: 

Replace:

return seed / 0x7FFFFFFF+0.000000000233;

with:

return (double)seed / 0x7FFFFFFF+0.000000000233;
Peter
+4  A: 
seed / 0x7FFFFFFF

is an integer operation, since both arguments are integers. Integer division always rounds the "true" result downwards. In this case, the true result is between 0 and 1, so the operation always returns 0.

To get a floating-point result, at least one of the arguments must be a float, which can be achieved like this:

return (double)seed / 0x7FFFFFFF + 0.000000000233;
Artelius
+1  A: 

Operator precedence.

(seed / 0x7FFFFFFF)+0.000000000233;

is what you really have. Is it what you meant?

Charlie Martin
That's what the AS3 code did. So yes, it must be.
Artelius
No, the precedence rules could be different. Also, the semantics of the division operator can be different, which is what appears to have happened.
Charlie Martin
+2  A: 

OK, so the essential problem is that you need to do floating-point division, not integer division, as others have pointed out.

But I think fixing this particular code is sort of besides the point. Why bother with it in the first place? It's using essentially the same class of algorithm is java.lang.Random!

If you want a fast generator, consider an XORShift generator. If you want a good-quality generator, you have SecureRandom out of the box (though it's much slower), consider the Numerical Recipes algorithm (a fairly fast, combined generator), which you could implement in Java as follows:

public class HighQualityRandom extends Random {

  private Lock l = new ReentrantLock();
  private long u;
  private long v = 4101842887655102017L;
  private long w = 1;

  public HighQualityRandom() {
    this(System.nanoTime());
  }
  public HighQualityRandom(long seed) {
    l.lock();
    u = seed ^ v;
    nextLong();
    v = u;
    nextLong();
    w = v;
    nextLong();
    l.unlock();
  }

  @Override
  public long nextLong() {
    l.lock();
    try {
      u = u * 2862933555777941757L + 7046029254386353087L;
      v ^= v >>> 17;
      v ^= v << 31;
      v ^= v >>> 8;
      w = 4294957665L * (w & 0xffffffff) + (w >>> 32);
      long x = u ^ (u << 21);
      x ^= x >>> 35;
      x ^= x << 4;
      return (x + v) ^ w;
    } finally {
      l.unlock();
    }
  }

  protected int next(int bits) {
    return (int) (nextLong() >>> (64-bits));
  }

}

This is copied from some code where I needed it to be concurrent; you could get rid of the lock in principle, or just use regular synchronization.

If you absolutely insist on using Park-Miller-Carta, I'd at least wrap it in a Random subclass, and let java.util.Random take care of converting ints to doubles etc-- after all, that's what extensible libraries in an object-oriented language are for...

Neil Coffey