简体   繁体   中英

How can I generate N random values that sum to predetermined value?

I need your help with a little problem. I have four labels and I want to display on them random value between 0 to 100, and the sum of them must be 100.

This is my code:

private void randomly_Click(object sender, EventArgs e)
{
    double alpha = 0, beta = 0, gamma = 0, delta = 0;
    double temp;
    int tempDouble;

    Random rnd = new Random();

    alpha = rnd.Next(0, 100);

    temp = 100 - alpha;
    tempDouble = (int)temp;
    beta = rnd.Next(0, tempDouble);

    temp = 100 - (alpha + beta);
    tempDouble = (int)temp;
    gamma = rnd.Next(0, tempDouble);

    temp = 100 - (alpha + beta + gamma);
    tempDouble = (int)temp;
    delta = rnd.Next(0, tempDouble);

    temp = alpha + beta + delta + gamma;
    temp = 100 - temp;
    temp = temp / 4;

    alpha = alpha + temp;
    beta = beta + temp;
    gamma = gamma + temp;
    delta = delta + temp;

    cInsertion.Text = alpha.ToString();
    cMoyens.Text = beta.ToString();
    cInternational.Text = gamma.ToString();
    cRecherche.Text = delta.ToString();
}   

The problem is that I'm giving to the alpha the chance to have the biggest value, and for delta the lowest value.

Is there any way to give them all the same chance to have a real random value?

You could do something like this:

double alpha = 0, beta = 0, gamma = 0, delta = 0, k = 0;
Random rnd = new Random();

alpha = rnd.Next(0, 100);
beta = rnd.Next(0, 100);
gamma = rnd.Next(0, 100);
delta = rnd.Next(0, 100);

k = (alpha + beta + gamma + delta) / 100;

alpha /= k;
beta /= k;
gamma /= k;
delta /= k;

cInsertion.Text = alpha.ToString();
cMoyens.Text = beta.ToString();
cInternational.Text = gamma.ToString();
cRecherche.Text = delta.ToString();

This way you're saying let's take a random value for all 4 variables, and then we'll scale them by a factor k that'll make their sum be 100.

What if you stick all four values in an array, then shuffle them , then assign them according to their ordinal position? That way each variable (gamma, theta, etc.) has equal probability of getting a high vs low number.

It's a very interesting problem. I like @Ivan_Ferić's solution, I think, it's perfect, but I have another idea:

int total = 100;
Random rand = new Random();

int half = rand.next(0,total);   // the line

a = rand.Next(0,half);
b = max - a;
c = rand.Next(0,half);
d = max - c;

(Not tested, maybe +/-1 must be added to some of vars.) Also, you may shuffle values.

The interesting part is coming now. If we change "the line":

min = <some_value>
max = <another_value>;
int half = rand.next(min,max);

...then we can finetune the result:

  • min = 0; max = 100: original;
  • min = 20; max = 80: avoid little numbers;
  • min = 33; max = 100: force two numbers to be less than 32.

Instead of generating the individual numbers randomly, generate the partial sums, then calculate the parts:

double alpha, beta, gama, delta = 0;

var rnd = new System.Random();
var cuts = new List<double>();

cuts.Add(Math.Floor(rnd.Next(0, 101)));
cuts.Add(Math.Floor(rnd.Next(0, 101)));
cuts.Add(Math.Floor(rnd.Next(0, 101)));

cuts.Sort();

alpha = cuts[0];
beta = cuts[1] - cuts[0];
gamma = cuts[2] - cuts[1];
delta = 100 - cuts[2];

If the numbers that you want represent probabilities of mutually exclusive outcomes, you can imagine them as a multinomial distribution. Since you want a random probability vector, you should sample that from the conjugate prior of the multinomial distribution: the Dirichlet distribution.

In code, you just what Ivan suggested, except that alpha, beta, gamma, and delta should all be drawn from Gamma distributions with shape paramaters alpha_shape, beta_shape, gamma_shape, and delta_shape respective. (Let the other parameter of the Gamma distribution be 1.) These shape parameters control the expected relative proportions.

A real random number? In short, no. Only a pseudo-random number.

The random number generator operates off clock cycles and it is typical to see repeated sequences. The seed used depends on the.Tick property of the current DateTime.Now ( if I remember correctly ). There are a lot of decent random number generators and APIs for.Net.I would recommend searching for those if you feel like you don't have the time or desire to put into rolling your own.

Moved up to comments as it seems more appropriate.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM