Warm tip: This article is reproduced from stackoverflow.com, please click
algorithm javascript math probability

How to generate random numbers biased towards one value in a range?

发布于 2020-03-28 23:15:25

Say, if I wanted to generate an unbiased random number between min and max, I'd do:

var rand = function(min, max) {
    return Math.floor(Math.random() * (max - min + 1)) + min;
};

But what if I want to generate a random number between min and max but more biased towards a value N between min and max to a degree D? It's best to illustrate it with a probability curve:

enter image description here

Questioner
c00000fd
Viewed
112
user1693593 2015-03-29 18:33

Here is one way:

  • Get a random number in the min-max range
  • Get a random normalized mix value
  • Mix random with bias based on random mix

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]).

Demo

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>