Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to increase or decrease the probability of an item of an array being picked? [duplicate]

So, let's say I'm making something like a slots machine, to use the emojis I'd like to use I'd define them in an array.

var arr = ["emoji","emoji2","emoji3","emoji4","emoji5"]

Let's say I'd want emojis 1 - 4 to appear more than 5, and say decrease the probability of emoji5 being picked.

I could do something large like:

var arr = [
"emoji","emoji2","emoji3","emoji4",
"emoji","emoji2","emoji3","emoji4",
"emoji","emoji2","emoji3","emoji4",
"emoji","emoji2","emoji3","emoji4",
"emoji","emoji2","emoji3","emoji4",
"emoji","emoji2","emoji3","emoji4","emoji5",
]
var emoji = arr[Math.floor(Math.random() * arr.length)]

But that is not a very efficient idea, so is it possible to do the idea above without making a very large array?

What I'm aiming for basically is to have an array like

var arr = ["emoji","emoji2","emoji3","emoji4","emoji5"]

and it would output something where emojis 1 - 4 would appear way more often than emoji5, without a large array.

like image 794
SomePerson Avatar asked Mar 02 '23 12:03

SomePerson


2 Answers

For the general case of weighted probabilities, one option would be to have an object whose keys are of cumulative probabilities. Say you wanted emoji5 to occur 4% of the time - then, the cumulative probabilities would be 24, 48, 72, 96, 100 (where the last interval of 96 to 100 indicates emoji5's low weight) . Then generate a random number between 1-100 and find the first key which is greater than the picked number:

const probs = {
  24: "emoji",
  48: "emoji2",
  72: "emoji3",
  96: "emoji4",
  100: "emoji5"
};

const keys = Object.keys(probs).map(Number);
const generate = () => {
  const rand = Math.floor(Math.random() * 100);
  const key = keys.find(key => rand < key);
  return probs[key];
};
for (let i = 0; i < 10; i++) {
  console.log(generate());
}

Another option would be to associate a weight number with each string, and give the emoji5 a low one, add up the weights, generate a random number between 0 and the total weight, and find the first match:

const weights = [
  [4, 'emoji'],
  [4, 'emoji2'],
  [4, 'emoji3'],
  [4, 'emoji4'],
  [1, 'emoji5'],
];

const totalWeight = weights.reduce((a, [weight]) => a + weight, 0);
const weightObj = {};
let weightUsed = 0;
for (const item of weights) {
  weightUsed += item[0];
  weightObj[weightUsed] = item;
}
const keys = Object.keys(weightObj);
const generate = () => {
  const rand = Math.floor(Math.random() * totalWeight);
  const key = keys.find(key => rand < key);
  return weightObj[key][1];
};
for (let i = 0; i < 10; i++) {
  console.log(generate());
}
like image 177
CertainPerformance Avatar answered Apr 30 '23 01:04

CertainPerformance


Try in this way

var arr = ["emoji","emoji2","emoji3","emoji4","emoji5"]
var emoji = arr[Math.floor(Math.random() * (Math.random() < 0.75 ? arr.length - 1 : arr.length))]
like image 25
dellink Avatar answered Apr 30 '23 01:04

dellink