首页 > 解决方案 > 以概率随机播放 JS 数组

问题描述

假设我有一个这样的数组:

const alphabet = ['a', 'b', 'c', 'd'];

这代表 4 个政治候选人和一个等级选择投票,其中候选人a是第一选择,b是第二选择,等等。

我想把它打乱成一堆随机顺序,但在这种情况下,我想a首先出现可能有 60%,b第二个出现概率为 20%,c第三个出现概率为 10%,所有其他排序可能有 10%。是否有一些 lodash 和 ramda 功能可以做到这一点?

这是为了测试等级选择投票算法。随机改组数组会产生具有几乎相同投票数的候选人,这并不反映大多数现实(尽管我也会对此进行测试)。

我有一个非常可怕的例程,它将生成一个随机数组:

const getValues = function () {

  const results = [];
  const remaining = new Set(alphabet);
  const probabilities = [0.6, 0.2, 0.1, 0.1];

  for(let i = 0; i < alphabet.length; i++){

    const r  = Math.random();
    const letter = alphabet[i];

    if(r < probabilities[i] && remaining.has(letter)){
      results.push(letter);
      remaining.delete(letter);
    }
    else{
      const rand = Math.floor(Math.random()*remaining.size);
      const x = Array.from(remaining)[rand];
      remaining.delete(x);
      results.push(x);
    }

  }

   return results;
};

这“有效”,但由于条件概率,它并没有根据指定的概率对事物进行排序。如上所述,有人知道以一定概率出现订单的好方法吗?

这是我正在寻找的一些示例输出:

[ [ 'd', 'b', 'a', 'c' ],
  [ 'a', 'b', 'c', 'd' ],
  [ 'a', 'd', 'b', 'c' ],
  [ 'd', 'b', 'a', 'c' ],
  [ 'b', 'c', 'a', 'd' ],
  [ 'a', 'b', 'c', 'd' ],
  [ 'd', 'b', 'c', 'a' ],
  [ 'c', 'd', 'a', 'b' ],
  [ 'd', 'b', 'a', 'c' ],
  [ 'a', 'b', 'c', 'd' ] ]

如果您生成了足够的数据,它将不符合所需的顺序/分布。

标签: javascriptalgorithmdata-sciencevotingvoting-system

解决方案


您可以随机抽取数组的一部分并对剩余的可能性进行归一化,然后再抽取一个,直到所有项目都被取走。

结果,您几乎得到了想要的结果,正如您在counts项目及其最终索引中看到的那样。

const
    getIndex = (prob) => prob.findIndex((r => p => r < p || (r -= p, false))(Math.random())),
    normalized = array => {
        var sum = array.reduce((a, b) => a + b, 0);
        return array.map(v => v / sum);
    };

var items = ['a', 'b', 'c', 'd'],
    probabilities = [0.6, 0.2, 0.1, 0.1],
    counts = { a: { 0: 0, 1: 0, 2: 0, 3: 0 }, b: { 0: 0, 1: 0, 2: 0, 3: 0 }, c: { 0: 0, 1: 0, 2: 0, 3: 0 }, d: { 0: 0, 1: 0, 2: 0, 3: 0 } },
    l = 100,
    index,
    result = [], 
    subP,
    subI,
    temp;

while (l--) {
    temp = [];
    subP = probabilities.slice();
    subI = items.slice();
    while (subP.length) {
        sum = subP.reduce
        index = getIndex(normalized(subP));
        temp.push(subI[index]);
        subI.splice(index, 1);
        subP.splice(index, 1);
    }
    result.push(temp);
}

console.log(result.map(a => a.join()));

result.forEach(a => a.forEach((v, i) => counts[v][i]++));

console.log(counts);
.as-console-wrapper { max-height: 100% !important; top: 0; }


推荐阅读