public static double[] GenerateRandomNumbers(uint values, double minimum, double maximum, double sum, Random generator = null)
{
if (values == 0)
throw new InvalidOperationException($"Cannot create list of zero numbers.");
if (minimum * values > sum)
throw new InvalidOperationException($"The minimum value ({minimum}) is too high.");
if (maximum * values < sum)
throw new InvalidOperationException($"The maximum value ({maximum}) is too low.");
if (minimum > maximum)
throw new InvalidOperationException($"The maximum value ({maximum}) is lower than the minimum value ({minimum}).");
if (generator == null)
generator = new Random();
var numberList = new double[values];
for (var index = 0; index < values - 1; index++)
{
var rest = numberList.Length - (index + 1);
var restMinimum = minimum * rest;
var restMaximum = maximum * rest;
minimum = Math.Max(minimum, sum - restMaximum);
maximum = Math.Min(maximum, sum - restMinimum);
numberList[index]var newRandomValue = generator.NextDouble(minimum, maximum);
sumnumberList[index] = newRandomValue;
sum -= numberList[index];newRandomValue;
}
numberList[values - 1] = sum;
return numberList;
}
var min = -1.0;
var max = 1.0;
var sum = 10.0;
uint values = 123456;100000;
var seed = 123;
var generator = new Random(seed);
var randomListrandomNumbers = Extensions.GenerateRandomNumbers(values, min, max, sum, generator);
Debug.WriteLine($"Distinct Values: {randomListrandomNumbers.Distinct().Count()}");
Debug.WriteLine($"Min: {randomListrandomNumbers.Min().ToString("F99").TrimEnd('0')}");
Debug.WriteLine($"Max: {randomListrandomNumbers.Max().ToString("F99").TrimEnd('0')}");
Debug.WriteLine($"Average: {randomListrandomNumbers.Average().ToString("F99").TrimEnd('0')}");
Debug.WriteLine($"Median: {randomListrandomNumbers.Median()}");
Debug.ToStringWriteLine("F99")$"Sum: {randomNumbers.TrimEndSum('0')}");
Debug.WriteLine($"Sum:"\nFirst {randomList10 values:");
randomNumbers.SumTake(10).ToStringToList("F99").TrimEndForEach('0'v => Debug.WriteLine(v)}");
Distinct Values: 12320199800
Min: -0,999982522800557999962684698385
Max: 1,
Average: 0,00000810005184033385
Median value: 0,0010687396866588800128587102577371
Sum: 10
First 10 values:
0,00000000000026969113830462617
0,815630646336652
0,487091036274606
0,623283306892628
0,477558290342595
-0,903369966849391
-0,965998261219821
-0,701281160908416
-0,610592191857562
0,26017893536956
One problem I faced with Winkels' code was that it was a recursive method and for large lists (> 30 000 numbers) the program would throw StackOverflow exceptions. This is why I wrote it as a iterative function. I also tried to clean up and removed unnecessary operations. I also added support for seeding the random number generator, and some error handling.
There is still room for improvement. I haven't looked too much into how truly random this is, so investigate further if you need to do anything scientific.