SRL Archive - SRL Snippets

Weighted random number


Kevin

2013-02-01 11:25:29

The idea of a weighted random number is to be able to have a random number in some range, but have the chances of the result actually center around any desired section of the range, with likelihood of that centering also variable. This seems a little hard to explain in simple words, so I'll give an example.

IE: A random number between 150 and 350, with a likelihood of being 10 times more likely to be near 300 than 150.

This is valuable in cases where you want to click a range of area (some button), and want the clicking to possibly click anywhere, while still more likely to be around the center than the edges (although you could weight an edge instead of the center in some other desireable cases as well).

I have no idea if anyone else cares to use this, but here it is. I personally use it mostly as a method of staying within a dedicated SPS area with my center as the most likely area.


//returns -1 as an error, sorry if that's in your desired range.
function WeightedRandom(min, max, weight, weightedNumber: integer): integer;
var
total, currentWeight, differencePer: extended;
i, arrayOffset, rand: integer;
Range: TExtendedArray;
begin
if((min > max) or (weightedNumber > max) or (weightedNumber < min))then
begin
result:= -1;
end else if (min = max)then
begin
result:= min;
end else
begin
SetLength(Range, (max - min+1));
total:= 0;
differencePer:= (weight - 1);//Forces it to an extended number for calculation at the next line
if((max - weightedNumber) > (weightedNumber - min)) then
begin
differencePer:= differencePer / (max - weightedNumber);
currentWeight:= weight - ((weightedNumber - min) * differencePer);
end else
begin
differencePer:= differencePer / (weightedNumber - min);
currentWeight:= 1;
end;
for i:= min to max do
begin
arrayOffset:= i - min;
Range[arrayOffset]:= currentWeight;//((arrayOffset) * currentWeight);
if(i=weightedNumber)then
begin
differencePer:= -1 * differencePer;
end;
total:= total + currentWeight;//Range[arrayOffset];
currentWeight:= currentWeight + differencePer;
end;
i:= round(total);
rand:= randomrange(0, i+1);
total:= 0;
i:= 0;
while(total < rand)do
begin
total:= total + Range[i];
Inc(i);
end;
if(i=0)then
begin
result:= min;
end else
result:= i-1+min;
end;
end;

DannyRS

2013-02-01 11:34:31

I normally do something like


Case Random(100) Of
0..50: MouseBox(75,75,75,75);
50..75: MouseBox(100,100,100,100);
75..100: MouseBox(120,120,120,120);
End;

I'm crap at math, nice job maybe I'll give it a try some time, is the calculation ever going to be slow? Don't want to slow down a script ;)

Kevin

2013-02-01 11:38:24

I thought of doing something like what you show, but that doesn't work in small amounts of codes over hundreds of points (100 line long case statement sounds annoying).

The calculation is O(2n) time, which means longer ranges passed take longer, but from my testing, it can handle ranges of about 500 before it hits 1ms of run-time (although 2500 range hit 15ms of run-time on average).

Weight does not affect run-time at all, and I use it in a range of about 200x200 points. So I call it twice and get a point within 40,000 points in about 1 millisecond :D

DannyRS

2013-02-01 11:53:42

Only did it to 'simulate' a curve of random box, so over 100's of hours it would look slightly more human, but something like this would be better i guess :p

riwu

2013-02-01 20:25:25

GaussRand()? Imitates a normal distribution.

Kevin

2013-02-01 22:13:52

From: riwu
GaussRand()? Imitates a normal distribution.


Admittedly I was unaware of GaussRand before now… however that won't work in edge cases wherein you may want a distribution towards one of the ends more akin to a Log-Normal distribution. However, that does show me a different way I could handle the distribution for an exponential based distribution as opposed to the laplace-esque distribution I have.

riwu

2013-02-02 00:21:21

From: nivek1989
Admittedly I was unaware of GaussRand before now… however that won't work in edge cases wherein you may want a distribution towards one of the ends more akin to a Log-Normal distribution. However, that does show me a different way I could handle the distribution for an exponential based distribution as opposed to the laplace-esque distribution I have.

You could set the mean at the edge, and reroll the function if the result exceeds edge. So it will be like a normal distribution with 1 side being cut off.

Kevin

2013-02-03 01:38:01

From: riwu
You could set the mean at the edge, and reroll the function if the result exceeds edge. So it will be like a normal distribution with 1 side being cut off.


That removes all possibilities for the cut-off side in that case. I'm still aiming to keep the edge cases, just have them be vastly less likely to occur.

I will likely rewrite this for an exponential distribution however anyway, depending on the run-time of that adjustment.