I want to generate random delays between x and y. I want the ouputs to be around the middle of x and y more commonly than at x or y.
For example if my x is 10 and y is 20, I want the most common output to be 15.
I have been trying to solve this problem on paper but I am not a math genius, unfortunately.
I tried some formulas with Random.nextGuassian() but I simply cannot comprehend the odd output that it gives.
Before you can implement a random number generator with a parabolic distribution you need to define what you mean by this. Here is one attempt:
The distribution is defined on the interval 0 to 1 as a quadratic polynomial. On this interval the area below the curve has to be 1. By varying the parameters K and M you can adjust the distribution. K determines where the center of the parabola is located in the interval and M determines how far down to "pull" the parabola. In your question K is ½ as you want the apex to be in the middle of the interval. You did not specify M.
The parabola is specified by the function
f(x) = ax2 + bx + c
Provided K and M and that the area below the curve on the interval 0 to 1 should be 1 you need to compute a, b and c.
If K = ½ and M = ½ the exact solution is
f(x) = 6x2 - 6x + 2.
If this solution is not sufficient (because you want other values for K and/or M) then you will have to create a set of equations. Based on how the parabola is placed it is known that a has to be positive, b negative and c > M.
The apex of the parabola should touch (K, M). This means that f(K) = M or f(K) - M = 0.
For the apex to touch this point the discriminant of the modified quadratic equation has to be 0:
d' = b2 - 4a(c - M)
Provided that you know a and c you can calculate b (it is already known that b has to be negative to create the desired parabola):
b = -√(4a(c - M)) [equation A]
The equation for the apex is:
f(K) - M = aK2 + bK + c - M = 0 [equation B]
The area below the parabola is computed from the definite integral over the interval 0 to 1 of f(x)
∫01 f(x) dx = a/3 + b/2 + c = 1 [equation C]
You can substitute b from equation A into equation B and C giving two equations with two unknowns a and c. Unfortunately, these equations are non-linear and my math is getting rusty so I chose the "easy path" and used the Solver add-in in Excel to find approximate solutions for different values of K and M. If you take this approach you should probably add a constraint in the solver that d' cannot be negative as well as constraints on a and c as noted above.
Now that you know a, b and c for the desired parabolic equation the approach to create the random numbers with a parabolic distribution is as follows:
This solution picked is a random number with a parabolic distribution.
The inverse of f(x) is
x = (-b ± √(b2 - 4a(c - y)))/2a
Combining all this I have created a C# class. You will have to substitute the desired values for the parameters K, M, a, b and c. Or you can extend this code to compute a, b and c from K and M using numerical algorithms.
class ParabolicRandom
{
const double k = 0.5D;
const double m = 0.5D;
const double a = 6;
const double b = -6;
const double c = 2;
readonly double yMin = m;
readonly double yMax = Math.Max(F(0D), F(1D));
Random random;
public ParabolicRandom() => random = new Random();
public ParabolicRandom(int seed) => random = new Random(seed);
public double Next()
{
var randomY = (yMax - yMin) * random.NextDouble() + yMin;
var randomX1 = ReverseF1(randomY);
var randomX2 = ReverseF2(randomY);
if (randomX1 < 0D || randomX1 > 1D)
return randomX2;
if (randomX2 < 0D || randomX2 > 1D)
return randomX1;
return random.Next()%2 == 0 ? randomX1 : randomX2;
}
static double F(double x) => a * x * x + b * x + c;
double ReverseF1(double y) => (-b + Math.Sqrt(b * b - 4 * a * (c - y))) / (2 * a);
double ReverseF2(double y) => (-b - Math.Sqrt(b * b - 4 * a * (c - y))) / (2 * a);
}
To generate random number between 10 and 20 with half as many at 15 compared to 10 and 20 you can use it like this:
var lowerInclusive = 10;
var upperInclusive = 20;
var value = (int) Math.Floor(
(upperInclusive - lowerInclusive + 1)*parabolicRandom.Next() + lowerInclusive);