Say, if I wanted to generate an unbiased random number between min
and max
, I\'d do:
var rand = function(min, max) {
return Mat
Say when you use Math.floor(Math.random() * (max - min + 1)) + min;
, you are actually creating a Uniform distribution. To get the data distribution in your chart, what you need is a distribution with non-zero skewness.
There are different techniques to get those kinds of distributions. Here is an example of beta distribution found on stackoverflow.
Here is the example summarized from the link:
unif = Math.random() // The original uniform distribution.
And we can transfer it into beta distribution by doing
beta = sin(unif*pi/2)^2 // The standard beta distribution
To get the skewness shown in your chart,
beta_right = (beta > 0.5) ? 2*beta-1 : 2*(1-beta)-1;
You can change the value 1 to any else to have it skew to other value.
Just for fun, here's a version that relies on the Gaussian function, as mentioned in SpiderPig's comment to your question. The Gaussian function is applied to a random number between 1 and 100, where the height of the bell indicates how close the final value will be to N
. I interpreted the degree D
to mean how likely the final value is to be close to N
, and so D
corresponds to the width of the bell - the smaller D
is, the less likely is the bias. Clearly, the example could be further calibrated.
(I copied Ken Fyrstenberg's canvas method to demonstrate the function.)
function randBias(min, max, N, D) {
var a = 1,
b = 50,
c = D;
var influence = Math.floor(Math.random() * (101)),
x = Math.floor(Math.random() * (max - min + 1)) + min;
return x > N
? x + Math.floor(gauss(influence) * (N - x))
: x - Math.floor(gauss(influence) * (x - N));
function gauss(x) {
return a * Math.exp(-(x - b) * (x - b) / (2 * c * c));
}
}
var ctx = document.querySelector("canvas").getContext("2d");
ctx.fillStyle = "red";
ctx.fillRect(399, 0, 2, 110);
ctx.fillStyle = "rgba(0,0,0,0.07)";
(function loop() {
for (var i = 0; i < 5; i++) {
ctx.fillRect(randBias(0, 600, 400, 50), 4, 2, 50);
ctx.fillRect(randBias(0, 600, 400, 10), 55, 2, 50);
ctx.fillRect(Math.random() * 600, 115, 2, 35);
}
requestAnimationFrame(loop);
})();
<canvas width=600></canvas>
Here is one way:
Ie., in pseudo:
Variables: min = 0 max = 100 bias = 67 (N) influence = 1 (D) [0.0, 1.0] Formula: rnd = random() x (max - min) + min mix = random() x influence value = rnd x (1 - mix) + bias x mix
The mix factor can be reduced with a secondary factor to set how much it should influence (ie. mix * factor
where factor is [0, 1]).
This will plot a biased random range. The upper band has 1 as influence, the bottom 0.75 influence. Bias is here set to be at 2/3 position in the range. The bottom band is without (deliberate) bias for comparison.
var ctx = document.querySelector("canvas").getContext("2d");
ctx.fillStyle = "red"; ctx.fillRect(399,0,2,110); // draw bias target
ctx.fillStyle = "rgba(0,0,0,0.07)";
function getRndBias(min, max, bias, influence) {
var rnd = Math.random() * (max - min) + min, // random in range
mix = Math.random() * influence; // random mixer
return rnd * (1 - mix) + bias * mix; // mix full range and bias
}
// plot biased result
(function loop() {
for(var i = 0; i < 5; i++) { // just sub-frames (speedier plot)
ctx.fillRect( getRndBias(0, 600, 400, 1.00), 4, 2, 50);
ctx.fillRect( getRndBias(0, 600, 400, 0.75), 55, 2, 50);
ctx.fillRect( Math.random() * 600 ,115, 2, 35);
}
requestAnimationFrame(loop);
})();
<canvas width=600></canvas>
Fun: use the image as the density function. Sample random pixels until you get a black one, then take the x co-ordinate.
Code:
getPixels = require("get-pixels"); // npm install get-pixels
getPixels("distribution.png", function(err, pixels) {
var height, r, s, width, x, y;
if (err) {
return;
}
width = pixels.shape[0];
height = pixels.shape[1];
while (pixels.get(x, y, 0) !== 0) {
r = Math.random();
s = Math.random();
x = Math.floor(r * width);
y = Math.floor(s * height);
}
return console.log(r);
});
Example output:
0.7892316638026386
0.8595335511490703
0.5459279934875667
0.9044852438382804
0.35129814594984055
0.5352215224411339
0.8271261665504426
0.4871773284394294
0.8202084102667868
0.39301465335302055
Scale to taste.