我有一个看起来像这样的数组:
[
{
plays: 0,
otherData: someValues
}, {
plays: 4,
otherData: someValues
}, {
plays: 1,
otherData: someValues
}, {
plays: 2,
otherData: someValues
} {
plays: 9,
otherData: someValues
}, {
plays: 7,
otherData: someValues
}, {
plays: 5,
otherData: someValues
}, {
plays: 0,
otherData: someValues
}, {
plays: 8,
otherData: someValues
}
]
这是关于播放列表中歌曲的一系列信息,其中plays
是歌曲播放的次数。我试图想出一个加权随机数生成器,它将选择一个元素的索引,加权使得较少播放的歌曲更有可能被选中。这是我现在的代码:
function pickRandom(){
var oldIndex = index;
if(songs.length <= 1)
return index = 0;
var unheard = [];
for(i in songs){
if(!songs[i].plays)
unheard.push(i);
}if(unheard.length > 0)
return index = unheard[Math.round(Math.random() * (unheard.length - 1))];
var tries = 0;
while(index == oldIndex && tries < 100){
index = Math.round(Math.random() * (songs.length - 1));
tries++;
}return index;
}
我对这个解决方案有很多不满意的地方。首先,它没有加权,因为它真的只是选择一首未播放的歌曲,或任何旧的随机曲目,如果阵列中的所有内容至少播放过一次。其次,它创建了一个新的阵列,由于播放列表有时会有数百首歌曲,如果可能的话,我想远离它。
我能够提出的最接近的解决方案是根据其plays
值将每个元素多次复制到一个新数组中,然后选择一个元素,但这会加剧创建的问题一个新的数组,因为第二个数组可以很容易地达到数千个元素。我非常感谢任何帮助或建议;甚至伪代码都没问题。
答案 0 :(得分:3)
我会做你想做的循环。总计列表中任何歌曲的最大播放次数,然后通过计算反向加权的数字并从反向总计中选择来反转概率。像这样:
function pickRandom(myArray) {
var maxPlays = 0, reverseTotPlays = 0, ipl, picked, revAcc = 0;
// Empty array or bad input param
if (!myArray || !myArray.length) {
return -1;
}
// Calculate the max plays for any song in the list
for (ipl = 0; ipl < myArray.length; ++ipl) {
if (myArray[ipl].plays > maxPlays) {
maxPlays = myArray[ipl].plays;
}
}
maxPlays += 1; // Avoid excluding max songs
// Calculate the reverse weighted total plays
for (ipl = 0; ipl < myArray.length; ++ipl) {
reverseTotPlays += maxPlays - myArray[ipl].plays;
}
// Choose a random number over the reverse weighted spectrum
picked = ~~(Math.random() * reverseTotPlays);
// Find which array member the random number belongs to
for (ipl = 0; ipl < myArray.length; ++ipl) {
revAcc += maxPlays - myArray[ipl].plays;
if (revAcc > picked) {
return ipl;
}
}
return myArray.length - 1;
}
var pp = [{ plays: 3 }, { plays: 1 }, { plays: 2 }];
console.log(pickRandom(pp));
使用JSFiddle Here
编辑:如果您不希望在播放已在列表中播放最多次数的歌曲时出现零概率,请在第一次循环后为maxPlay添加+1。
答案 1 :(得分:0)
最简单的做两步选择,首先选择一首随机歌曲,然后查看该歌曲是否通过了第二次测试,旨在优先选择播放较少的歌曲。如果第二次测试失败,则将其丢弃并再次启动整个过程。
一个例子(原谅我,如果我犯了一个错误,自从我做了任何javascript以来已经很长时间了):
function pickRandom(){
var candidate;
while (true){
candidate = songs[Math.round(Math.random() * (songs.length - 1))];
//largest_played is the largest number of plays of any one song
// I've magiced it out of nowhere here, find it in a manner that
// suits your program.
if ( candidate.plays/largest_played < math.random() ){
return candidate;
}
}
}
显然,缺少很多光泽和错误检查,但它应该足以让你开始。