-2

So I am trying to generate a sequence of N numbers where the sum will be S.

Right now I am doing this:

float baseamount = (float)(amount / norders) * 0.8f;
float secondamount = amount / norders;

then generate N random numbers between these 2 numbers.

However this generates a pretty uniform range of numbers whereas I want a bit more randomized, for example, instead of (sum = 200, n = 10):

16.92321 17.49378 16.26426 16.03404 16.12497 17.53131 18.10094 16.86982 17.0831 16.06921

I prefer:

12.345 17.4534 19.3542342 11.345345 18.4325235 14.4353245 ...

What's the best way to approach this?

Joan Venge
  • 315,713
  • 212
  • 479
  • 689
  • have you looked at this posting [Generate Random Numbers Based on Input](https://stackoverflow.com/questions/12182983/generating-a-random-number-related-to-input-parameters) – MethodMan Dec 10 '17 at 14:11
  • just looking at it now – Joan Venge Dec 10 '17 at 14:13
  • 1
    As these number are dependent, you can't solve this problem using some independent random variables. – OmG Dec 10 '17 at 14:16
  • 1
    Do you recognize it is impossible to do? With sum=200, and n=10 mean for each number would be 20. So sampling randomly will produce numbers some above 20 and some below, to keep sum to 200. With your requirements you want numbers to be clearly cupped by 20, so all of them would be less than 20, and NO WAY sum would be set to 200. – Severin Pappadeux Dec 10 '17 at 17:37
  • Even in your example sum is way off 200 – Severin Pappadeux Dec 11 '17 at 02:33

3 Answers3

1

This method creates n values that have a mean of sum/count varying by a defined value. The constant calculation of minand max in each cycle achieves that the last value (which in this case actually is the first of the array), does not stick out so much. Though, its still not perfect, especially when you have a small mean value and a large variance. But maybe this gives you a start.

public static IEnumerable<double> GetValues(int count, double sum, double variance)
    {
        var rnd = new Random();
        var values = new double[count];

        var remainingSum = sum;
        for (int i = count-1; i > 0; i--)
        {
            var min = (int)(remainingSum / (i+1) - variance / 2) * 1000;
            var max = (int)(remainingSum / (i+1) + variance / 2) * 1000;

            var rndVal = rnd.Next(min, max) / 1000.0;
            values[i] = rndVal;
            remainingSum -= rndVal; 
        }
        values[0] = remainingSum;

        return values;
    }
Lightbringer
  • 765
  • 1
  • 4
  • 16
  • 1
    Using multi instances of Random is not a good idea. If they are created at the same time, the results of Next() will be same. Test: `for (int i = 0; i < 9; i++) { Console.WriteLine(new Random().Next()); }` – skyoxZ Dec 11 '17 at 07:11
0

Generate (n-1) random numbers not greater than sum/n and keep track of the partial sum (let's call it PS). Then, your last number is (sum - PS).

Hope this helps.

EDIT: of course you'll have the last number exceptionally greater than all the previous ones. I'll investigate a more mathematical-oriented solution to this problem (or at least I'll try to). Meanwhile a "fix" has occurred to me:

(Let's call the random sequence a0,...,an).

  1. Generate the sequence as described above
  2. Generate a random number rd in the interval [0;k] (you need to choose k).
  3. an-=rd
  4. For each ai: ai+=rd/(n-1).

If I did everything well, the sum should not have changed and you have a more uniform sequence. k must be chosen by you depending on how "uniform" you want your sequence to be.

LuxGiammi
  • 631
  • 6
  • 20
0
static Random random = new Random();

float[] Generate(float sum, int n, float minLimit)
{
    float max = sum - minLimit * n;
    float[] arr = new float[n];
    for (int i = 0; i < n - 1; i++)
    {
        arr[i] = (float)random.NextDouble() * max;
    }
    arr[n - 1] = max;
    Array.Sort(arr);
    for (int i = n - 1; i > 0; i--)
    {
        arr[i] = arr[i] - arr[i - 1] + minLimit;
    }
    arr[0] += minLimit;
    return arr;
}
skyoxZ
  • 362
  • 1
  • 3
  • 10