在您的评论中,您说独特意味着:
我不想两次选择相同的项目。
.. 权重决定了被选中的可能性。
您需要做的就是确保您不选择重复项,只需在选择下一个之前从列表中删除最后一个选择的项目。是的,这会稍微改变你的权重,但如果你确实想要独特的结果,这是正确的统计改变。
此外,我不确定你是如何使用权重来确定候选者的,但我想出了这个算法,它应该用最少的循环来做到这一点(并且不需要根据权重填充数组,这可能导致非常大的数组,需要 int 权重等)
我在这里使用了 JavaScript,只是为了在没有服务器的情况下很容易在浏览器中查看输出。移植到 PHP 应该是微不足道的,因为它没有做任何复杂的事情。
常数
var FRUITS = [
{name : "Apple", weight: 8 },
{name : "Orange", weight: 4 },
{name : "Banana", weight: 4 },
{name : "Nectarine", weight: 3 },
{name : "Kiwi", weight: 1 }
];
var PICKS = 3;
function getNewFruitsAvailable(fruits, removeFruit) {
var newFruits = [];
for (var idx in fruits) {
if (fruits[idx].name != removeFruit) {
newFruits.push(fruits[idx]);
}
}
return newFruits;
}
脚本
var results = [];
var candidateFruits = FRUITS;
for (var i=0; i < PICKS; i++) {
// CALCULATE TOTAL WEIGHT OF AVAILABLE FRUITS
var totalweight = 0;
for (var idx in candidateFruits) {
totalweight += candidateFruits[idx].weight;
}
console.log("Total weight: " + totalweight);
var rand = Math.random();
console.log("Random: " + rand);
// ITERATE THROUGH FRUITS AND PICK THE ONE THAT MATCHES THE RANDOM
var weightinc = 0;
for (idx in candidateFruits) {
// INCREMENT THE WEIGHT BY THE NEXT FRUIT'S WEIGHT
var candidate = candidateFruits[idx];
weightinc += candidate.weight;
// IF rand IS BETWEEN LAST WEIGHT AND NEXT WEIGHT, PICK THIS FRUIT
if (rand < weightinc/totalweight) {
results.push(candidate.name);
console.log("Pick: " + candidate.name);
// GET NEXT SET OF FRUITS (REMOVING PICKED FRUIT)
candidateFruits = getNewFruitsAvailable(candidateFruits, candidate.name);
break;
}
}
console.log("CandidateFruits: " + candidateFruits.length);
};
输出
for (var i=0; i < results.length; i++) {
document.write(results[i] + "<br/>");
}
基本策略是为每个水果分配总范围的一部分[0,1)
。在第一个循环中,您将拥有以下内容:
- 苹果— 8/20 = 0.0 到 0.4
- 橙色— 4/20 = 0.4 到 0.6
- 香蕉— 4/20 = 0.6 到 0.8
- 油桃— 3/20 = 0.8 至 0.95
- 猕猴桃— 8/20 = 0.95 至 1.0
该脚本遍历列表中的每个项目,并推进一个重量计数器。当它到达包含第一个随机数的范围时,它会选择该项目,将其从列表中删除,然后根据新的总重量重新计算范围并再次运行。