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

后端 未结 4 1771
独厮守ぢ
独厮守ぢ 2020-12-08 01:08

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

var rand = function(min, max) {
    return Mat         


        
相关标签:
4条回答
  • 2020-12-08 01:29

    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.

    0 讨论(0)
  • 2020-12-08 01:33

    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>

    0 讨论(0)
  • 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>

    0 讨论(0)
  • 2020-12-08 01:52

    Fun: use the image as the density function. Sample random pixels until you get a black one, then take the x co-ordinate.

    enter image description here

    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.

    0 讨论(0)
提交回复
热议问题