Get pseudo-random item with given probability

后端 未结 3 1075
终归单人心
终归单人心 2021-01-23 12:44

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 w

3条回答
  •  陌清茗
    陌清茗 (楼主)
    2021-01-23 13:17

    'Right off the top of my head'-approach would be to prepare an array where each source item occurs the number of times that corresponds to respective probability and pick random item out of that array (assuming probability value has no more than 2 decimal places):

    // main function
    const getPseudoRandom = items => {
      const {min, random} = Math,
            commonMultiplier = 100,
            itemBox = []
      for(item in items){
        for(let i = 0; i < items[item]*commonMultiplier; i++){
          const randomPosition = 0|random()*itemBox.length  
          itemBox.splice(randomPosition, 0, item)
        }
      }        
      return itemBox[0|random()*itemBox.length]
    }
    
    // test of random outcomes distribution
    const outcomes = Array(1000)
            .fill()
            .map(_ => getPseudoRandom({'flower': 0.5, 'book': 0.3, 'mobile': 0.2})),
          
          distribution = outcomes.reduce((acc, item, _, s) => 
            (acc[item] = (acc[item]||0)+100/s.length, acc), {})
    
    console.log(distribution)
    .as-console-wrapper{min-height:100%;}

    While above approach may seem easy to comprehend and deploy, you may consider another one - build up the sort of probability ranges of respective width and have your random value falling into one of those - the wider the range, the greater probability:

    const items = {'flower': 0.5, 'book': 0.2, 'mobile': 0.2, '1mUSD': 0.1},
    
          // main function
          getPseudoRandom = items => {
            let totalWeight = 0,
                ranges = [],
                rnd = Math.random()
            for(const itemName in items){      
              ranges.push({
                itemName,
                max: totalWeight += items[itemName]
              })
            }           
            return ranges
                    .find(({max}) => max > rnd*totalWeight)
                    .itemName
          },
          
          // test of random outcomes distribution
          outcomes = Array(1000)
            .fill()
            .map(_ => getPseudoRandom(items)),
    
          distribution = outcomes.reduce((acc, item, _, s) => 
            (acc[item] = (acc[item]||0)+100/s.length, acc), {})
    
    console.log(distribution)

提交回复
热议问题