\$\begingroup\$

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.