How to pick an element randomly using a probability vector?

Hi,

If I have an array such as arr=[0.6,0.1,0.3]
How can I write pick and index randomly using these numbers in javaScript?
like pick 0, 60% of the times.
1, 10% of the times
2, 30% of the times

Thanks

1 Like

I assume that sum of elements = 1.
The idea is to make a new array called prArr in which elements occurs as much times as their probability ratio.

let nums = [0.6, 0.1, 0.3]
let lc = 0.1

let randomPick = arr => {
  let idx = 0, begin, count, prArr = []

  arr.map(val => {
    begin = idx
    count = val / lc
    for(; idx < count + begin; idx++)
      prArr[idx] = val
  })

  let k = Math.floor(Math.random() * prArr.length)
  return prArr[k]
}

for(let i = 0; i < 10; i++) 
  console.log(randomPick(nums))

Live :tv: example

Tip: You can adjust lc to 0.01 or 0.001 based upon number of decimals :wink:

3 Likes

The idea is good. But you already have the probabilities so why not calculate with them?

You just do a Math.random(); and use this to get the index which correlates to this by using the cumulative probabilities, where every element has a certain area on the number scale from 0.0 up to 1.0 (e.g. element 0 is from 0.0 to 0.6, 1 is from 0.6 to 0.7 and 2 is 0.7 to 1.0):

const rnd = Math.random();
const arr = [0.6, 0.1, 0.3]

// The index and the cummulative probability
let index = -1;
let cum = 0;

// We add the cummulative probabilities and search till we get to where rnd is
arr.forEach((e, i) => {
  if(rnd >= cum && rnd <  cum + e) {
    index = i;
  }
  cum += e;
});

Feel free to use also findIndex() method with the same idea behind it:

let cum = 0;
const rnd = Math.random();
const arr = [0.6, 0.1, 0.3];
const index = arr.findIndex(e => {  cum += e;  return rnd < cum && rnd >=  cum - e;})

(I am sorry for naming the one variable “cum”)

4 Likes

@JaielSoft, another one, based on subtracting elements from random value in each loop :wink:

let nums = [0.6, 0.1, 0.3]

let randomPick = arr => {
  let k = Math.random()
  // get the first value that makes "rnd int" negative on subtraction
  let res = arr.find(val => (k -= val) < 0)
  return res || arr.pop() // else pop the last one
}

for(let i = 0; i < 10; i++) 
  console.log(randomPick(nums))

For one-liners,

let randPick = (a, k=Math.random()) => a.find(v => (k -= v) < 0) || a.pop()

Live :tv: example

1 Like

This one line works perfectly in the live example. But I am having trouble implementing it in my code.

what I did is create a function

 randPick = (a, k=Math.random()) =>{
 a.find(v => (k -= v) < 0) || a.pop() 

}

Then in the main function that is running I used.

var randNum=this.randPick(percentVector[j]);
console.log(randNum);

For trying it I used

percentVector[j]=[0.5,0.5]

but the results is “undefined”

sorry if this is a trivial question but I am new at using JavaScript

you used {} in the lambda function so you need to add a return statement. The original code didnt have them and a return is implied.

edit: So if you dont get it the solution:

randPick = (a, k=Math.random()) =>{
 return a.find(v => (k -= v) < 0) || a.pop();
}

(Although I dont like the (k -= v) statement because it doesn’t adhere to general programming principals of making code that is easy to read and doesn’t hold any surprises. Also altering the state of a parameter, that should always be treated as constant, is not the best practice. But if it works out it’s fine. I like short code too after all)

1 Like

Thank you!
you are right the longer code is actually more readable and easier to understand. I preferred the one line because my code already contains multiple nested loops. I am trying to do things with no more loops for things to be faster.