-1

I want to have a function which returns a random number between 2 given values. The problem, is that I want it to always "prefer" the lower values over the higher values, going up in a sort of "curve".

So, say I give it numbers 100 and 1000, it could give me any number between those 2 values... However it would give me between 100 and 200 far more than the 11.11% you would expect it to, instead it might give those values around 30-40% of the time, whilst the upper most values might only be given 2-4% of the time. Any ideas on how best to tackle this?

Language is C# but probably doesnt matter all that much.

Bhargav Rao
  • 50,140
  • 28
  • 121
  • 140
pingu2k4
  • 956
  • 2
  • 15
  • 31

5 Answers5

3

You could square your random number, this would weight the numbers to the bottom of your range:

public double GetWeightedRandom(int min, int max)
{
Random random = new Random();
double randomZeroToOne = random.NextDouble();
double weightedRandom = randomZeroToOne * randomZeroToOne * (max - min) + min;
return weightedRandom;
}

In my tests for 100 to 1000 it gives an average of 400 instead of 550. You can then cube the number if you want it to be more weighted to the bottom.

TomDoesCode
  • 3,580
  • 2
  • 18
  • 34
  • The `randomZeroToOne * randomZeroToOne` part can also be replaced by `Math.Pow(randomZeroToOne, x)` for finer control. Use a `x` greater than 1 to favors smaller values. *The larger `x` is, the more smaller values are favored.* – Xiaoy312 May 22 '19 at 19:20
0

What i would suggest then is that you use your own custom way of implementing your random number generator. For example write a wrapper class that will create a random number between 1- 100, if it falls between 1- 40 it gives back random number between 100 - 200, or so on, so then you essentially add your own set of prioritization to your number generation. If the value is between 41 - 100 it will give back a number between 201 - 1000. Which ever way your prefer

0

i would say to create a exponential curve using y=ab^x, which would create a curve how ever you want between the two input numbers, then get a random number between 0 and 1 to get a normalized location along the x axis of the curve and return the y value.

maraaaaaaaa
  • 7,749
  • 2
  • 22
  • 37
0

A weighted random is probably what you want, a good way to imagine in this is to think of a list with repeated elements where the weight is higher

var elements = new[]{1,1,2,2,3,3,10,11,12,13};
var rnd = elements[rnd.Next(0,elements.length-1)];

You can see from the above that there is twice as much chance that rnd equals 1,2 or 3 than 10,11,12, or 13.

You can generate the same list using a function which takes a flat list and the associated weights:

public List<int> GenerateWeighedList(int[] list, float[] weight) {
    var weighedList = new List<int>();

    // Loop over weights
    for (var i = 0; i < weight.Length; i++) {
        var multiples = weight[i] * 100;

        // Loop over the list of items
        for (var j = 0; j < multiples; j++) {
            weighedList.push(list[i]);
        }
    }

    return weighedList;
};

So the above list can be generated with

var input = new[]{1,2,3,10,11,12,13};
var weight = new[]{0.2,0.2,0.2,0.1,0.1,0.1,0.1};
var weightedList = GenerateWeighedList(input,weight);

weightedList can then be used to generate a weighted random number from the required set at the specified weightings

Jamiec
  • 133,658
  • 13
  • 134
  • 193
0

I'd do it in two steps. The first step - is to determine whether your number should be lowered or not. The second step is return a random number within the corresponding range.

public int RandomWeighted(int min, int max, int cap, int percent)
{
    Random rand = new Random();
    bool isInCap = rand.Next(0, 101) < percent;
    return isInCap? rand.Next(min, cap) : rand.Next(cap, max);
}

And use it like : int num = RandomWeighted(100, 1000, 200, 50); this will give you 100 - 200 in 50% times and 200 - 1000 in other 50%

Fabjan
  • 13,506
  • 4
  • 25
  • 52