Home > Back-end >  Assign probabilities to shuffle in Javascript
Assign probabilities to shuffle in Javascript

Time:10-05

I have the following array:

var myArray =  [BF1,BA1,BF2, BA2, BF3,BA3, BA4, BF4 , BA5, BF5, BF6, BA6, BF7, BA7, BA8, BF8, BA9, BF9, 
                BF10, BA10, BA11, BF11, BA12, BF12, BA13, BF13, BA14, BF14, BA15, BF15, BA16, BF16, BF17, BA18, 
                BF18, BA19, BF19, BA20, BF20, BA21, BF21, BF22, BA22, BA23, BF23, BA24, BF24, BA25, BF25, BA26, 
                BA27, BF27, BA28, BF28, WA29, WA30, WF30, WA31, WA31, WF32, WA32, WF33, WA33, WF34, WA34, 
                WF35, WA35, WA36, WA37, WA38, WF38, WA39, WF40, WA40, WA41, WF41, WA42, WF42, WF43, WA43, 
                WF44, WA44, WF45, WA45, WF46, WF46, WA47, WF47, WA48, WF48, WF49, WA49, WA50, WF50, WF51, 
                WA51];

I use the following function to shuffle, such that shuffle(myArray);

function shuffle(array){
    var counter = array.length,
        temp, index;
    while (counter > 0){
        index = Math.floor(Math.random() * counter);
        counter = counter-1;
        temp = array[counter];
        array[counter] = array[index];
        array[index] = temp;
    }
    return array;

However, I'd like to shuffle with probabilities. Such that items in myArray that begin with B (BF1,BA1,BF2...BF28) have a 0.8 probability of being the first items in the array, and items that begin with W (WA29,WA30,WF30...WA51) have a 0.2 probability of being the first items in the array.

Can I do this using math.random? Or should I use something else?

Thanks in advance!

CodePudding user response:

I'm going to assume that what you mean is that when you pick the first item, the 'B's are weighted so that they are 4 times as likely to be picked first. For example if we have two items B1 and W1, there is an 80% chance you end up with ['B1', 'W1'] and a 20% chance you end up with ['W1', 'B1']. And if you have four items B1, W1, W2, W3 that there would be an 80/140 chance of having B1 first (weighted) and a 20/140 chance of having each of W1, W2, or W3 first. If B1 is first each remaining W item has an equal chance of being the next. If a W is first then the B1 has an 80/120 chance of being 2nd. And so on...

One way to do that would be to weigh each item in the remaining list and calculate a random number according to the total probability and step through the list until you reach that number. For example you would have B1 = 0.8, B2 = 0.8, W1 = 0.2, W2 = 0.2. So you calculate a random number >0 = and < 2.0. Say it's 1.7. The running totals would be B1 = 0.8, B2 = 1.6, W1 = 1.8, W2 = 2.0. Since it's greater than B2 and less than W1, you pick W1.

For a simple list with two characters, it would be faster to split the array into W and B, then compose an array for each, then calculate one random number (weighted) for which array to pick from, then another random number to pick which item from that array.

var myArray =  ['BF1','BA1','BF2','BA2','BF3','BA3','BA4',
'BF4','BA5','BF5','BF6','BA6','BF7','BA7',
'BA8','BF8','BA9','BF9','BF10','BA10',
'BA11','BF11','BA12','BF12','BA13','BF13',
'BA14','BF14','BA15','BF15','BA16','BF16',
'BF17','BA18','BF18','BA19','BF19','BA20',
'BF20','BA21','BF21','BF22','BA22','BA23',
'BF23','BA24','BF24','BA25','BF25','BA26',
'BA27','BF27','BA28','BF28','WA29','WA30',
'WF30','WA31','WA31','WF32','WA32','WF33',
'WA33','WF34','WA34','WF35','WA35','WA36',
'WA37','WA38','WF38','WA39','WF40','WA40',
'WA41','WF41','WA42','WF42','WF43','WA43',
'WF44','WA44','WF45','WA45','WF46','WF46',
'WA47','WF47','WA48','WF48','WF49','WA49',
'WA50','WF50','WF51','WA51'];
                
const shuffle = arr => {
  let bs = [], ws = [];
  arr.forEach(ele => {
    if (ele[0] === 'B') {
      bs.push(ele);
    } else {
      ws.push(ele);
    }
  });
  
  let result = [];
  while (bs.length > 0 || ws.length > 0) {
    let bProbability = bs.length * 0.8;
    let wProbability = ws.length * 0.2;
    let totalProbability = bProbability   wProbability;
    let whichRandom = Math.random() * totalProbability;
    if (whichRandom < bProbability) {
      let index = whichRandom * 
          bs.length / bProbability;
      result.push(bs.splice(index, 1));
    } else {
      let index = (whichRandom - bProbability) *
          ws.length / wProbability;
      result.push(ws.splice(index, 1));
    }
  }
  return result;
}

let sorted = shuffle(myArray);
console.log(sorted);

CodePudding user response:

You can use this function which make a (pseudo)random number and return true or false if it´s smaller than probability (between 0 and 1, interval: [0,1) ) . Depends of result, you can or dont doing some action:

const isInProbability = (probability) => {
  const rand = Math.random();
  return rand < probability;
 };

Remember that Math.random() make a pseudorandom number. If you need more security, use crypto.getRandomValues() (https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Global_Objects/Math/random)

  • Related