说我有一个像这样的数组:
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;
};
如果生成的数据足够多,将不符合所需的订单/分布。
答案 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结束。