Get a random number focused on center

后端 未结 20 2988
离开以前
离开以前 2020-12-12 09:28

Is it possible to get a random number between 1-100 and keep the results mainly within the 40-60 range? I mean, it will go out of that range rarely, but I want it to be main

相关标签:
20条回答
  • 2020-12-12 09:37
    var randNum;
    // generate random number from 1-5
    var freq = Math.floor(Math.random() * (6 - 1) + 1);
    // focus on 40-60 if the number is odd (1,3, or 5)
    // this should happen %60 of the time
    if (freq % 2){
        randNum = Math.floor(Math.random() * (60 - 40) + 40);
    }
    else {
        randNum = Math.floor(Math.random() * (100 - 1) + 1);
    }
    
    0 讨论(0)
  • 2020-12-12 09:37

    The best solution targeting this very problem is the one proposed by BlueRaja - Danny Pflughoeft but I think a somewhat faster and more general solution is also worth mentioning.


    When I have to generate random numbers (strings, coordinate pairs, etc.) satisfying the two requirements of

    1. The result set is quite small. (not larger than 16K numbers)
    2. The result set is discreet. (like integer numbers only)

    I usually start by creating an array of numbers (strings, coordinate pairs, etc.) fulfilling the requirement (In your case: an array of numbers containing the more probable ones multiple times.), then choose a random item of that array. This way, you only have to call the expensive random function once per item.

    0 讨论(0)
  • 2020-12-12 09:39

    Here's a weighted solution at 3/4 40-60 and 1/4 outside that range.

    function weighted() {
    
      var w = 4;
    
      // number 1 to w
      var r = Math.floor(Math.random() * w) + 1;
    
      if (r === 1) { // 1/w goes to outside 40-60
        var n = Math.floor(Math.random() * 80) + 1;
        if (n >= 40 && n <= 60) n += 40;
        return n
      }
      // w-1/w goes to 40-60 range.
      return Math.floor(Math.random() * 21) + 40;
    }
    
    function test() {
      var counts = [];
    
      for (var i = 0; i < 2000; i++) {
        var n = weighted();
        if (!counts[n]) counts[n] = 0;
        counts[n] ++;
      }
      var output = document.getElementById('output');
      var o = "";
      for (var i = 1; i <= 100; i++) {
        o += i + " - " + (counts[i] | 0) + "\n";
      }
      output.innerHTML = o;
    }
    
    test();
    <pre id="output"></pre>

    0 讨论(0)
  • 2020-12-12 09:40

    There is a lot of different ways to generate such random numbers. One way to do it is to compute the sum of multiple uniformly random numbers. How many random numbers you sum and what their range is will determine how the final distribution will look.

    The more numbers you sum up, the more it will be biased towards the center. Using the sum of 1 random number was already proposed in your question, but as you notice is not biased towards the center of the range. Other answers have propose using the sum of 2 random numbers or the sum of 3 random numbers.

    You can get even more bias towards the center of the range by taking the sum of more random numbers. At the extreme you could take the sum of 99 random numbers which each were either 0 or 1. That would be a binomial distribution. (Binomial distributions can in some sense be seen as the discrete version of normal distributions). This can still in theory cover the full range, but it has so much bias towards the center that you should never expect to see it reach the endpoints.

    This approach means you can tweak just how much bias you want.

    0 讨论(0)
  • 2020-12-12 09:40

    Distribution

     5% for [ 0,39]
    90% for [40,59]
     5% for [60,99]
    

    Solution

    var f = Math.random();
    if (f < 0.05) return random(0,39);
    else if (f < 0.95) return random(40,59);
    else return random(60,99);
    

    Generic Solution

    random_choose([series(0,39),series(40,59),series(60,99)],[0.05,0.90,0.05]);
    
    function random_choose (collections,probabilities)
    {
        var acc = 0.00;
        var r1 = Math.random();
        var r2 = Math.random();
    
        for (var i = 0; i < probabilities.length; i++)
        {
          acc += probabilities[i];
          if (r1 < acc)
            return collections[i][Math.floor(r2*collections[i].length)];
        }
    
        return (-1);
    }
    
    function series(min,max)
    {
        var i = min; var s = [];
        while (s[s.length-1] < max) s[s.length]=i++;
        return s;
    }
    
    0 讨论(0)
  • 2020-12-12 09:41

    You can write a function that maps random values between [0, 1) to [1, 100] according to weight. Consider this example:

    0.0-1.0 to 1-100 by percentage weight

    Here, the value 0.95 maps to value between [61, 100].
    In fact we have .05 / .1 = 0.5, which, when mapped to [61, 100], yields 81.

    Here is the function:

    /*
     * Function that returns a function that maps random number to value according to map of probability
     */
    function createDistributionFunction(data) {
      // cache data + some pre-calculations
      var cache = [];
      var i;
      for (i = 0; i < data.length; i++) {
        cache[i] = {};
        cache[i].valueMin = data[i].values[0];
        cache[i].valueMax = data[i].values[1];
        cache[i].rangeMin = i === 0 ? 0 : cache[i - 1].rangeMax;
        cache[i].rangeMax = cache[i].rangeMin + data[i].weight;
      }
      return function(random) {
        var value;
        for (i = 0; i < cache.length; i++) {
          // this maps random number to the bracket and the value inside that bracket
          if (cache[i].rangeMin <= random && random < cache[i].rangeMax) {
            value = (random - cache[i].rangeMin) / (cache[i].rangeMax - cache[i].rangeMin);
            value *= cache[i].valueMax - cache[i].valueMin + 1;
            value += cache[i].valueMin;
            return Math.floor(value);
          }
        }
      };
    }
    
    /*
     * Example usage
     */
    var distributionFunction = createDistributionFunction([
      { weight: 0.1, values: [1, 40] },
      { weight: 0.8, values: [41, 60] },
      { weight: 0.1, values: [61, 100] }
    ]);
    
    /*
     * Test the example and draw results using Google charts API
     */
    function testAndDrawResult() {
      var counts = [];
      var i;
      var value;
      // run the function in a loop and count the number of occurrences of each value
      for (i = 0; i < 10000; i++) {
        value = distributionFunction(Math.random());
        counts[value] = (counts[value] || 0) + 1;
      }
      // convert results to datatable and display
      var data = new google.visualization.DataTable();
      data.addColumn("number", "Value");
      data.addColumn("number", "Count");
      for (value = 0; value < counts.length; value++) {
        if (counts[value] !== undefined) {
          data.addRow([value, counts[value]]);
        }
      }
      var chart = new google.visualization.ColumnChart(document.getElementById("chart"));
      chart.draw(data);
    }
    google.load("visualization", "1", { packages: ["corechart"] });
    google.setOnLoadCallback(testAndDrawResult);
    <script src="https://www.google.com/jsapi"></script>
    <div id="chart"></div>

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