Can I have multiple independent random number generators with different seeds?

#1

I’m hoping to use multiple random number generators so that I can have different random processes that don’t interfere with each other and can be seeded for some amount of consistency. Is this possible with p5.js?

2 Likes

#2

Normally, we would expect that each instance of p5 would have its own random seed. :seedling:

In such case, we could instantiate a new p5 and call its noCanvas(), just for the sake of using its random() method w/ a specific randomSeed(). :money_mouth_face:

However, the code responsible to generate seeded pseudo-random values in p5js is a singleton object stored in a closure variable named lcg: :neutral_face:

B/c lcg is a closure variable, no matter how many times we instantiate the class p5, there’ll only be 1 unique lcg object shared across all p5 instances! :astonished:

Instead, lcg needs to be changed into a class property, so each instance of that class would have its own lcg, rather than a shared 1. :face_with_monocle:

BtW, the p5js library is littered w/ such closure local variables, which are a dumb, poor, short-sighted attempt to make private, unaccessible states. :male_detective:

And considering the library is supposed to have multiple instances of it at the same time, it shouldn’t have any shared states at all! :no_good_man:

Now, as a workaround, you can simply rewrite the “random.js” file as a class, whose instances will have their own seed state. :bulb:

More discussion about pseudo-random generation: :sunglasses:

5 Likes

#3

Thanks for the incredibly thorough and informative answer, @GoToLoop. If I am running p5 inside of another js environment and have access to math.random, does that change the answer? I’m working with a friend on a web app that uses javascript, p5, react, and probably some other things I’m not aware of. I’m a js noob.

0 Likes

#4

The only sensible solution is creating a class for random + seed. :seedling:

If you prefer not to, the only other feasible solution is using iframes: :framed_picture:

Let’s say you need 10 unique seeds. Then you’re gonna need 10 iframes w/ a p5js in it. :grimacing:

1 Like

#5

Gotcha. Thank you so much! This has been extremely helpful.

0 Likes

#6

If you just want a standalone random class that takes a seed, I implemented one recently as part of a cross-language processing randomness kit called CommonRandom. (The goal was repeatable psuedorandom generators across JavaScript, Java, and python mode sketches). The pseudo-random method is based on XORshift. Create as many RandAnywhere objects as you like an use them individually.

Here is a demo sketch:

var rand;

function setup() {
  createCanvas(400, 200);
  //rand = new RandAnywhere(0);
  //// rand = new RandAnywhere(1536694537142); // example of a specific millisecond timecode

  // print a series of examples values from a specific seed
  // corresponding to epoch milliseconds, as if a seed was
  // requested on 2018-11-11 at 11:11:11.
  rand = new RandAnywhere(1541963471000);
  print(rand.nextLong());        // 1043807117
  print(rand.nextInt());         // 935077768
  print(rand.random());          // 0.7892195325294601
  print(rand.random(10));        // 1.1313271294959482
  print(rand.random(0, width));  // 140.0591152441032
  
  // reset seed
  rand.randomSeed(0);

  // set native seed
  randomSeed(0);
}

/**
 * draw a series of random lines
 */
function draw() {
  if (frameCount>64) {
    noLoop();
  }
  stroke(0, 0, 0);
  line(rand.random(0, width/2), rand.random(0, height), 
    rand.random(0, width/2), rand.random(0, height));
  stroke(255, 0, 0);
  translate(width/2, 0);
  line(random(0, width/2), random(0, height), 
    random(0, width/2), random(0, height));
}

/**
 * a simple cross-language pseudorandom number generator (PRNG)
 * implemented in Processing (Java)
 * inspired by https://stackoverflow.com/a/34659107/7207622
 */
function RandAnywhere(newSeed) {
  // a simple XorShift implementation
  // inspired by https://stackoverflow.com/a/34659107/7207622
  this.a = 1664525;
  this.c = 1013904223;
  this.Integer_MAX_VALUE = 2147483647;
  // Defaults to current system time in milliseconds.
  if (newSeed === undefined) {
    var d = new Date();
    newSeed = d.getMilliseconds()%this.Integer_MAX_VALUE;
  }
  this.randomSeed(newSeed);
}

/**
 * Updates seed based on past value, returns new value.
 * 
 * Returns unsigned 32bit value stored in a long.
 * (named for cross-language compatability with Java nextLong)
 */
RandAnywhere.prototype.nextLong = function() {
  // The intermediate result fits in 52 bits, so no overflow
  this.seed = ((this.seed * 1664525 + 1013904223) | 0) >>> 0;
  return this.seed;
}
/**
 * Returns next long wrapped to the max (Java) integer value.
 * (for cross-language compatability)
 * Implemented so that calls to nextInt or nextLong stay in sync,
 * the next seed internally is always updated to the next long.
 */
RandAnywhere.prototype.nextInt = function() {
  var next = this.nextLong();
  return next%this.Integer_MAX_VALUE;
}
/**
 * Set the current seed.
 */
RandAnywhere.prototype.randomSeed = function(newSeed) {
  this.seed = newSeed%this.Integer_MAX_VALUE;
}
/**
 * Return a random float in the specified range.
 * 
 * 0 arguments: 0-1
 * 1 argument:  0-max
 * 2 arguments: min-max
 */
RandAnywhere.prototype.random = function(a, b) {
  if(!a && !b){
    rmin = 0;
    rmax = 1;
  } else if(!b){
    rmin = 0;
    rmax = a;
  } else {
    rmin = a;
    rmax = b;
  }
  return map(this.nextInt(), 0, this.Integer_MAX_VALUE, rmin, rmax);
}
2 Likes

#7

Thanks, @jeremydouglass! I’ll take a stab at plugging that into my code.

0 Likes