Published
Edited
Aug 23, 2020
12 stars
Insert cell
Insert cell
Insert cell
Insert cell
pcgrandom = {
// Note that the index order [0, 1, 2, 3] is little-endian
const
eps = Math.pow(2, -32),
m0 = 0x7F2D, m1 = 0x4C95, m2 = 0xF42D, m3 = 0x5851, // 6364136223846793005
a0 = 0x814F, a1 = 0xF767, a2 = 0x7B7E, a3 = 0x1405, // 1442695040888963407
state = Uint16Array.from({length: 4}, () => Math.random() * 0x10000); // random seed

return function pcgrandom() {
// Advance internal state
const
s0 = state[0], s1 = state[1], s2 = state[2], s3 = state[3],
new0 = a0 + m0*s0 |0,
new1 = a1 + m0*s1 + (m1*s0 + (new0 >>> 16)) |0,
new2 = a2 + m0*s2 + m1*s1 + (m2*s0 + (new1 >>> 16)) |0,
new3 = a3 + m0*s3 + (m1*s2 + m2*s1) + (m3*s0 + (new2 >>> 16));
state[0] = new0, state[1] = new1, state[2] = new2; state[3] = new3;

// Calculate output function (XSH RR), uses old state
const
xorshifted = (s3 << 21) + ((s3 >> 2 ^ s2) << 5) + ((s2 >> 2 ^ s1) >> 11),
out_int32 = xorshifted >>> (s3 >> 11) | xorshifted << (-(s3 >> 11) & 31);
return eps * (out_int32 >>> 0);
}
}

// In the code above both new2 and new3 can overflow 32 bits, and the
// resulting carry bits are lost, but we don’t need to care because
// those bits are larger than our 64-bit number anyway.
//
// new1 can't overflow 32 bits because m0 and m1 are small:
// a1 + m0*s1 + m1*s0 + carry
// <= (2 + 0x7F2D + 0x4C95) * 0xFFFF == 0xCBC3343C
Insert cell
{
const n = 1e7;
const A = new Array(n).fill(0);
let time = +new Date;
for (let i = 0; i < n; i++) A[i] = pcgrandom();
time = (+new Date - time) / 1000;
return md`${(n / 1e6 / time).toFixed(1)} million pseudorandom numbers per second`
}
Insert cell
{
const n = 1e7;
const A = new Array(n).fill(0);
let time = +new Date;
for (let i = 0; i < n; i++) A[i] = pcgrandom_job();
time = (+new Date - time) / 1000;
return md`${(n / 1e6 / time).toFixed(1)} million pseudorandom numbers per second`
}
Insert cell
pcgrandom_job = {
// Note that the index order [0, 1, 2, 3] is little-endian
const
eps = Math.pow(2, -32),
lo = 0xFFFF,
m0 = 0x7F2D, m1 = 0x4C95, m2 = 0xF42D, m3 = 0x5851, // 6364136223846793005
a0 = 0x814F, a1 = 0xF767, a2 = 0x7B7E, a3 = 0x1405, // 1442695040888963407
state = Uint16Array.from({length: 4}, () => Math.random() * 0x10000); // random seed

return function pcgrandom(){
// Advance internal state
const
s0 = state[0], s1 = state[1], s2 = state[2], s3 = state[3],
new0 = a0 + (s0*m0 & lo),
new1 = a1 + (new0 >>> 16) +
((s0*m1 & lo) + (s0*m0 >>> 16)) +
(s1*m0 & lo),
new2 = a2 + (new1 >>> 16) +
((s0*m2 & lo) + (s0*m1 >>> 16)) +
((s1*m1 & lo) + (s1*m0 >>> 16)) +
(s2*m0 & lo),
new3 = a3 + (new2 >>> 16) +
((s0*m3 & lo) + (s0*m2 >>> 16)) +
((s1*m2 & lo) + (s1*m1 >>> 16)) +
((s2*m1 & lo) + (s2*m0 >>> 16)) +
(s3*m0 & lo);
state[0] = new0; state[1] = new1; state[2] = new2; state[3] = new3;

// Calculate output function (XSH RR), uses old state
const
xorshifted = (s3 << 21) + ((s3 >> 2 ^ s2) << 5) + ((s2 >> 2 ^ s1) >> 11),
out_int32 = xorshifted >>> (s3 >> 11) | xorshifted << (-(s3 >> 11) & 31);
return eps * (out_int32 >>> 0);
}
}
Insert cell
Array.from({ length: 1000 }, pcgrandom)
Insert cell
{
const
height = 500,
context = DOM.context2d(width, height);

animate(context.canvas, invalidation, () => {
context.fillStyle = "rgba(255,255,255,0.01)";
context.fillRect(0, 0, width, height);
context.fillStyle = "#000";
for (let i = 0; i < width; i++) {
context.fillRect(width * pcgrandom(), height * pcgrandom(), 1, 1);
}
});
return context.canvas;
}
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more