所以我有一個加權項目列表,我想從這個列表中選擇4個非重復項目.
Item Weight
Apple 5
Banana 7
Cherry 12
...
Orange 8
Pineapple 50
最有效的方法是什么?我最初的嘗試是,如果一個已經被選中的項目出現的話,只需重新選擇隨后的選秀權……但是對于一個小名單,這可能會導致大量的重新加入.
編輯以澄清:
對于上面的例子,忽略水果D到N,總重量為82.所以先被挑選的機會是:
約6%
B~8.5%
C~14.6%
O~9.8%
P~61%
一旦選擇了一個項目,概率就會(應該!)改變.
解決方法:
在你的評論中,你說這是獨特的意思:
I don’t want to pick the same item twice.
..并且權重決定了被挑選的可能性.
您需要做的就是確保不挑選重復項,只需從列表中刪除最后一個選中的項目,然后再選擇下一個項目.是的,這會稍微改變您的權重,但如果您確實需要獨特的結果,那么這是正確的統計變化.
另外,我不確定你是如何使用權重來確定候選者的,但我想出了這個算法,它應該用最少的循環來完成這個(并且不需要根據權重填充數組,可能導致非常大的數組,需要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] + "
");
}
基本策略是為每個水果分配總范圍[0,1]的一部分.在第一個循環中,你有這個:
> Apple – 8/20 = 0.0最高0.4
>橙色 – 4/20 = 0.4至0.6
>香蕉 – 4/20 = 0.6至0.8
>油桃 – 3/20 = 0.8至0.95
> Kiwi – 8/20 = 0.95至1.0
該腳本遍歷列表中的每個項目,并進行權重計數器.當它到達包含第一個隨機的范圍時,它會選擇該項目,將其從列表中刪除,然后根據新的總重量重新計算范圍并再次運行.
標簽:php,algorithm,random,list,weighted
來源: https://codeday.me/bug/20190610/1210472.html