如何实施“Celestial Jukebox”的洗牌?
更准确地说,在每个时间t,返回0..n(t)之间的均匀随机数,这样整个序列中就没有重复,n()随着时间的推移而增加。
对于具体示例,假设一种统一费率的音乐服务,它允许通过基于0的索引号播放目录中的任何歌曲。每隔一段时间,就会增加新的歌曲,增加索引号的范围。目标是每次播放一首新歌(假设目录中没有重复)。
理想的解决方案在现有硬件上是可行的 - 我如何在8MB的DRAM中找到600万首歌曲的列表?类似地,高歌数会加剧O(n)选择时序。
- 对于LCG发生器,如果在0..N 0 上给出部分耗尽的LCG,则可以将其转换为0..N 1 上的不同LCG (其中N1> N0),不重复耗尽的序列 - 检查某首歌是否已播放似乎很快失控,虽然这可能是唯一的方法?这有一个有效的数据结构吗?
答案 0 :(得分:3)
我喜欢这种非重复随机选择的方式是有一个列表,每次我在[0-N)
之间随机选择一个项目时,我会从该列表中删除它。在您的情况下,当新项目添加到目录时,它也将添加到尚未选择的列表中。一旦结束,只需将所有歌曲重新加载到列表中即可。
编辑:
如果考虑到v3的建议,可以在O(1)
初始化步骤后的基本O(N)
时间内完成此操作。它保证了不重复的随机选择。
这是概述:
i
(来自[0,N)
)i
i
处的洞替换为Nth
项(如果i == Nth
,则为空,并递减N
N
由于您正在尝试处理相当大的集合,我建议使用数据库。一个基本上包含两个字段的简单表格:id
和“pointer
”(其中“pointer
”告诉您要播放的歌曲,可能是GUID,FileName等,具体取决于你想怎么做)。在id
上有一个索引,你应该在应用程序运行之间保持稳定的性能。
编辑8MB限制:
嗯,这确实让它变得有点困难......在8 MB中,你可以使用32位密钥存储最多约2M的条目。
所以我建议的是预先选择下一个2M条目。如果用户一生中播放2M歌曲,该死的!要预先选择它们,请使用上述算法执行pre-init步骤。我要做的一个改变就是当你添加新歌时,滚动骰子,看看你是否想要随意添加这首歌。如果是,则选择随机索引并将其替换为新歌曲的索引。
答案 1 :(得分:1)
对于600万首歌曲,限制为8MB,即使每首歌曲只有一个32位整数,也显然没有空间存储。除非您准备将列表存储在磁盘上(在这种情况下,请参见下文)。
如果你准备放弃将新项目立即添加到随机播放的要求,你可以在当前的歌曲集上生成一个LCG,然后当它用完时,只在那些歌曲上生成一个新的LCG。自从你开始以来添加冲洗并重复,直到您不再有新歌。您还可以使用this rather cool algorithm在任意范围内生成不可取的排列而不存储它。
如果您准备放宽对800万首歌曲的8MB ram的要求,或者转到磁盘(例如,通过内存映射),您可以在开始时从1..n生成序列,将其随机播放使用fisher-yates,每当添加新歌曲时,从最远未播放的部分中选择一个随机元素,在其中插入新ID,并将原始ID附加到列表的末尾。
如果您不太关心计算效率,可以存储所有歌曲的位图,并随机重复选择ID,直到找到尚未播放的ID。这需要600万次尝试才能找到最后一首歌(平均而言),这在现代CPU上仍然很快。
答案 2 :(得分:0)
虽然Erich的解决方案可能更适合您的特定用例,但检查歌曲是否已经播放的速度非常快(使用基于散列的结构分摊O(1)),例如Python中的set
或者是C ++中的hashset<int>
。
答案 3 :(得分:0)
您可以简单地生成从1到n的数字序列,然后使用Fisher-Yates shuffle对其进行随机播放。这样就可以保证序列不会重复,无论n。
答案 4 :(得分:0)
您可以在数组中使用链接列表: 要构建初始播放列表,请使用包含以下内容的数组:
struct playlistNode{
songLocator* song;
playlistNode *next;
};
struct playlistNode arr[N];
还要保留“头部”和“空闲列表”指针;
在2遍中填充:
1.用0..N。中填写目录中所有歌曲的arr
2.随机遍历所有索引,填入next
指针;
删除播放的歌曲是O(1):
head=cur->next;
cur->song=NULL;
freelist->next = freelist;
cur->next=freelist;
freelist=cur;
插入新歌也是O(1):随机选择一个数组索引,并修补一个新节点。
node = freelist;
freelist=freelist->next;
do {
i=rand(N);
} while (!arr[i].song); //make sure you didn't hit a played node
node->next = arr[i].next;
arr[i].next=node;