以概率随机播放JS数组

时间:2019-04-04 06:14:12

标签: javascript algorithm data-science voting voting-system

说我有一个像这样的数组:

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

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

我希望将其随机排列成一堆随机顺序,但在这种情况下,我希望a以60%的价格首先出现,b其次以20%的概率出现,而{{1} }第三位的概率为10%,所有其他顺序的可能为10%。有没有可以实现此目的的lodash和ramda功能?

这用于测试等级选择投票算法。随机地对数组进行改组会产生候选人,他们的投票数几乎完全相同,这与大多数现实情况不符(尽管我也会对此进行测试)。

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

c

这个“有效”,但是由于条件概率而不能按照指定的概率对事物进行排序。如上所述,有人知道让订单以一定概率出现的好方法吗?

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

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

如果生成的数据足够多,将不符合所需的订单/分布。

4 个答案:

答案 0 :(得分:1)

这可能希望对您有所帮助,例如根据您的情况https://github.com/substack/node-deck

  

示例

 const normalize = function (weights) {
	if (typeof weights !== 'object' || Array.isArray(weights)) {
		throw 'Not an object'
	}

	let keys = Object.keys(weights);
	if (keys.length === 0) return undefined;

	let total = keys.reduce(function (sum, key) {
		let x = weights[key];
		if (x < 0) {
			throw new Error('Negative weight encountered at key ' + key);
		}
		else if (typeof x !== 'number') {
			throw new TypeError('Number expected, got ' + typeof x);
		}
		else {
			return sum + x;
		}
	}, 0);

	return total === 1
		? weights
		: keys.reduce(function (acc, key) {
			acc[key] = weights[key] / total;
			return acc;
		}, {})
		;
};

const pick = function (xs) {
	if (Array.isArray(xs)) {
		return xs[Math.floor(Math.random() * xs.length)];
	}
	else if (typeof xs === 'object') {
		// Weighted Sample
		let weights = normalize(xs);
		if (!weights) return undefined;

		var n = Math.random();
		var threshold = 0;
		var keys = Object.keys(weights);

		for (let i = 0; i < keys.length; i++) {
			threshold += weights[keys[i]];
			if (n < threshold) return keys[i];
		}
		throw new Error('Exceeded threshold. Something is very wrong.');
	}
	else {
		throw new TypeError('Must be an Array or an object');
	}
};

const shuffle = function (xs) {
	if (Array.isArray(xs)) {
		let res = xs.slice();
		for (var i = res.length - 1; i >= 0; i--) {
			var n = Math.floor(Math.random() * i);
			var t = res[i];
			res[i] = res[n];
			res[n] = t;
		}
		return res;
	}
	else if (typeof xs === 'object') {
		// Weighted
		let weights = Object.keys(xs).reduce(function (acc, key) {
			acc[key] = xs[key];
			return acc;
		}, {});

		let ret = [];

		while (Object.keys(weights).length > 0) {
			let key = pick(weights);
			delete weights[key];
			ret.push(key);
		}

		return ret;
	}
	else {
		throw new TypeError('Must be an Array or an object');
	}
};


let results = [];
for (let i = 0; i < 100; i++) {
	let weighted = shuffle({
		a : 60, 
		b : 20, 
		c : 10, 
		d : 10, // or .1, 100, 1000
	});
	results.push(weighted);
}
console.log(results);

答案 1 :(得分:1)

您可以随机选择数组的一部分,并对剩余的可能性进行标准化,然后再选择另一项,直到所有项都取完为止。

结果,您得到了几乎想要的结果,如您在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; }

答案 2 :(得分:1)

您可以使用如下所示的随机播放功能对它们进行排序:

const candidates = [
  { name: "a", weight: 6 },
  { name: "b", weight: 2 },
  { name: "c", weight: 1 },
  { name: "d", weight: 1 }
];

const randomShuffleFn = () => Math.random() - .5;

const shuffleFn = (candidateA, candidateB) =>
  Math.random() * (candidateB.weight + candidateA.weight) - candidateA.weight;

console.log([...candidates].sort(randomShuffleFn).sort(shuffleFn));

好的,这并不完全相同,但是我认为通过调整权重,您可以获得所需的分布(实际上,A赢得60%的次数)。

答案 3 :(得分:1)

我认为这个问题陈述得不好。

按书面规定,A放在第1位,概率为60%,B放在第2位,占20%,C和D放在第3或4位,各为10%。没有满足这些概率标准的分布,因此没有算法可以产生该分布:如果在60%的情况下,A位于第1位,则C或D必须位于这些位置的第3位或第4位60%,因此远远超出了要求的10%概率

因此,这里的第一个任务是使问题中的内容有意义(因为经过解释后当然可以理解)。

我想A的60%和B的20%不应被理解为概率,而应被理解为受欢迎度。但这不能只是每个候选人的法定人数,因为在投票过程中,A将在100%的情况下排在第1位。

因此,我们假设投票过程涉及一些随机性,这使A在第1位以60%概率结束,B在第1位(!)以20%概率等。然后我们可以使用位置1的加权随机选择来实现此目标。

如何继续放置2..n位?我们只是保持权重不变,然后删除已经选择的候选对象。如果其他候选人之一进入第1名,那么我认为很有可能在第2名上以A结束。