【问题标题】:Get pseudo-random item with given probability获取给定概率的伪随机项
【发布时间】:2021-01-08 14:27:30
【问题描述】:

我想在用户登录时给他一个奖品; 但它需要有一些稀有的奖品,所以我想使用百分比出现有不同机会出现的奖品

我想显示其中之一 [50 : 'flower'], [30 : 'book'], [20 : 'mobile']; 使用他们的百分比

如果有任何方法使用 Node.js 或仅使用 javascript 函数,那就太好了

【问题讨论】:

  • 您需要获取有关代码外观的更多信息,顺便说一句,此列表的语法无效。而且您的问题过于笼统,您需要更具体
  • 奖品是随机选择的(有相应的概率)还是严格匹配还是最接近输入值?
  • 您应该将输入数据调整为一些有效的符号(我猜,对象最适合:{50: 'flower', 30: 'book', 20: 'mobile'}
  • 是的,奖品是随机的,但出现的机会不同

标签: javascript node.js arrays


【解决方案1】:

“一定概率”和“随机”可能导致不同的方法!

如果你每次都想随机,比如:

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] 

这每次都会给出一个随机结果。有可能连续获得 100 次“移动”!当然很少见,但生成一个好的随机数会让这种情况发生。

但也许您想确保在 100 个结果中,您只分发 20 部手机、30 本书和 50 朵鲜花。然后,您可能需要为每个用户创建一个“随机数组”。预先填充所有插槽并在使用时将其移除。比如:

// 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()

对此有不同的方法,但只有一些想法可以帮助您入门。

【讨论】:

  • 你是对的!我忘记了号码。这个想法是,当用户完成最后的机会项目时重置所有机会(未实现)。很好的收获。
【解决方案2】:

'就在我的脑海中'-方法是准备一个数组,其中每个源项出现的次数对应于各自的概率,并从该数组中选择随机项(假设概率值不超过 2 位小数):

// 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%;}

虽然上述方法似乎很容易理解和部署,但您可以考虑另一种方法 - 建立相应宽度的概率范围,并让您的随机值落入其中之一 - 范围越宽,概率越大:

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)

【讨论】:

  • 它给了我一个随机的,但机会不起作用;我做了const probabilities = {1000: '0', 0.1: '3'},,它仍然给了我很多 3
  • @rashidhacker : 只是漏掉了一个小细节,got it working now as expected
  • 即使这个代码真的很难;但是谢谢你,它的工作!!!!
  • @rashidhacker : 有什么让你困惑的都可以问,我很乐意逐段解释上面的代码
  • 我想知道如何添加 0.1 或 0.01 和相同的值,该函数不适用于这些数字,这就是我需要知道的全部
【解决方案3】:

您可以创建一个函数来获得加权随机结果,如下所示:

const prizes = [[50, 'flower'], [30, 'book'], [20, 'mobile']]

const total = prizes.reduce((sum, [weight]) => sum + weight, 0)

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

    for (const [weight, item] of prizes) {
        accumulator += weight

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

// check frequencies of each result

const results = {}

for (let i = 0; i < 100000; ++i) {
    const prize = getPrize()
    
    results[prize] = (results[prize] || 0) + 1
}

console.log(results)

无论权重加起来是否为 100、是否为整数等等,这都会起作用。

【讨论】:

    猜你喜欢
    • 2011-12-25
    • 2016-01-06
    • 1970-01-01
    • 2013-04-27
    • 2021-08-27
    • 1970-01-01
    • 2020-12-05
    • 2012-12-04
    相关资源
    最近更新 更多