2

I am attempting to build a function that will generate random numbers that sum to the provided value. However I also need those values to be greater than the provided value.

My current attempt: The process: (Example of N = 10, M = 3) Generate a list of random numbers (N-1) in length. Add 0 and M (3) to that list. Sort the List. Take the differences of the Adjacent Numbers.

This provides a nice random breakdown of the Sum with Uniform amounts. However, now I need to limit those amounts to a Minimum Value. I am stumped.

Example:

                var r = new Random();

           var N = 10;
           var M = 3*100;

           var startingList = new List<int>();
           for (int i = 1; i <= N - 1; i++)
           {
               startingList.Add(r.Next(0, M));
           }
           startingList.Add(0);
           startingList.Add(M);
           startingList = startingList.OrderBy(o => o).ToList();

           startingList.ForEach(Console.WriteLine);


           var a = 0;
           var b = 1;

           var randomList = new List<double>();

           for (int i = 1; i <= startingList.Count-1; i++)
           {
               double difference = (Convert.ToDouble(startingList[b]) - Convert.ToDouble(startingList[a]))/100;
               randomList.Add(difference);
               a++;
               b++;
           }

           randomList.ForEach(f => Console.Write(f + ", "));
           Console.WriteLine("Sum = " + randomList.Sum());
           Console.ReadLine();

Output:

0
33
84
142
175
209
230
245
272
298
300
0.33, 0.51, 0.58, 0.33, 0.34, 0.21, 0.15, 0.27, 0.26, 0.02, Sum = 3

0
1
2
5
75
101
140
203
204
295
300
0.01, 0.01, 0.03, 0.7, 0.26, 0.39, 0.63, 0.01, 0.91, 0.05, Sum = 3

0
26
44
73
83
96
140
168
178
189
300
0.26, 0.18, 0.29, 0.1, 0.13, 0.44, 0.28, 0.1, 0.11, 1.11, Sum = 3
Adam Reed
  • 229
  • 4
  • 16

2 Answers2

2

Just select N random numbers and normalize them by multiplyting with M/theirsum

int N = 10;
int M = 3;
Random rnd = new Random();

var tempRandoms = Enumerable.Range(0, N).Select(_ => rnd.NextDouble()).ToArray();
var sum = tempRandoms.Sum();
var randoms = tempRandoms.Select(x => x* M/sum).ToArray();

var check = randoms.Sum(); //should be equal to M
L.B
  • 114,136
  • 19
  • 178
  • 224
  • This is a much easier way of doing this, indeed. However, I also need each amount to be Greater Than provided number, in my example I need Greater Than 5. – Adam Reed Oct 18 '16 at 20:49
  • @AdamReed I think you got the idea. Map some randoms number to other set by doing simple arithmetic operations like (`x => x* M/sum`)... Can you do it? – L.B Oct 18 '16 at 20:52
  • Combining @Nico Schertlers math with your method worked great, you guys rock! – Adam Reed Oct 18 '16 at 21:14
  • Now lets add one more part to it, how can I make my randoms only 2 decimal points, while keeping my sum accurate, I tried applying rounding to the NextDouble and Select, but the Sums started to skew ;( – Adam Reed Oct 18 '16 at 21:23
  • @AdamReed use `decimal` instead of double... http://stackoverflow.com/a/1165788/932418 – L.B Oct 18 '16 at 21:24
  • O.O there is no NextDecimal :(? – Adam Reed Oct 18 '16 at 21:25
  • @AdamReed Just cast the double to decimal – L.B Oct 18 '16 at 21:26
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/126050/discussion-between-adam-reed-and-l-b). – Adam Reed Oct 18 '16 at 21:27
1

If your minimum value is min and you generate N numbers, then you will end up with a sum of at least min * N. So the remaining space M - min * N is the space where you can sample randomly. Here is the general idea:

samples := N random samples in [0, 1)
sum := sum(samples)
for each s in samples
    emit min + s * (M - min * N) / sum
Nico Schertler
  • 32,049
  • 4
  • 39
  • 70