考虑下表:
Song
----
SongID GUID Primary Key
Name Varchar
dateAdded DateTime
我正在创建一个播放Song表中歌曲的网站。我想为网站的每个访问者生成一个随机排序的播放列表,该访问者可以在不使用任何服务器端存储(没有会话风格或数据库存储)的情况下持续存在请求。播放器具有播放,下一首歌曲和上一首歌曲按钮。跨请求保留此数据的最有效方法是什么?查询此信息的最有效查询是什么?我有一个解决方案(在mySql中)有效,但我希望看看是否有效的方法。另外,我不想将答案偏向使用我的解决方案。请在答案中加入查询。
答案 0 :(得分:4)
更新:这是我遵循的要求,拼写出来:
我认为你还需要一条信息:一个整数位置的二级歌曲键,除了你的GUID(或者你可以用它替换它)。然后,结合PRNG,您可以进行最简单快速的查找。伪代码:
def next_song(initial_seed, current_prng_index, high_song_index):
"""Returns the next song and the parameters to be later passed
to next/prev_song."""
while True:
current_prng_index += 1
current_seed = PRNG_advance(initial_seed, current_prng_index)
song_index = song_index_from_seed(current_seed, high_song_index)
song_id = (SELECT SongID FROM Songs WHERE SongIndex=song_index)
if song_id: # test, somehow, for non-deleted songs
return song_id, (initial_seed, current_prng_index, high_song_index)
# include values that must be passed on the next call
# prev is the same, except uses -= 1
def song_index_from_seed(seed, highest):
return seed % (highest + 1)
# simple, but better possibilities might exist for your data
def new_playlist():
"""Returns the parameters to be passed to next/prev_song."""
high_song_index = (SELECT MAX(SongIndex) FROM Songs)
return get_a_random_number(), 0, high_song_index
这会在每首下一首歌曲上逐渐变慢,并且不允许通过倒退来进行包装(没有一些创造力)。如果您找到合适的可逆PRNG(虽然在PRNG,AFAIK中不常见):
def next_song(current_seed, high_song_index):
while True:
current_seed = PRNG_next(current_seed)
song_index = song_index_from_seed(current_seed, high_song_index)
song_id = (SELECT SongID FROM Songs WHERE SongIndex=song_index)
if song_id: # test, somehow, for non-deleted songs
return song_id, (current_seed, high_song_index)
# include values that must be passed on the next call
# prev is the same, except uses PRNG_prev
它通过跳过这些歌曲来处理删除(或者如果你永远不删除那些甚至不是问题),但无论删除了多少,都不会改变顺序。插入由song_index_from_seed函数处理,通过约束索引使新歌不会被“看到”。如果已删除所有可用歌曲,这也是一个无限循环,但我认为此代码处理所有其他极端情况。
(将下一个/ prev版本重构为一个常见函数的简单包装器并不难,但是为了提高清晰度,这里省略了。)
我已经用我的位置索引有效地替换了你的dateAdded,但这是一个改进,因为你不需要将删除的行保留为假人(但仍然不能重用他们的索引),就像你计算位置索引一样通过在dateAdded上订购。
使用像这样的PRNG似乎很幼稚,但有效;并且你必须要小心选择的PRNG和song_index_from_seed组合的行为与你期望的一样:一些初始种子可能会生成“非随机”播放列表。 (不是因为它们不是随机的,而是因为听众期望混合歌曲而不仅仅是4, 4,9, 9,...)
答案 1 :(得分:3)
这是一个想法,如果SQL,将进行伪随机排序。我现在没有数据库来测试它,所以实际的SQL可能需要调整。我还假设你的ID列是整数。
随机选择两个多位数素数,P1和P2,其中P2大于P1。
从歌曲顺序中选择*(mod((ID * P2),P1)),ID
想法是将ID乘以一个素数,然后使用P1进行mod操作。这应该可以很好地改变歌曲,每个素数对都会以不同的方式改变它们。
要重新创建确切的顺序,您只需要做P1和P2,它应该只是几个字符。在请求期间来回发送没问题。您可能需要稍微尝试一下素数范围,但我猜想使用所有4,5,6位数的素数应该会给你相当多的初始化。
答案 2 :(得分:1)
您可以使用Pseudo random sequence。将gseed和播放列表位置存储在cookie中。伪随机种子将保证您在每个请求上重新创建相同的播放列表序列,而无需在服务器上实际存储播放列表