# Random number in a range, biased toward the low end of the range [duplicate]

I have an input of two values, let's say 1 and 10. Lesser numbers should overall occur more often in the output than higher numbers, while there is a peak in the probability at #2:

1: very common
2: extremely common
3: very common
4: common
5: somewhat common
...
10: extremely rare


How do I achieve this? My code until now looks like this:

function getValue($min,$max) {
return random($min,$max);
}

• Really interesting question. Check out this post: stackoverflow.com/questions/4420502/… – jgallant Feb 18 '16 at 12:47
• ...and this post. – Vaillancourt Feb 18 '16 at 12:53
• Do you just want to get whole numbers? Or any number in the range e.g. 0.5, 7.9834... – Malrig Feb 18 '16 at 13:22
• Define "very common" vs. "extremely common". If you generate a million numbers how many of each do you want in the result (roughly). – ratchet freak Feb 18 '16 at 13:51
• I give a gentle introduction to the mathematics of generating an arbitrary probability distribution when given a uniform probability distribution here: ericlippert.com/2012/02/21/generating-random-non-uniform-data -- the code is in C# but you can adapt the mathematics to the language of your choice. – Eric Lippert Feb 18 '16 at 17:40

Assuming you have a random() function that returns a uniformly-distributed numeric value in the interval [0, 1)...

(I see the edit attempt to "fix" the mismatched bracket above, but this is deliberate and carries specific meaning)

random() - random()


Gives a distribution that peaks at 0 and falls off toward -1 and 1.

abs(random() - random())


Peaks around 0 and falls off toward 1

 floor(abs(random() - random()) * (1 + max - min) + min)


Gives you a random number between min and max, with outputs closer to min being more common, falling off linearly toward the max.

Note as pointed out by Logan Pickup in the comments below, there's a slight mathematical artifact here if you're using this to generate continuous random numbers (ie. without the floor): the only way to get zero out of abs is to start with zero, but you can get a value of epsilon if the input is either positive or negative epsilon. So you have about half the chance of getting exactly zero as you have of getting the next larger representable number. If you're using this with floor after the abs to generate random integers, then this little quirk will be swamped and not noticeably affect the result.

Using inverse transform sampling you can get this same linear distribution (without the 0 artifact) with one random sample via the formula:

1 - sqrt(1 - random())


(and we can apply the same scale/offset/floor approach to get a corresponding discrete distribution)

You can find methods to get custom-shaped probability distributions in this answer or the links suggested in comments above.

• Ooh! That's clever! – Vaillancourt Feb 18 '16 at 13:45
• @user1306322 because random() is uniformly distributed on [0, 1), so random() * 2 - 1 is uniformly distributed on [-1, 1) - it doesn't have the non-uniformity OP has asked for. By taking two independent random rolls a and b, we introduce some non-uniformity: there are more ways to get 0 (all cases a == b) than there are to get 1 (only a == 1, b == 0 fits the bill - this is a simplification since 1 is actually outside the range of this function, but the extreme is easier to talk about) – DMGregory Feb 18 '16 at 15:47
• To expand on this answer: this trick works with random() returning float/double but it doesn't work if you use random integers, because the probability of returning min will be about half of what it should be. For continuous or high-resolution outputs, the probability of returning min is approximately 0 anyway so it doesn't matter. – Dietrich Epp Feb 18 '16 at 19:10
• @DietrichEpp I've updated the rounding function to floor, which should keep the distribution linear, including for the min & max buckets. – DMGregory Feb 18 '16 at 19:38
• Not that I'm complaining about the upvotes, but don't forget to spread the love a bit. ;) This was just a quick off-the-cuff answer - the power-based approaches in some of the other answers offer more flexibility in the falloff of the probability distribution, with similar simplicity. – DMGregory Feb 19 '16 at 15:51

Let's say "rand()" gives you a random number between 0 and 1 (inclusive).

pow(rand(), 2)


will give you an answer between 0 and 1 (inclusive), but the result is more likely to be close to zero, following a quadratic curve.

pow(rand(), 0.5)


will give you an answer between 0 and 1 (inclusive), but the result is more likely to be close to one, following a square root curve.

And since the min and max of the output are still 0 and 1, this distribution can be mapped to any range (given min, max, and the exponent p) like so:

result = min + (max - min) * pow(rand(), p)


Using different values of p can bias you towards the minimum or maximum value depending on your needs: (example of mapping a 0-1 range by raising x to different powers. From top to bottom: 1/4, 1/2, 2, 4)

Experiment with different powers to find what works for you. Just remember that bigger powers greater than one will bias you towards the minimum value, and smaller powers less than one but greater than zero will bias you towards the maximum value.

You can also swap max and min to flip the distribution of results, and if you want integer results, I'd use floor(x), increase max by one, and either use a rand() function that is exclusive at the top end (won't return 1.0) or clamp the final output to the range to filter out that rare case.

• This is the answer I was going to offer, only better illustrated +1! – Wren Feb 19 '16 at 16:51

Why not use a system similar to advantage/disadvantage as used in DnD 5e?

It boils down to: disadvantage: roll 2 (or any number of) dice and keep the lowest. advantage: roll 2 (or any number of) dice and keep the highest.

for 1 out of 2 dice this gives a linear chance decreasing as you get higher:

example: http://anydice.com/program/1227

for one out of more dice this gives a curve:

example: http://anydice.com/program/585d

And picking more dice out of the set favors numbers in the lower-middle of your range.

I hope this helps

• Was going to post this, but you beat me. :) Also, you might check out this rpg.SE post that uses a very similar method. – MichaelS Feb 19 '16 at 0:40

A naive and not efficient algorithm that would give you a linear distribution:

vector values;
int iterations = 1;
for ( val = max; val >= min; --val ) {
for ( i = 1; i <= iterations ; ++i )
values.push_back( val );

++iterations;
}

// The goal is to have in your values vector something like this for min = 1 and max = 4:
// [1, 1, 1, 1, 2, 2, 2, 3, 3, 4]

return values[random(0, values.size() - 1)];


This will give you what you need.

• nice idea, but really inefficient for bigger number ranges. :( – wernersbacher Feb 18 '16 at 12:42
• @wernersbacher you're absolutely right ;) That's why I said it in the first sentence! I'm sure someone else will come up with a better solution. – Vaillancourt Feb 18 '16 at 12:43
• We can bring it to O(n) I think. First loop, i=0; for(n=min to max){i+=n; values[n]=i} (If I did it right, you have an array with {1, (1+2), (1+2+3), etc.}.) Switch to for(n=max to min) so it's what the OP wants {max, (max+[max-1]), etc.}. Next, roll r=random(1, i) (inclusive, and i should equal the last value in the values array). Then, for(n=1 to values.length){if(i<=values[n]){return n}}. This way rolling 1 to a million just does 2 million iterations, instead of a million million. – MichaelS Feb 19 '16 at 0:55
• The generation of the list may be inefficient, but if its done once and stored as a static, its not so bad. The line return values[random(0, values.size() - 1)]; isn't inefficient. – Octopus Feb 19 '16 at 17:38

You can use a local variable to save a random call:

function F(min, max) {
var t = random(); //Assuming this returns a value between 0 and 1
return min + (max - min) * t * t;
}


This makes a non linear distribution where values close to min are more frequent. You can force even more extremal distribution by inserting t = t*t or user a pow with other values.

• "to save a random call"? Save from what? If the algorithm needs two calls, it needs to calls, and if it needs one call, it needs one call. I'm not sure why you'd "save" a call to random. – Vaillancourt Feb 18 '16 at 14:49
• @AlexandreVaillancourt To save one call to random() as compared to the formula in my answer which takes the difference of two calls. – DMGregory Feb 18 '16 at 14:56
• Or just do return min + (max - min) * pow(random(), 2). Still +1 for smart use of Uniform Product Distribution. – Dorus Feb 18 '16 at 15:16
• @AlexandreVaillancourt Some PRNGs are computationally intensive. But then pow() may be as well, depending on floating-point performance of a particular CPU and optimizing power of the compiler. Benchmark. – Damian Yerrick Feb 19 '16 at 23:07

If this is for a game, often you want to tweak values intuitively rather than rely on some existing distribution.

A good way to do this is to pick numbers from an array, and duplicate the values you want more than one. For example, here's some python code that does that. The nice thing about it is that you can see pretty easily the probability of the different options.

import random
values = [
1, 1, 1, 1, 1, 1,
2, 2, 2, 2, 2, 2, 2,
3, 3, 3, 3, 3, 3,
4, 4, 4, 4,
5, 5, 5, 5,
6, 6, 6,
7, 7,
8,
9,
10
]
random.choice(values)


Obviously this only makes sense with a small number of values.

Alternatively, if you have a probability distribution in mind you can use Inverse Transform Sampling. It can take the uniform random number from 0 to 1 and map it so that it matches any probability distribution. It's requires some maths, but if you have some good libraries they should be able to help you out.

• This is effectively Alexandre Vaillancourt's answer, but without the method to generate the desired table without specifying every entry explicitly. – DMGregory Feb 19 '16 at 0:16
• @DMGregory: Two differences. First, this one is more time efficient (the data already exists at runtime, and doesn't need to be created). Second, you can tweak the curve precisely as you see fit, instead of trying to come up with some formula or logic that approximates the curve you're going for. – MichaelS Feb 19 '16 at 0:46
• The curve here only matches what you want only if the probabilities of each numbers are a small integer ratio with each other. There are many cases where you may want a non-integer distribution probabilities. In this particular implementation, for example, 8,9,10 have the exact same probability of occurring. – Lie Ryan Feb 20 '16 at 11:39

If you have uniformly distributed random numbers, you can use those as the parameter to a mathematical function that has a exponential-like shape, so that most numbers in the random range yield a low value. The initial random input can be normalized, then scaled to your range, so the function is well-behaved.

For example, the following function, if given normalized values from 0 to 1 as input:

You can tweak the parameters to make this cover the whole output range from 0 to 1. And if you need higher numbers, just multiply or offset the result.

I didn't check this mathematically, but it seems good enough experimentally.

• I like this answer because it made me understand what the functions of the other answers actually do mathematically. – problemofficer Feb 19 '16 at 22:36

I recommend using the function:

where s and g represent your minimum and maximum values, respectively, R is a random number between 0 and 1 (inclusive) and n is a pre-determined constant. The notation ||x|| represents the nearest integer to x.

An example in PHP 5:

function biasedRandom($min,$max) {
$power = 2; # lcg_value() returns a random number between 0 and 1$bias  = pow(lcg_value(), $power); return round($min + ($max-$min) * $bias); }  In an IPython notebook I ran my function 1000 times each for different powers. It generated this graph, where each value (x-axis) is plotted against the probability of it occurring (y-axis): The graph shows that as p, the power which we raise the random number to, increases, the 'steepness' of the graph also increases. That is to say, a higher value of p will make the smallest value, in this case 1, to have a much higher probability of occurring than the highest value, 10. See the green line, where p equals 8. In the case of the blue line, where p is only equal to 2, the bias is far less extreme, and all numbers are closer in probability. This animation demonstrates how the probabilities of each value change with different values of p: I used this python function to generate each of the biased random numbers used as data in all of the following graphs. These box-plots each show the distribution of 500 values generated by my function, with powers of (from top to bottom) 2, then 2.5 then 4. The box-plots clearly show that smaller numbers are more common than larger numbers (the medians are all less than 5.5), and that there is a higher density of values towards the lower end of the spectrum. However, one can also see how high values (all the way up to ten) still emerge sometimes. If we compare the box-plots we can again see that a lower power (the top-most box-plot has the lowest power, in this case 2) leads to a more even spread of values, with high values appearing more frequently and low values appearing less frequently than when using higher powers. Without rounding the function's final result, we can obtain the following visualisation, which shows clearly the higher density of smaller numbers. Values are plotted as they are obtained by the function. The above image shows the distribution of results for powers 8, 16 and 32. The layout is intended to represent a number line, with 1 on the far left and 10 on the right. • This function seems to be exactly what I want for my own use-case, so thanks for that! I noticed that p=0 always produces the maximum value for the range (expected, since x^0=1), and higher values for p bias towards lower values for the result. However, p=1 gets odd. While overall it's close to an even distribution of values across the range, the minimum and maximum values are each approximately half as likely as the rest of the values in the range, and I'm not sure why. Screenshot of my Mathematica notebook demonstrating the phenomenon: i.imgur.com/wByvP0s.png – Brian S Jun 21 '19 at 23:58 • @BrianS I know why and I know how to fix it: you will find that the probability of getting a min (or equivalently, a max) value outputted is exactly half of getting any other value. That is because the function I proposed just rounds a distribution of real (decimal) numbers, and that means anything between e.g. (in your example) 0.5 to 1.5 will be outputted as 1, but to get 0 outputted you half as many possible values: only 0 to 0.5. To fix this you can use Round[min - 0.5 + (max-min + 1) * bias]. – theonlygusti Jun 22 '19 at 0:42 Couple other ideas: # Folded Normal Distribution. This is achieved by generating a standard Gaussian random, then running it through abs(). Values greater than 1 are possible, feel free to deal with it however you choose; clamp it, re-randomize, ignore. # Wald Distribution. Also known as the Inverse Gaussian. 0 as a result has a very low probability, while values near 0 have a very high probability, trailing off towards infinity (see above about clamping). • Really cool concepts – theonlygusti Feb 20 '16 at 15:58 • @theonlygusti Yup, I actually implemented the Wald distribution in the Custom Ore Gen mod for Minecraft so I could use it in my ore distributions. Long, generally straight veins with rare, but hard, corners. – Draco18s no longer trusts SE Feb 20 '16 at 17:35 • That's pretty cool, but surely the fact that y=0 when x=0 would have complicated its implementation into your mod? Doesn't the slope down towards the origin mess anything up? – theonlygusti Feb 20 '16 at 22:12 • @theonlygusti The random is a relative modifier, not an absolute one. Also, remember that Y here is a probability value, not a return value. X is the return value from the rand_wald() function. – Draco18s no longer trusts SE Feb 21 '16 at 0:47 The important thing is to re-map the function from 0-1 to what you desire. A big part of this depends on just what distribution you want from it. I suspect you want something that has an exponential decaying type curve in it, although you have to really ask yourself what you want the distribution to look like. A couple of possibilities are an exponential decay function, and a inverse function, although in both cases the edge cases need to be well studied. I'm not going to give you the code, but rather the formulas. Assume R is a random number between 0-1. Inverse Linear: while (R<.1) { R*=10; } return ceil(10-(1/(R))  Exponential Decay: // Will likely need to work on some edge cases return 10-10*(e-e^R)  To test it, I suggest that instead of using real random numbers, iterate through, say, every 0.01 value of R, and see if the distribution is correct. If there are points that lie outside your range, find a way to remove them. Good luck! One can use a simple formula like so: function getValue(min,max) { return min-1+Math.pow((max-min+1),Math.random()); }  When given min as 1 and max as 10 this generates a number from 1.0 to 10.0 where 10.0 is rare. Remember Math.random() generates a uniform random number from 0.0 to 1.0 Here is some R code to show you the results: (function(min,max){ x=min-1+(max-min+1)^runif(10000); print(summary(x)); hist(x) })(4,28)  • Interesting concept, but doesnt this achieve the opposite effect? 1111 1111(max) XOR 0000 0001(rand) would give 1111 1110 right? the most significant bit has a 50% chance of staying up thus there would be a tendency of producing high numbers, also max doesn't limit the outcome of rand. I like the concept though. Would & achieve the desired effect? – Niels Feb 19 '16 at 11:58 • I meant hat to be the power operator. I've been doing a lot of R programming recently. I changed it to POW(x,y) – Chris Feb 19 '16 at 14:18 You could call the random function twice:  function getValue($min, $max) { return random($min,random($min,$max));
}


For numbers 1 - 5, the probabilities for getValue(1,5) are
1 = 137 out of 300
2 = 72 out of 300
3 = 47 out of 300
4 = 27 out of 300
5 = 12 out of 300

• This is effectively a subset of user2645227's answer. – DMGregory Feb 19 '16 at 0:18