Random Number Thread

This thread is for code and information on random number generation. For most uses, Pixelblaze’s built-in random() function is the right solution, but if you need something a little different, this is the place to start looking!

random(x) of course, generates a random number between 0 and x, exclusive, with a uniform distribution. I don’t know its implementation details. If it’s seeded rather than hardware, the seed is not available to patterns. If you call it multiple times, you’ll get a different sequence of random numbers every time.

So the first set of alternative methods I’ll give are seeded psuedo-random generators (PRNGs) that, for a given seed, always return the same sequence of numbers. This sort of generator is extremely useful for making repeatable procedural and generative graphics. The random procedural dungeons and worlds in many games are made this way, starting with a single number as a seed.

Here are a couple of algorithms to get things rolling :

xorshift - very lightweight and fast. Popular for games and small computers. This example is taken from @wizard’s “static random colors” library pattern.

xorshift Source Code
// 16 bit xorshift from 
// http://www.retroprogramming.com/2017/07/xorshift-pseudorandom-numbers-in-z80.html
var xs
function xorshift() {
  xs ^= xs << 7
  xs ^= xs >> 9
  xs ^= xs << 8
  return xs
}

// return a pseudorandom value between 0 and 1
function pseudorandomFraction() {
  return xorshift() / 100 % 1
}

Wolfram Rule 30 - This is kind of overkill, but if you need a stream of pseudo-random bits as well as random numbers of user specified word length, it’s is worth considering. It uses the chaotic central column of a cellular automaton as its random bitstream source. The pattern is one I did as part of @scruffynerf’s first tutorial task series.

Rule 30 Flasher in forum thread

Code for Task #1 - Random Random Roger Roger! - #8 by zranger1

All of the algorithms above generate random numbers in a uniform distribution. In most cases, that’s exactly what you want. But just in case… here’s code I cooked up a while ago that shows two different methods for generating normally distributed random numbers – numbers near the center of the range will come up frequently, and the extremes less frequently.

I was going to use this as part of a particle system to procedurally generate grass-like plants, which I haven’t yet got 'round to. (but I did get to dust off my 90’s paper copy of Knuth’s “The Art of Computer Programming”, which is always fun!)

RNG with Normal Distribution Source Code
var frameTime = 888;
export var speed = 60;
export var method = 0;        // 0 - Box-Muller, 1 - Marsaglia
export var val = 0;

export function sliderSpeed(v) {
  speed = 150 * (1-v)
}

export function sliderMethod(v) {
  method = (v > 0.5);
}

export function beforeRender(delta) {
  frameTime += delta;
  
  t1 = time(.1)
  if (frameTime > speed) {
    if (method) {
      val = gaussianRandomMarsaglia();
    } else {
      val = gaussianRandomBoxMuller();    
    }
    frameTime = 0;
  }
}

// display random pixels so you can see the distribution.  It should
// have a marked central tendency -- extremes should light up infrequently.
export function render(index) {
  var  h = t1 + index/pixelCount
  var  s = 1
  var  v = index == floor(val * pixelCount);
  hsv(h, s, v)
}

// largest number our regular random() function should generate
// used by both functions.
var MAX_RANDOM = 32763;   

// Box-Mueller transform method for generating normally distributed
// random numbers in the range 0..1. 
// https://en.wikipedia.org/wiki/Box%E2%80%93Muller_transform
var bmU,bmV;
var bmPhase = 0;
function gaussianRandomBoxMuller() {
	var z;

	if(!bmPhase) {
		bmU = (random(MAX_RANDOM) + 1) / (MAX_RANDOM + 2);
		bmV = random(MAX_RANDOM) / (MAX_RANDOM + 1);
		z = sqrt(-2 * log(bmU)) * sin(2 * PI * bmV);
	} else
		z = sqrt(-2 * log(bmU)) * cos(2 * PI * bmV);

	bmPhase = !bmPhase;

// scale to proper range -- the algorithm converges on 4.4 - -4.4
	return 0.5+(z/8.8);
}

// Marsaglia (Knuth) method for generating normally distributed
// random numbers from 0..1.
// Algorithm from Knuth's The Art of Computer Programming, vol 2
// sec 3.4.1, p 118. Attributed to G. Marsaglia. 
var v1, v2,ks;
var kPhase = 0;
function gaussianRandomMarsaglia() {
	var x,u1,u2;

	if(!kPhase) {
		while (1) {
			u1 = random(MAX_RANDOM) / MAX_RANDOM;
			u2 = random(MAX_RANDOM) / MAX_RANDOM;

			v1 = 2 * u1 - 1;
			v2 = 2 * u2 - 1;
			ks = v1 * v1 + v2 * v2;
		  if (ks < 1 && ks > 0) break;			
		}
		x = v1 * sqrt(-2 * log(ks) / ks);
	} else
		x = v2 * sqrt(-2 * log(ks) / ks);

	kPhase = !kPhase;
	
// scale to proper range -- the algorithm converges on 4.0 - -4/0
	return 0.5+(x/8);
}
1 Like

[moved this post to the Noise/Graphtoy thread as more in context]

Resources:

(Lists multiple triplets that are decent for 16bit, so multiple implementations is possible with one routine, changing variables))

Array shuffle function, moved from the Graphtoy thread. This particular version shuffles an array of 1D pixel coordinates, but can easily be adapted to shuffle any other 1D array. Uses the Fisher-Yates algorithm, by way of Knuth again.

// array to hold randomized pixel indices
var pixels = array(pixelCount);

// produces a randomized array of pixel indices.
function shufflePixelIndices() {
// initialize pixel index array in normal 0..pixelCount order.
  for (range = 0; range < pixelCount; range++) {
    pixels[range] = range;
  }
  
  // shuffle array coordinates
  for (var i = pixelCount - 1; i > 0; i--) {
    var r = floor(random(i+1));
    var tmp  = pixels[i];
    pixels[i] = pixels[r];
    pixels[r] = tmp;
  }
}
1 Like

and this was interesting too

and then the summary of a paper
McCullough, B. D. (2008). Microsoft Excel’s “Not The Wichmann–Hill” random number generators. Computational Statistics & Data Analysis, 52(10), 4587–4593. doi:10.1016/j.csda.2008.03.006

So if I get the below wrong, I’m in good company.

Modified for PB (untested by Scruffy)
the following equivalent code only uses numbers up to 30,323:

// s1, s2, s3 should be #s from 1 to 30,000 
var s1=seed1#
var s2=seed2#
var s3=seed3#
// OR YOU CAN USE
//var s1,s2,s3 // and then call whseed(#,#,#)
function whseed(v1,v2,v3) {s1=v1;s2=v2;s3=v3}

function whrand() {
   s1 = 171 * mod(s1, 177) - 2  * floor(s1 / 177) 
   s2 = 172 * mod(s2, 176) - 35 * floor(s2 / 176) 
   s3 = 170 * mod(s3, 178) - 63 * floor(s3 / 178) 
   return mod(s1/30269 + s2/30307 + s3/30323, 1)
}

just for fun, the original Wichmann–Hill code (in Fortran)

real function random()
    c
    c Algorithm AS 183 Appl. Statist. (1982) vol.31, no.2
    c
    c Returns a pseudo-random number rectangularly
    c distributed between 0 and 1.
    c
    c IX, IY and IZ should be set to integer values
    c between 1 and 30000 before the first entry.
    c
    c Integer arithmetic up to 30323 is required.
    c
    integer ix, iy, iz
    common /randc/ ix, iy, iz
    c
    ix = 171 * mod(ix, 177) - 2 * (ix / 177)
    iy = 172 * mod(iy, 176) - 35 * (iy / 176)
    iz = 170 * mod(iz, 178) - 63 * (iz / 178)
    c
    if (ix .lt. 0) ix = ix + 30269
    if (iy .lt. 0) iy = iy + 30307
    if (iz .lt. 0) iz = iz + 30323
    c
    c If integer arithmetic up to 5212632 is available, the
    c the preceding 6 statements may be replaced by:
    c
    c ix = mod(171 * ix, 30269)
    c iy = mod(172 * iy, 30307)
    c iz = mod(170 * iz, 30323)
    c
    c on some machines, this may slightly increase
    c the speed. the results will be identical.
    random = amod(float(ix) / 30269.0 + float(iy) / 30307.0 + float(iz) / 30323.0, 1.0)
    return
    end

so I got a chance to test this, and fixed the code above slightly (it was cut and paste issues only), but I’m noticing that the seeds go negative, while the original adds to ensure it’s always positive. Easy to fix, but unsure it matters. Likely we’d have to run tests to see if it does. I’ve made a test that tracks min/max, just to be sure…
After 6,400,000 calls, it’s max is 1.0 and it’s min is 0.000015, so not bad. Never hitting zero, but does hit 1.0.

I’ve added this to the pattern library, mostly so people can see a working demo. If anyone finds a bug, let me know.

Found this, and it seems like it’s adaptable to PB usage, both for PRNG usage, as well as PRNG analysis.

https://www.donnelly-house.net/programming/cdp1802/8bitPRNGcolor.html

The above WH PRNG code just displayed random pixels, which is annoying to look at… My current version (code to be posted sooner or later) now sets up as many distribution slots as pixels you have, and then colors each based on how often the random number falls into that slot. So it all starts off red in hue (0), and slowly transitions through the spectrum. So on a 256 pixel matrix, you can watch the colors shift, and if any particular slot (random number between 0, and just less than 1, *256) gets more (or less) hits over time, it will stand out. Seems to work well, and I can leave it running and if any pixel goes seriously off color, you know that the PRNG isn’t being as random as it should be.

I may setup this as a PRNG test suite.

Having a handful of seedable decent PRNG code options is the end goal.
Don’t do serious encryption/etc on your PB. :wink:

This website might be useful if you haven’t seen it already: https://www.pcg-random.org/

Interesting, but it’s a 64bit algo… and I haven’t found a nice simple JS version (I found JS, but none of them are too simple). Feel free to contribute a PB version.

I’m bummed. I got 70% done with a 16.16 fixed point implementation of PCG-XSH-RR that I’d love to share here and let someone try to finish, and either I deleted it or it’s not on the first N Pixelblazes that I plugged in, where N is an embarrassing number.

1 Like

ah, randograms

https://tom-kaitchuck.medium.com/designing-a-new-prng-1c4ffd27124d

I was plotting over 256 slots (16x16 matrix), but it looks like plotting to a 16x16, I’d be better off with calling the PRNG twice, for x and y, and then also adding a spacer option (so get X, skip N random numbers, get Y)

I’m going to use random(), the WH PRNG, xorshift, and one or two others I know are posted here or elsewhere, and compile them all as selectable into a RNG testbed pattern

Updated for Monday Randomondayess:

Needs some cleanup and more comments, but it works now, and it’s neat to play with.

  • Speed is how quickly the hues change…
  • Skip is if it should generate any numbers between the ones for X and Y… so X, 1, 2,3, Y
    • (reveals ‘hidden’ patterns in numbers, if any), use LCG algo to view example
  • Algo selects the Algo in use. There are 6 now, including built in random().
  • Duration is how often to reset field, uses a count of n*32000 cycles, which take 2-4 seconds or so
The 16x16 (or whatever size matrix you have) PRNG Randogram generator
// For testing random algos  -  version 0.5alpha by Scruffynerf
// plots XY (2 random numbers) with configurable skips (0-N)
// If you notice any pattern at all, your algo isn't random enough.

// Algos tested here:
// random()  -- built in, no seed needed
// whrand() -- Wichmann–Hill - needs 3 seeds
// xorshift()
// lcg()  -- absolutely is broken, the randograms show it...
// gaussianRandomBoxMullerrand() -- makes a nice circle, as it tends to find results in middle
// gaussianRandomMarsagliarand()  -- same circle

// largest number our regular random() function should generate
var MAX_RANDOM = 32763;   
export var rawmin, rawmax

// Box-Mueller transform method for generating normally distributed
// random numbers in the range 0..1. 
// https://en.wikipedia.org/wiki/Box%E2%80%93Muller_transform
var bmU,bmV;
var bmPhase = 0;
function gaussianRandomBoxMullerrand() {
	var z;

	if(!bmPhase) {
		bmU = (random(MAX_RANDOM) + 1) / (MAX_RANDOM + 2);
		bmV = random(MAX_RANDOM) / (MAX_RANDOM + 1);
		z = sqrt(-2 * log(bmU)) * sin(2 * PI * bmV);
	} else
		z = sqrt(-2 * log(bmU)) * cos(2 * PI * bmV);

	bmPhase = !bmPhase;
  if (rawmin > z) { rawmin = z }
  if (rawmax < z) { rawmax = z }

// scale to proper range -- the algorithm converges on 4.4 - -4.4 (4.55+ in my tests)
	return abs((z+4.56)/9.12)
}

// Marsaglia (Knuth) method for generating normally distributed
// random numbers from 0..1.
// Algorithm from Knuth's The Art of Computer Programming, vol 2
// sec 3.4.1, p 118. Attributed to G. Marsaglia. 
var v1, v2,ks;
var kPhase = 0;
function gaussianRandomMarsagliarand() {
	var x,u1,u2;

	if(!kPhase) {
		while (1) {
			u1 = random(MAX_RANDOM) / MAX_RANDOM;
			u2 = random(MAX_RANDOM) / MAX_RANDOM;

			v1 = 2 * u1 - 1;
			v2 = 2 * u2 - 1;
			ks = v1 * v1 + v2 * v2;
		  if (ks < 1 && ks > 0) break;			
		}
		x = v1 * sqrt(-2 * log(ks) / ks);
	} else
		x = v2 * sqrt(-2 * log(ks) / ks);

	kPhase = !kPhase;
  if (rawmin > x) { rawmin = x }
  if (rawmax < x) { rawmax = x }
	
// scale to proper range -- the algorithm converges on 4.0 - -4/0
	return abs((x+4)/8)
}

// lcg setup
var m = 25;
var a = 11;
var c = 17;
var z = random(32000);
  
function lcgrand() {
  z = (a * z + c) % m;
  return abs(z/m);
};


// 16 bit xorshift from 
// http://www.retroprogramming.com/2017/07/xorshift-pseudorandom-numbers-in-z80.html
export var xs = random(32000)

function xorshift() {
  xs ^= xs << 7
  xs ^= xs >> 9
  xs ^= xs << 8
  return xs
}

// return a pseudorandom value between 0 and 1
function xorshiftrand() {
  return abs(xorshift()) / 100 % 1
}


//whrand()
//    Wichmann–Hill is a pseudorandom number generator proposed in 1982 by Brian Wichmann and David Hill.
//    See https://forum.electromage.com/t/random-number-thread/1260/5 for more info
// s1, s2, s3 should be #s from 1 to 30,000 
export var whs1,whs2,whs3 

// WH random seeding
whs1 = floor(random(30000)+1)
whs2 = floor(random(30000)+1)
whs3 = floor(random(30000)+1)
// OR YOU CAN call whseed(#,#,#)
function whseed(v1,v2,v3) {whs1=v1;whs2=v2;whs3=v3}

function whrand() {
  whs1 = abs(171 * mod(whs1, 177) - 2  * floor(whs1 / 177))%30269
  whs2 = abs(172 * mod(whs2, 176) - 35 * floor(whs2 / 176))%30307 
  whs3 = abs(170 * mod(whs3, 178) - 63 * floor(whs3 / 178))%30323
  if (s1max < whs1) { s1max = whs1 }
  if (s2max < whs2) { s2max = whs2 }
  if (s3max < whs3) { s3max = whs3 }
  return mod(whs1/30269 + whs2/30307 + whs3/30323, 1)
}


export var calls,calls2  // for debugging
export var result, min, max // also for debugging
var min = 1
var max = 0
export var s1max, s2max,s3max

var boxes = pixelCount
var height = floor(sqrt(boxes))
var width = floor(sqrt(boxes))
var distribute = array(boxes)

export var speed = 0.0001
export var skip = 0
export var algo = 0
export var duration = 99


export function sliderSpeed(v){
  speed = (v/10)*(v/10)
}

export function sliderSkip(v){
  skip = floor(v*16)
}

export function sliderAlgo(v){
   algo = floor(v * 5)
}

export function sliderDuration(v){
   duration = floor(v * 100)
}

function getrandom(v) {
  calls += 1
  if (calls == 32001) {calls = 1; calls2 += 1}
  if (calls2 > duration){
    reset();
    calls2 = 0;
  }
  if (algo == 0) {
    result = floor(random(v))
  }
  if (algo == 1) {
    result = floor(whrand()*v)
  }
  if (algo == 2) {
    result = floor(xorshiftrand()*v)
  }
  if (algo == 3) {
    result = floor(lcgrand()*v)
  }
  if (algo == 4) {
    result = floor(gaussianRandomBoxMullerrand()*v)
  }
  if (algo == 5) {
    result = floor(gaussianRandomMarsagliarand()*v)
  }
  
  if (min > result) { min = result }
  if (max < result) { max = result }
  return result
}

function reset() {
  for (i = 0; i < boxes; i++) {
    distribute[i] = 0
  }
}

export function render(index) {
  wresult = getrandom(width)
  if (skip) {
    for (i = 1; i == skip; i++) {
      skipresult = getrandom(10);
    }
  }
  hresult = getrandom(height)
  box = hresult*height + wresult
  distribute[box] = (distribute[box] + speed)%1
  hsv(distribute[index], 1, distribute[index]*99)
}

Some of that code could be adapted to PB, I think.

maybe this too?