Basta criar um array contendo a probabilidade de cada valor, e em seguida gere um número aleatório entre 1 e a soma das probabilidades, e percorra o array, subtraindo cada probabilidade deste número até que ele zere:
function sortear(probabilidades) {
var min = 1;
var max = 0; // o valor máximo é a soma das probabilidades
for (const p of probabilidades) {
max += p[0];
}
var x = Math.floor(Math.random() * (max - min + 1)) + min;
for (const [p, valor] of probabilidades) {
x -= p;
if (x <= 0) {
return valor;
}
}
}
const probabilidades = [
[50, 'algum valor'],
[15, 'outro valor'],
[35, 'e outro']
];
console.log(sortear(probabilidades));
O raciocínio é que a probabilidade de um item é maior que a soma das probabilidades dos itens anteriores, daí vc vai subtraindo e quando ficar menor ou igual a zero, sabe que chegou na probabilidade daquele item.
No caso precisei adicionar um item com probabilidade 35%, para completar os 100%. E as probabilidades não podem estar em um objeto, pois podem ter dois valores com a mesma probabilidade. Por exemplo, se dois itens tivessem 30%, não poderia fazer probabilidades = { 30: 'um valor', 30: 'outro valor' }
, pois um objeto não permite chaves duplicadas. Por isso fiz um array de arrays.
Mas note que o algoritmo é um pouco mais genérico, pois funciona com qualquer proporção (não apenas porcentagem). Por exemplo, se tiver dois itens e um tem 5 vezes mais chances de sair que o outro, bastaria trocar para:
const probabilidades = [
[5, 'tenho 5 vezes mais chances de sair que o outro'],
[1, 'outro']
];
Se fosse fazer com porcentagem, o primeiro item teria que ser 83.3333333333...
e o segundo, 16.6666666666...
. São dízimas periódicas, então o total não seria exatamente 100. Porém, usando 5
e 1
, funciona da forma esperada, pois o algoritmo sempre considera a soma das "probabilidades", que na verdade funcionam mais como "pesos". A porcentagem é simplesmente um caso especial em que a soma dos pesos é 100.
Algoritmo retirado daqui.