Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get pseudo-random item with given probability

I want to give the user a prize when he signs in; but it needs to be there some rare prizes so I want to appear prizes with different chances to appear using percents

i want to display one of these [50 : 'flower'], [30 : 'book'], [20 : 'mobile']; using percents they have

if there any way using Node.js or just javascript functions it would be great

like image 686
rashid hacker Avatar asked Oct 24 '25 12:10

rashid hacker


2 Answers

You can create a function to get weighted random results, something like this:

const weightedSample = (items) => {
    // cache if necessary; in Chrome, seems to make little difference
    const total = Object.values(items).reduce((sum, weight) => sum + weight, 0)

    const rnd = Math.random() * total
    let accumulator = 0

    for (const [item, weight] of Object.entries(items)) {
        accumulator += weight

        if (rnd < accumulator) {
            return item
        }
    }
}

// check frequencies of each result

const prizes = { flower: 50, book: 30, mobile: 20 }
const results = Object.fromEntries(Object.keys(prizes).map(k => [k, 0]))

for (let i = 0; i < 1e6; ++i) {
    const prize = weightedSample(prizes)
    
    ++results[prize]
}

// sample results: { flower: 500287, book: 299478, mobile: 200235 }
console.log(results)

This will work regardless of whether the weights add up to 100, whether they're integers, and so on.

like image 97
Lionel Rowe Avatar answered Oct 26 '25 00:10

Lionel Rowe


"Certain probability" and "random" could lead to different approaches!

If you want random each time, something like:

let chances = [[0.2,'mobile'],[0.5,'book'],[1.0,'flower']]
let val = Math.random() // floating number from 0 to 1.0
let result = chances.find( c => c[0] <= val )[1] 

This will give a random result each time. It could be possible to get 'mobile' 100 times in a row! Rare, of course, but a good random number generate will let that happen.

But perhaps you want to ensure that, in 100 results, you only hand out 20 mobiles, 30 books, and 50 flowers. Then you might want a "random array" for each user. Pre-fill the all the slots and remove them as they are used. Something like:

// when setting up a new user
let userArray = []
let chances = [[20,'mobile'],[30,'book'],[50,'flower']]
changes.forEach( c => {
  for(let i = 0; i < c[0]; i++) userArray.push(c[1])
})
// save userArray, which has exactly 100 values
// then, when picking a random value for a user, find an index in the current length
let index = Math.floor(Math.random() * userArray.length)
let result = userArray[index]
userArray.splice(index,1) // modify and save userArray for next login
if(userArray.length === 0) reinitializeUserArray()

There are different approaches to this, but just some ideas to get you started.

like image 26
clay Avatar answered Oct 26 '25 01:10

clay



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!