# Random.Range with decreasing probability

Hi folks,
I’m trying to write a method that basically works like Random.Range(a: int, b: int): int, with the difference that the probability of a possible result quadratically decreases towards b.
I hope you guys have some math-voodoo for situations like this because my head starts to hurt everytime I try to think of a possible solution.

Here’s one way to do it:

``````var startValue = 0;
var endValue = 100;

function Update () {
Debug.Log(Random.Range(startValue, endValue));
startValue += 1;
if (startValue >= endValue) startValue = endValue - 1;
}
``````

As it is, you’ll be getting nothing but ‘99’ pretty quickly. You could wrap the incrementing into a function so you’d have more control over speed.

Now, if you need the lower values to be returned occasionally, but with decreasing frequency, you could have two random functions and randomly call one or the other, one that returns lower numbers, one that returns higher, and change the rate at which these functions are called over time.

Maybe something like this:

``````var endValue = 1;

function getLowNumber(){
return Random.Range(0,50);
}

function getHighNumber(){
return Random.Range(50,100);
}

function Update(){
if (Random.Range(0, endValue) == 0){
Debug.Log(getLowNumber());
endValue += 1;
}
else{
Debug.Log(getHighNumber());
}
}
``````

Ah, I missed the bit where you are trying to write a method to to this.

Here’s one possible solution:

``````class MyRandom{
private var startValue : int;
private var endValue : int;
private var result : int;
private var increment : float = 0.0;
function MyRandom(n1 : int, n2 : int){
startValue = n1;
endValue = n2;
}
function DecreasingRange(inc : float){
increment += inc;
result = Random.Range(startValue, endValue);
if (increment >= 1.0){
startValue = Mathf.Min(startValue + 1, endValue - 1);
increment = 0.0;
}
return result;
}
}
``````

Call with:

``````var rand = MyRandom(0,100);
function Update () {
Debug.Log(rand.DecreasingRange(.01)); //allows you to adjust rate
}
``````

Your code is either so advanced that it goes straight over my head or it’s not exactly what I want.

When I call the method with the parameters a = 1 and b = 100 I expect a result between 1 and 100. However, the probability of each possible result decreases the higher the potential result is. This means that it is far more probable to get 1 as a result than 10, which is more probable than 11 and unbelievably more probable than 100.

The method does not interpolate over time or something like this, it’s only used occasionally.

For now I use a weighted random number appraoch with a prepopulated array of probabilities, but I’d prefer a shiny, more elegant version :~)

Yeah, I misunderstood what you were trying to accomplish.

Something like this should greatly favor lower numbers:

``````function getNumber(){
while (true){
number = Random.Range(1,101);
if (Random.Range(1, number+1) == number) return number;
}
}

function Update(){
Debug.Log(getNumber());
}
``````

If 1 is the number, it makes it thru 100% of the time.
If 2 is the number, it makes it thru 50% of the time.
If 3 is the number, it makes it thru 33% of the time.
And so on…

If you want to make it even harder for larger numbers to get thru, change the fourth line of code to:

``````		if (Random.Range(1, number+1) == number  Random.Range(1, number+1) == number) return number;
``````

Man, that’s so elegant I wanna wear it :~)
I’ll have to tweak it until I get the results I want, but this looks very promising already. Thanks!

Some languages I’ve used (like Python) actually have distribution changes built-in to the random module. But as far as I can tell, this doesn’t seem to be the case with Random or System.Random.

You can get a slightly more tweakable version of this using the Mathf.Pow function:-

``````function SkewedRandomRange(start: int, end: int, p: float) {
var rnd = Mathf.Pow(Random.value, p);
var index = Mathf.FloorToInt(start + rnd * (end - start + 1);
return Mathf.Clamp(index, start, end);
}
``````

If you supply a value of 1 as the p parameter, you get uniform distribution. If you use 2, you get the quadratic behaviour you mentioned. 0.5 gives you square root (ie, higher numbers are favoured). You can use any fractional value for this, so you can vary the behaviour smoothly.

1 Like

Thanks Andeeee!

Not only is it more tweakable than mine, it avoids the performance hit that while loops can introduce.

``````function SkewedRandomRange(start: float, end: float, p: float) : float
{
return Mathf.Pow(Random.value,p)*(end-start)+start;
}
``````

Essentially the same thing as what Andeeee gave you, but in one line.

Aw shoot, Andeeee that’s what I was going to suggest! Elegant. Very nice. (I did that for an adventure game where a player of 1 level going up against an enemy of a different level has a higher or lower probability of scoring a hit.)

Wow, thanks guys!

``````    int Random(int min, int max)
{
for (int i = min; i <= max; i++)
{
if (Random.value >= 0.5f) return i;
}
return max;
}
``````