243

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 mainly within that range... Is it possible with JavaScript/jQuery?

Right now I'm just using the basic Math.random() * 100 + 1.

Salman A
  • 262,204
  • 82
  • 430
  • 521
Darryl Huffman
  • 2,499
  • 3
  • 18
  • 40
  • 7
    http://en.wikipedia.org/wiki/Random_number_generation#Generation_from_a_probability_distribution – Roko C. Buljan May 27 '15 at 20:18
  • 4
    http://codetheory.in/weighted-biased-random-number-generation-with-javascript-based-on-probability/ – Josh May 27 '15 at 20:19
  • 1
    possible duplicate: http://stackoverflow.com/questions/1527803/generating-random-numbers-in-javascript-in-a-specific-range – Mahedi Sabuj May 27 '15 at 20:26
  • 20
    I like where this question is going, but I think it should be more specific. Do you want a Z-distribution (bell curve), a triangle distribution, or some sort of sawtooth distribution? There are multiple possibilities for answering this question in my opinion. – Patrick Roberts May 27 '15 at 20:30
  • 12
    This can be done in javascript but for sure has nothing to do with jQuery... :) – A. Wolff May 27 '15 at 20:41
  • Might be too much to ask, but a function like this would be a truly awesome answer: [image](http://i.imgur.com/f2nbbq9.png) (where black is the output chance) – Jonathan May 27 '15 at 20:48
  • 1
    What about using normally distributed random values. Check [this link](http://stackoverflow.com/a/20161247/636942) for an awesome example. – Brad C May 27 '15 at 20:51
  • I believe you should be interested in the [Central Limit Theorem](http://en.wikipedia.org/wiki/Central_limit_theorem)... – Bakuriu May 28 '15 at 12:11
  • @Siguza please don't direct that question to maths, I do hate the trivial questions like that. – Alec Teal May 28 '15 at 14:52
  • Wait a minute, would the number be really "random" if it is generated by such decision making? – Aqeel Ashiq Jun 01 '15 at 21:42
  • 1
    @djaqeel You might want to define random. You're probably either confusing "uniformly random" with "random," or being pedantic and pointing out that computers can only generate pseudo-random numbers, not random numbers. – apnorton Jun 01 '15 at 21:43
  • @Siguza Although this question could be rewritten to be on-topic on Math.SE, we wouldn't like it as written in its current form; I believe it's better left here. (If it were on Math, we'd probably want to take out reference to the JS library, and phrase it more like "given a uniform, continuous random variable, how can I transform it into a random variable 'centered' around the 40-60 range?") – apnorton Jun 01 '15 at 21:43
  • make an array of [0,100] and extend this array by adding which numbers you want more and how many times, like add [40,60] range a couple of times, then choose random from this array. – Teoman shipahi Jun 03 '15 at 14:45

20 Answers20

402

The simplest way would be to generate two random numbers from 0-50 and add them together.

This gives a distribution biased towards 50, in the same way rolling two dice biases towards 7.

In fact, by using a larger number of "dice" (as @Falco suggests), you can make a closer approximation to a bell-curve:

function weightedRandom(max, numDice) {
    let num = 0;
    for (let i = 0; i < numDice; i++) {
        num += Math.random() * (max/numDice);
    }    
    return num;
}

Weighted random numbers

JSFiddle: http://jsfiddle.net/797qhcza/1/

BlueRaja - Danny Pflughoeft
  • 84,206
  • 33
  • 197
  • 283
  • 12
    This is an easy and quick solution, which can be easily weighted more, by adding more numbers e.g. 4 x (0-25) and will give you a nice bell-curve for the distribution! – Falco May 28 '15 at 12:29
  • 8
    This is a fantastic bit of code. I think I'm in love with it. Simple, quick, efficient; great answer. Thank you for posting this. – ctwheels May 28 '15 at 14:17
  • 14
    Great answer, but in case anybody intends to use this to generate Normal distribution, it is pretty inefficient (and you need to transform it to get the desired mean and standard deviation). A more efficient option would be the Box-Muller transform, which is quite easy to implement and understand if you know a bit of maths. – Brendon May 29 '15 at 15:04
  • Phenomenal answer. Could you point me towards resources for understanding at a mathematical/statistical level why this works, e.g. why two die bias towards 7? I understand it from the distribution of possibilities, but I feel there is probably a more elegant explanation. – Razi Shaban Jun 02 '15 at 20:38
  • 1
    @RaziShaban It's pretty intuitive: There's only one combination of die throws that adds up to 2 (just snake eyes), but there are 6 different combinations that add up to 7 (6-1, 5-2, 4-3, 3-4, 2-5, 1-6). If you generalize to N-sided dice, the peak is always N+1. – Barmar Jun 02 '15 at 20:56
  • 2
    @RaziShaban The study of [random variables](http://en.wikipedia.org/wiki/Random_variable) is a central part of statistics. The fact that as we increase the dice we approach a normal distribution is the famous [Central Limit Theorem](http://en.wikipedia.org/wiki/Central_limit_theorem). – BlueRaja - Danny Pflughoeft Jun 02 '15 at 21:03
  • "I understand it from the distribution of possibilities" - that's the simple explanation that is always used, but it does nothing to help explain this phenomenon in other contexts. Thanks for the number theory @BlueRaja-DannyPflughoeft exactly what I was looking for! – Razi Shaban Jun 03 '15 at 00:18
  • Here's [a fiddle](http://jsfiddle.net/Luxelin/u53wnL4z/) that makes it easier (less typing) to adjust the number of graphs/the `bellFactor`. – royhowie Jun 08 '15 at 05:04
49

You have some good answers here that give specific solutions; let me describe for you the general solution. The problem is:

  • I have a source of more-or-less uniformly distributed random numbers between 0 and 1.
  • I wish to produce a sequence of random numbers that follow a different distribution.

The general solution to this problem is to work out the quantile function of your desired distribution, and then apply the quantile function to the output of your uniform source.

The quantile function is the inverse of the integral of your desired distribution function. The distribution function is the function where the area under a portion of the curve is equal to the probability that the randomly-chosen item will be in that portion.

I give an example of how to do so here:

http://ericlippert.com/2012/02/21/generating-random-non-uniform-data/

The code in there is in C#, but the principles apply to any language; it should be straightforward to adapt the solution to JavaScript.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • 2
    I like this approach. Might want to add that there exists a javascript library that does generate Gaussian (and other non-normal) distributions: http://simjs.com/random.html – Floris May 31 '15 at 03:56
36

Taking arrays of numbers, etc. isn't efficient. You should take a mapping which takes a random number between 0 to 100 and maps to the distribution you need. So in your case, you could take f(x)=-(1/25)x2+4x to get a distribution with the most values in the middle of your range.

Distribution

Pang
  • 9,564
  • 146
  • 81
  • 122
iCaramba
  • 2,589
  • 16
  • 31
  • 2
    We don't actually know what distribution is needed. "Mainly 40-60" implies a bell-curve to me. – Lefty May 28 '15 at 08:06
  • yes you're right, maybe you need a better mapping, but that's trivial – iCaramba May 28 '15 at 08:45
  • 3
    I'll take your word for that because this is not my area of expertise. Could you adjust the function and display the new curve? – Lefty May 28 '15 at 08:59
  • 1
    @Lefty - Simplified bell curve for `x` between 0 and 100 (taken from [this question](http://stackoverflow.com/questions/13097005/easing-functions-for-bell-curves)): `y = (Math.sin(2 * Math.PI * (x/100 - 1/4)) + 1) / 2` – Sphinxxx May 28 '15 at 12:17
  • @Sphinxxx That is not a bell curve, it's a sin curve. A bell curve never touches the x-axis. – BlueRaja - Danny Pflughoeft Jul 17 '18 at 22:19
  • @BlueRaja-DannyPflughoeft That's true, but as I said, it's a **simplification**, and only **between 0 and 100**. – Sphinxxx Jul 17 '18 at 22:40
17

I might do something like setup a "chance" for the number to be allowed to go "out of bounds". In this example, a 20% chance the number will be 1-100, otherwise, 40-60:

$(function () {
    $('button').click(function () {
        var outOfBoundsChance = .2;
        var num = 0;
        if (Math.random() <= outOfBoundsChance) {
            num = getRandomInt(1, 100);
        } else {
            num = getRandomInt(40, 60);
        }
        $('#out').text(num);
    });
    
    function getRandomInt(min, max) {
        return Math.floor(Math.random() * (max - min + 1)) + min;
    }
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>

<button>Generate</button>
<div id="out"></div>

fiddle: http://jsfiddle.net/kbv39s9w/

Bitwise Creative
  • 4,035
  • 5
  • 27
  • 35
  • 5
    Maybe someone with more statistic detail can correct me, and although this does achieve what the OP is looking for (so I voted up), but this wouldn't truly choose a # out of bounds 20% of the time, correct? In this solution, 20% of the time you would have the opportunity to then choose a # from 1-100, which includes 40-60. Wouldn't this actually be (0.2 * 0.8) 16% to choose a # out of bounds, or am I missing something? – Josh May 27 '15 at 20:41
  • No, you're right. It's just my wording. I'll correct it. Thank you! – Bitwise Creative May 27 '15 at 20:45
  • 1
    @Josh - That is pretty spot on. Here is a simple proof of what that looks like http://jsfiddle.net/v51z8sd5/ . It will show the percent of numbers found out of bounds and hovers around 0.16 (16%). – Travis J May 27 '15 at 20:55
15

I needed to solve this problem a few years ago and my solution was easier than any of the other answers.

I generated 3 randoms between the bounds and averaged them. This pulls the result towards the centre but leaves it completely possible to reach the extremities.

Lefty
  • 391
  • 1
  • 13
  • 7
    How is this better / different than BlueRaja's answer? There, he takes the sum of (2,3,... any number you want) random numbers and takes the average. The result is identical to yours when you use a `BellFactor` of 3. – Floris May 31 '15 at 03:31
  • @floris well, I don't code in the c family of languages so that answer didn't even look as though it was doing the same thing as my answer until I just reread it now. I created my method by a little bit of trial-and-error and found that 3 randoms was the right number. Also, mine can be done in one line and still be easy to understand. – Lefty May 31 '15 at 07:33
  • The answer was written in JS, not "the c family of languages", so I am not sure I understand your comment... – Floris May 31 '15 at 12:17
  • 2
    Really? You don't think there is any similarity between JS and C? OK, well, lets just say I can't speak EITHER of those languages, nor Java, which, to me, are all similar compared to the languages that I am familiar with. – Lefty May 31 '15 at 13:41
  • peace. They have some similar structures although they are VERY different... I am surprised you would want to answer a question that is tagged with a language you don't speak. – Floris May 31 '15 at 14:10
  • 1
    Fair point, I was actually attracted only by the title as being something I'd solved myself and was quite proud of the way I did it. Again, I wasn't aware it is a js question until you just said that. Lucky really, because my technique isn't language dependent and some people seem to think it's a useful answer. – Lefty May 31 '15 at 14:37
  • 5
    JavaScript actually *is* a C-family language ... but ah well. – Joren May 31 '15 at 17:21
  • @Floris: Both methods are exactly equivalent. Example: in the case of `BellFactor == 2`, the code in my answer calculates `random()*(max/bellFactor) + random()*(max/bellFactor)`, whereas this answer will calculate `(random()*max + random()*max)/bellFactor`. It should be easy to see they're both the same. In fact, the `(max/bellFactor)` can be pulled out of the loop in my answer, giving the optimal `(random() + random())*(max/bellFactor)`. I haven't changed the code because I believe that would make it slightly more difficult to understand. – BlueRaja - Danny Pflughoeft Jun 01 '15 at 12:31
  • @BlueRaja-DannyPflughoeft that was my point precisely! - I was wondering about the need for a "second identical" answer... – Floris Jun 01 '15 at 12:39
14

It looks stupid but you can use rand twice:

var choice = Math.random() * 3;
var result;

if (choice < 2){
    result = Math.random() * 20 + 40; //you have 2/3 chance to go there
}
else {
    result = Math.random() * 100 + 1;
}
max890
  • 517
  • 2
  • 9
11

Sure it is possible. Make a random 1-100. If the number is <30 then generate number in range 1-100 if not generate in range 40-60.

Luka Krajnc
  • 915
  • 1
  • 7
  • 21
11

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.

Community
  • 1
  • 1
kasperd
  • 1,952
  • 1
  • 20
  • 31
8

What about using something like this:

var loops = 10;
var tries = 10;
var div = $("#results").html(random());
function random() {
    var values = "";
    for(var i=0; i < loops; i++) {
        var numTries = tries;
        do {
            var num = Math.floor((Math.random() * 100) + 1);
            numTries--;
        }
        while((num < 40 || num >60) && numTries > 1)
        values += num + "<br/>";
    }
    return values;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="results"></div>

The way I've coded it allows you to set a couple of variables:
loops = number of results
tries = number of times the function will try to get a number between 40-60 before it stops running through the while loop

Added bonus: It uses do while!!! Awesomeness at its best

ctwheels
  • 21,901
  • 9
  • 42
  • 77
8

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>
Salman A
  • 262,204
  • 82
  • 430
  • 521
7

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>
wolfhammer
  • 2,641
  • 1
  • 12
  • 11
6

Ok, so I decided to add another answer because I felt like my last answer, as well as most answers here, use some sort of half-statistical way of obtaining a bell-curve type result return. The code I provide below works the same way as when you roll a dice. Therefore, it is hardest to get 1 or 99, but easiest to get 50.

var loops = 10; //Number of numbers generated
var min = 1,
    max = 50;
var div = $("#results").html(random());

function random() {
    var values = "";
    for (var i = 0; i < loops; i++) {
        var one = generate();
        var two = generate();
        var ans = one + two - 1;
        var num = values += ans + "<br/>";
    }
    return values;
}

function generate() {
    return Math.floor((Math.random() * (max - min + 1)) + min);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="results"></div>
ctwheels
  • 21,901
  • 9
  • 42
  • 77
6

I'd recommend using the beta distribution to generate a number between 0-1, then scale it up. It's quite flexible and can create many different shapes of distributions.

Here's a quick and dirty sampler:

rbeta = function(alpha, beta) {
 var a = 0   
 for(var i = 0; i < alpha; i++)   
    a -= Math.log(Math.random())

 var b = 0   
 for(var i = 0; i < beta; i++)   
    b -= Math.log(Math.random())

  return Math.ceil(100 * a / (a+b))
}
Neal Fultz
  • 9,282
  • 1
  • 39
  • 60
5
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);
}
5

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.

Community
  • 1
  • 1
mg30rg
  • 1,311
  • 13
  • 24
  • 1
    If you're going to prefill an array of choices, you might also shuffle them afterward. Then you can just grab them in order until you run out. Shuffle again if/when you hit the end of the list. – Geobits May 29 '15 at 16:28
  • @Geobits Shuffling a list is far more resource-intensive task then randomly choosing one of its elements. It is only a good choice if the list has to be predictable. – mg30rg May 29 '15 at 18:04
  • 1
    But you only do it once per cycle of the list instead of every time. If you preprocess this (since you have a preprocessing step anyway, I assume that is fine), then it's very fast to get each number afterward. You can reshuffle whenever you have down time, or know you won't be needing a random number for a bit. Just offering it as an alternative, both have (dis)advantages. – Geobits May 29 '15 at 18:06
  • @Geobits If you do it your way, the "single probability" numbers will "fall out" and until resuffling they can't come up as a result. (i.e. if you simulate the throwing of two dices, you won't have the slightest chance to get number 2 more than twice.) – mg30rg May 29 '15 at 18:12
  • 1
    That's a *much* better reason not to use it, except for the rare applications where that is okay ;) – Geobits May 29 '15 at 18:15
  • My original solution offered the fastest way (is optimized for runtime against memory usage). My first answer (about resource-intensivity) was based on my mistake of believing you are proposing to shuffle the array after every pick. (I've misread your answer.) – mg30rg May 29 '15 at 18:21
4

You can use a helper random number to whether generate random numbers in 40-60 or 1-100:

// 90% of random numbers should be between 40 to 60.
var weight_percentage = 90;

var focuse_on_center = ( (Math.random() * 100) < weight_percentage );

if(focuse_on_center)
{
 // generate a random number within the 40-60 range.
 alert (40 + Math.random() * 20 + 1);
}
else
{
 // generate a random number within the 1-100 range.
 alert (Math.random() * 100 + 1);
}
Amir Saniyan
  • 13,014
  • 20
  • 92
  • 137
4

If you can use the gaussian function, use it. This function returns normal number with average 0 and sigma 1.

95% of this number are within average +/- 2*sigma. Your average = 50, and sigma = 5 so

randomNumber = 50 + 5*gaussian()
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
4

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;
}
Khaled.K
  • 5,828
  • 1
  • 33
  • 51
3

The best way to do that is generating a random number that is distributed equally in a certain set of numbers, and then apply a projection function to the set between 0 and a 100 where the projection is more likely to hit the numbers you want.

Typically the mathematical way of achieving this is plotting a probability function of the numbers you want. We could use the bell curve, but let's for the sake of easier calculation just work with a flipped parabola.

Let's make a parabola such that its roots are at 0 and 100 without skewing it. We get the following equation:

f(x) = -(x-0)(x-100) = -x * (x-100) = -x^2 + 100x

Now, all the area under the curve between 0 and 100 is representative of our first set where we want the numbers generated. There, the generation is completely random. So, all we need to do is find the bounds of our first set.

The lower bound is, of course, 0. The upper bound is the integral of our function at 100, which is

F(x) = -x^3/3 + 50x^2
F(100) = 500,000/3 = 166,666.66666 (let's just use 166,666, because rounding up would make the target out of bounds)

So we know that we need to generate a number somewhere between 0 and 166,666. Then, we simply need to take that number and project it to our second set, which is between 0 and 100.

We know that the random number we generated is some integral of our parabola with an input x between 0 and 100. That means that we simply have to assume that the random number is the result of F(x), and solve for x.

In this case, F(x) is a cubic equation, and in the form F(x) = ax^3 + bx^2 + cx + d = 0, the following statements are true:

a = -1/3
b = 50
c = 0
d = -1 * (your random number)

Solving this for x yields you the actual random number your are looking for, which is guaranteed to be in the [0, 100] range and a much higher likelihood to be close to the center than the edges.

arik
  • 28,170
  • 36
  • 100
  • 156
3

This answer is really good. But I would like to post implementation instructions (I'm not into JavaScript, so I hope you will understand) for different situation.


Assume you have ranges and weights for every range:

ranges - [1, 20], [21, 40], [41, 60], [61, 100]
weights - {1, 2, 100, 5}

Initial Static Information, could be cached:

  1. Sum of all weights (108 in sample)
  2. Range selection boundaries. It basically this formula: Boundary[n] = Boundary[n - 1] + weigh[n - 1] and Boundary[0] = 0. Sample has Boundary = {0, 1, 3, 103, 108}

Number generation:

  1. Generate random number N from range [0, Sum of all weights).
  2. for (i = 0; i < size(Boundary) && N > Boundary[i + 1]; ++i)
  3. Take ith range and generate random number in that range.

Additional note for performance optimizations. Ranges don't have to be ordered neither ascending nor descending order, so for faster range look-up range that has highest weight should go first and one with lowest weight should go last.

Community
  • 1
  • 1
ST3
  • 8,826
  • 3
  • 68
  • 92