I have created a weighted random number generator class. I’m pretty happy with it, it appears to be functioning, except for one issue — I want the consumer to have some control over just how weighted the generation is. I’ve allowed them to supply a byte, the default is 128. If they want the random numbers to be less weighted, they use a lower value, if they want the random numbers to be more weighted they use a higher value. I acknowledge that this is fairly arbitrary, and I’m fine with that. If they want it less weighted the the minimum supplied by my script they can just use Random, and I don’t see a use case for it to be more weighted.
The way it works is that I add the weightness factor to the upper and lower possible values before creating a weighted random value using squares and square roots. I then subtract the weightednessfactor from the resultant value before returning. This takes advantage of the fact that for larger squares the distance between them is closer than for smaller squares. I also normalize it, so that the weightedness applies to all input values equally; ergo a range 1-5 would have the same weightedness as a range 11-15 or 31-35
I have the consuming user supply a byte, because that is the smallest data type available to me. I use the floored square root of the supplied value because ultimately with my solution, a weightedness factor of above 20 makes the weighting essentially meaningless, so I artificially restrict the weightedness to 15 possible values, but implicitly tell the consuming user they can supply 255 values.
I’d like to have a compiler or language forced restriction in play here, so that the user can only supply a restricted range of values, something like 1-10 or 1-20. How, if possible, can I do this? Alternatively, what changes could I make so that all possible 255 values of my byte would be meaningful?
Ultimately, I could just say “It’s a random number generator, so it’s OK if I fudge a bit”, but I’d rather act in best faith to the consumers of this.
public class WeightedRandom { public WeightedRandom(Bias bias = Bias.Low, byte weightedness = 128) { BiasDirection = bias; Weightedness = weightedness; } public enum Bias { Low, High } private readonly Random rand = new Random(); public Bias BiasDirection {get;set;} public byte Weightedness { get { return _weightedness; } set { _weightedness = value; _randomFactor = (byte)Math.Sqrt(255 - value); } } private byte _weightedness; private byte _randomFactor; public int Next(int lower, int upper) { //By subtracting the lower bound, we normalize the squared value //to be one based. var newLower = lower + _randomFactor - lower; var newUpper = upper + _randomFactor - lower; var SquaredLower = newLower*newLower; var SquaredUpper = (newUpper+1)*(newUpper+1); var SquaredRand = rand.Next(SquaredLower, SquaredUpper); var val = (int)Math.Sqrt(SquaredRand) - _randomFactor; //Renormalize the result back to it's original bounds if (BiasDirection == Bias.Low) { val = upper - val; } else if (BiasDirection == Bias.High) { val = lower + val; } return val; } }