Random number generator with higher probabilities of giving low values?
How can I generate a pseudo-random number (preferably in Lua), where the generator has a higher probability of giving small numbers?
In my case, I want to give a random score in a game, where it's common to obtain the lower scores, but higher ones appear rarely. I've seen weighted random number generators that use a table, but it doesn't fit my plan. I just want to specify the minimum (0) the maximum (variable) and ensure most numbers stay low.
I am sure this is possible with a simple mathematical operation, but I cannot remember which one it was. Like to filter the开发者_StackOverflow社区 regular output of math.random, no need for a really random generator.
Try math.floor(minscore+(maxscore-minscore)*math.random()^2)
. Adjust the power to suit your desired distribution.
I found Ihf's answer very useful, and I create a C# method for it:
private int GetRandomNumber(int max, int min, double probabilityPower = 2)
{
var randomizer = new Random();
var randomDouble = randomizer.NextDouble();
var result = Math.Floor(min + (max + 1 - min) * (Math.Pow(randomDouble, probabilityPower)));
return (int) result;
}
If probabilityPower
is above 1, lower values will be more common than higher values.
If it's between 0 to 1, higher values will be more common than lower values.
If it's 1, the results will be in a general randomness.
Examples (all with 1 million iterations, min = 1, max = 20 ):
probabilityPower = 1.5
1: 135534 (13.5534%)
2: 76829 (7.6829%)
3: 68999 (6.8999%)
4: 60909 (6.0909%)
5: 54595 (5.4595%)
6: 53555 (5.3555%)
7: 48529 (4.8529%)
8: 44688 (4.4688%)
9: 43969 (4.3969%)
10: 44314 (4.4314%)
11: 40123 (4.0123%)
12: 39920 (3.992%)
13: 40466 (4.0466%)
14: 35821 (3.5821%)
15: 37862 (3.7862%)
16: 35222 (3.5222%)
17: 35902 (3.5902%)
18: 35202 (3.5202%)
19: 33961 (3.3961%)
20: 33600 (3.36%)
probabilityPower = 4
1: 471570 (47.157%)
2: 90114 (9.0114%)
3: 60333 (6.0333%)
4: 46574 (4.6574%)
5: 38905 (3.8905%)
6: 32379 (3.2379%)
7: 28309 (2.8309%)
8: 27906 (2.7906%)
9: 22389 (2.2389%)
10: 21524 (2.1524%)
11: 19444 (1.9444%)
12: 19688 (1.9688%)
13: 18398 (1.8398%)
14: 16870 (1.687%)
15: 15517 (1.5517%)
16: 15871 (1.5871%)
17: 14550 (1.455%)
18: 14635 (1.4635%)
19: 13399 (1.3399%)
20: 11625 (1.1625%)
probabilityPower = 1
1: 51534 (5.1534%)
2: 49239 (4.9239%)
3: 50955 (5.0955%)
4: 47992 (4.7992%)
5: 48971 (4.8971%)
6: 50456 (5.0456%)
7: 49282 (4.9282%)
8: 51344 (5.1344%)
9: 50841 (5.0841%)
10: 48548 (4.8548%)
11: 49294 (4.9294%)
12: 51795 (5.1795%)
13: 50583 (5.0583%)
14: 51020 (5.102%)
15: 51060 (5.106%)
16: 48632 (4.8632%)
17: 48568 (4.8568%)
18: 50039 (5.0039%)
19: 49863 (4.9863%)
20: 49984 (4.9984%)
probabilityPower = 0.5
1: 3899 (0.3899%)
2: 5818 (0.5818%)
3: 12808 (1.2808%)
4: 17880 (1.788%)
5: 23109 (2.3109%)
6: 26469 (2.6469%)
7: 33435 (3.3435%)
8: 35243 (3.5243%)
9: 42276 (4.2276%)
10: 47235 (4.7235%)
11: 52907 (5.2907%)
12: 58107 (5.8107%)
13: 63719 (6.3719%)
14: 66266 (6.6266%)
15: 72708 (7.2708%)
16: 79257 (7.9257%)
17: 81830 (8.183%)
18: 87243 (8.7243%)
19: 90958 (9.0958%)
20: 98833 (9.8833%)
probabilityPower = 0.4
1: 917 (0.0917%)
2: 3917 (0.3917%)
3: 3726 (0.3726%)
4: 10679 (1.0679%)
5: 13092 (1.3092%)
6: 17306 (1.7306%)
7: 22838 (2.2838%)
8: 29221 (2.9221%)
9: 35832 (3.5832%)
10: 38422 (3.8422%)
11: 47800 (4.78%)
12: 53431 (5.3431%)
13: 63791 (6.3791%)
14: 69460 (6.946%)
15: 75313 (7.5313%)
16: 86536 (8.6536%)
17: 95082 (9.5082%)
18: 103440 (10.344%)
19: 110203 (11.0203%)
20: 118994 (11.8994%)
I'd just transform the values of a standard random function, like this:
r1=math.random(0,255)
r2=math.exp(math.random(0,255))
You'll need to take into account your bounds, but you'll have something with a lot of low values, and few higher ones.
This may not be what you're looking for, since it's not a smoothly biased bell curve, but why not create two steps? Define a probability of getting a lower-range score and if you match it, your range is the lower range. Otherwise, your range is from the top of the low range to the end of the high range.
The net effect is that you'd usually get a low score, but sometimes you'd get high scores. I bet it would look pretty good, and is very simple.
What do you think?
function weighted_random (weights)
local summ = 0
for i, weight in pairs (weights) do
summ = summ + weight
end
local value = math.random (summ)
summ = 0
for i, weight in pairs (weights) do
summ = summ + weight
if value <= summ then
return i, weight
end
end
end
How to call:
local elements = {"a", "b", "c", "d"} -- elements
local weights = {40, 24, 22, 14} -- weights of elements
local n = weighted_random (weights) -- returns 1, 2, 3 or 4
local element = elements[n] -- returns "a", "b", "c" or "d"
-- with chances: 40/100, 24/100, 22/100, 14/100, as in weights
精彩评论