4

私は次のような配列を持っています:

[
    {
        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;
}

このソリューションには、私が不満に思っていることがたくさんあります。まず、実際には未再生の曲、または配列内のすべてが少なくとも1回再生されている場合は古いランダムな曲を選択するだけなので、それほど重み付けされていません。第二に、それは新しい配列を作成します、そしてプレイリストは時々何百もの曲を持っているので、それは私が可能であれば避けたいものです。

私が思いついた最も近い解決策は、各要素をそのplays値に基づいて新しい配列に複数回コピーし、その中から要素を選択することですが、その2番目の配列以降、新しい配列を作成する問題が悪化します何千もの要素に簡単に到達できます。私はどんな助けや提案にも大いに感謝するでしょう。擬似コードでも問題ありません。

4

2 に答える 2

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 を操作する

編集:リスト内の最大回数再生された曲を再生する確率をゼロにしたくない場合は、最初のループの後に maxPlays に +1 を追加します。

于 2012-09-20T15:55:54.183 に答える
0

おそらく最も簡単な方法は、2 段階の選択を行うことです。最初にランダムな曲を選択し、次に、あまり再生されていない曲を優先的に選択するように設計された 2 番目のテストにその曲が合格するかどうかを確認します。その 2 番目のテストに失敗した場合は、それを破棄して、プロセス全体をもう一度開始します。

例(私が間違いを犯した場合は許してください。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;
        }
    }
} 

明らかに、多くのグロス チェックとエラー チェックが欠けていますが、始めるには十分なはずです。

于 2012-09-20T16:03:42.627 に答える