The algorithm proposed in my other answer is functional but is not fair (ie does not distribute the value evenly between the lines). If there are many rows, the latter tend to be all with 0
. If there are few, the latter tend to get very large values. I will propose a workaround, limited to the case where the number to be distributed is integer and not too large , based on genetic algorithms :
- Again, you have
200
(which I'll call alvo
) to be distributed between N lines. For simplicity, this time let's assume that the lines have no minimums, only maximums (if they do, refer to the first part of my other answer for a means of distributing the minimums).
- Let's do a loop where at each iteration one of the lines will be chosen and receive
1
more. That is, the loop will execute 200
iterations.
- First a "roulette" is created in which the area of each row in the roulette wheel is proportional to its maximum value.
- Then one draws a line on the roulette wheel; it adds the value of
1
and reduces its maximum also in 1
. If it reaches zero, the roulette area for that line will become zero and it will no longer be drawn.
Example:
var alvo = 200;
var linhas = [{min: 0, max: 150}, {min:0, max: 130}];
var resultado = [0, 0];
function criarRoleta() {
var ret = [];
var soma = 0;
for ( var i = 0 ; i < linhas.length ; i++ ) {
var valor = linhas[i].max - resultado[i];
ret.push(valor+soma);
soma += valor;
}
return [ret,soma];
}
for ( var i = 0 ; i < alvo ; i++ ) {
var roleta = criarRoleta();
var sorteio = Math.floor(Math.random()*roleta[1]);
for ( var t = 0 ; t < linhas.length ; t++ )
if ( sorteio < roleta[0][t] ) {
resultado[t]++;
break;
}
}
Example in jsFiddle . Note that in this case the distribution tends to be uniform, and proportional to the maximum of each line. Extreme distributions are possible, but rare - the fact that rows already drawn have their areas reduced, so that the other rows increase their chance of being chosen in the subsequent rounds.
Update: This method is equivalent to opening an urn, placing N balls of different colors in it (1 for each space available on each line), and leaving removing balls until they reach the total. That is, the probability of each line being drawn changes during the draw, so it is more likely that all X% will be full than a full 100% and some will be empty.
If on the other hand what interests you is that the chance of the elements falling on a line is proportional to the initial size of each line, then it is necessary to "put the drawn balls back in the ballot box: this is done by creating the roulette once, instead of re-creating it at each iteration (ie move the call from criarRoleta()
out of the loop). In this case, you must check each draw if a line has reached its maximum and - if it has arrived - "remove all balls from that line of the ballot" (ie upgrade the roulette wheel so that the area of that row is zero, and the sum is adjusted from agreement).