繁体   English   中英

Javascript中的加权随机数生成

[英]Weighted random number generation in Javascript

我有一个看起来像这样的数组:

[
    {
        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值将每个元素多次复制到一个新数组plays ,然后从中选择一个元素,但这会加剧创建新数组的问题,因为那个数组可以很容易地达到数千个元素。 我非常感谢任何帮助或建议; 甚至伪代码也没关系。

我会做你想做的循环。 总计列表中任何歌曲的最大播放次数,然后通过计算反向加权的数字并从反向总计中选择来反转概率。 像这样的东西:

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

编辑 :如果您不想在播放列表中已播放最多次数的歌曲时出现零概率,请在第一次循环后向maxPlays添加+1。

最简单的做两步选择,首先选择一首随机歌曲,然后查看该歌曲是否通过了第二次测试,旨在优先选择较少播放的歌曲。 如果第二次测试失败,则将其丢弃并再次启动整个过程。

一个例子(原谅我,如果我犯了一个错误,自从我做了任何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;
        }
    }
} 

显然,缺少很多光泽和错误检查,但它应该足以让你开始。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM