Using Javascript, how can I generate random numbers that are skewed towards one end or the other of the distribution? Or ideally an point within the range?
For context: I'm creating a UI that has uses a grid of random grey squares. I'm generating the grey's RGB values using Math.random()
but would like to be able to skew the greys to be on average darker or lighter while still having the full range from black to white represented.
(I think this is a similar question to Skewing java random number generation toward a certain number but I'm working with Javascript...)
Any help greatly appreciated.
Raise Math.random()
to a power to get a gamma curve - this changes the distribution between 0 and 1, but 0 and 1 stay constant endpoints.
var r= Math.pow(Math.random(), 2);
var colour= 'rgb('+r*255+', '+r*255+', '+r*255+')';
For gamma>1, you will get darker output; for 0<gamma<1 you get lighter. (Here, '2' gives you the x-squared curve; the equidistant lightness would be '0.5' for the square-root curve.)
This seems a little crude and less graceful than @bobince's answer, but what the hell.
//setup
var colours = [], num_colours = 10, skew_to = 255, skew_chance = 20;
//get as many RGB vals as required
for (var i=0; i<num_colours; i++) {
//generate random grey
var this_grey = Math.floor(Math.random() * 256);
//skew it towards the @skew_to endpoint, or leave as-is?
if (Math.floor(Math.random() * 100) >= skew_chance && this_grey != skew_to) {
//skew by random amount (0 - difference between curr val and endpoint)
var skew_amount = Math.floor(Math.random() * Math.abs(this_grey - skew_to));
this_grey += ' (skewed to '+(skew_to < this_grey ? this_grey - skew_amount : this_grey + skew_amount)+')';
}
colours.push(this_grey);
}
console.log(colours);
Essentially it generates random greys then decides, based on probably specified (as a percentage) in skew_chance
, whether to skew it or not. (In case you wanted to make this occasional, not constant). If it decides to skew, a random number is then added or subtracted from the grey value (depending on whether the skew endpoint is under or above the current value).
This random number is a number between 0 and the absolute difference between the current value and the endpoint, e.g. if current value is 40, and the endpoint is 100, the number added would be between 0 and 60.
Like I say, @bobince's answer is somewhat, er, more graceful!
[This might be a little different approach.]
This approach deals with getting the number in the following fashion:
random = numberToSkewTo + random(-1,1)*stdDeviation
Where:
numberToSkewTo
is the number you want to skew towards.
stdDeviation
is the deviation from numberToSkewTo
numberToSkewTo + abs(stdDeviation) <= MAX_NUMBER
and
numberToSkewTo - abs(stdDeviation) >= MIN_NUMBER
What the following code does is, it pick a random number around the given number with constantly increasing standard deviations. It returns the average of results.
function skew(skewTo,stdDev){
var rand = (Math.random()*2 - 1) + (Math.random()*2 - 1) + (Math.random()*2 - 1);
return skewTo + rand*stdDev;
}
function getRandom(skewTo){
var difference = Math.min(skewTo-MIN_NUMBER, MAX_NUMBER-skewTo);
var steps = 5;
var total = 0.0;
for(var i=1; i<=steps; i++)
total += skew(skewTo, 1.0*i*difference/steps);
return total/steps
}