首先网站有一堆文章 Post,对于一个 User,我希望可以随机抽一篇文章给他看, 每次抽的文章必须不一样,并且不重复。文章会不断地增加。每个文章有一个 ID,ID 是顺序产生的。
可以用直接简单的解法:记录一个 Visited: user_id, post_id,但是这样就会出现一个很大的表, 因为 user 是 10000+,post 数量级数是 1000+ 的,每次获取文章的复杂度就比较高了。
请问是否有一个算法,可以让获取文章的复杂度保持在常数级?谢谢!
就用一个表记录一下取过的文章,因为有外键和主键在,查询效率完全不用担心:
query = "id not in (select id from visited where user_id = 1)"
Post.where(querey).offset(rand(Post.where(query).count)).limit(1)
{ user_id: [post_id_1, post_id_2] }(DB 里可以用 Array 保存?) 每次都 random 这个 array,取走一个,取走后把这个 post_id 从 array 里删除,那么从取的角度来说就是 O(n),而且不会重复。 如果日后有文章增加,就需要为每个用户记录的 array 里加入这个 post_id,这里可能操作数据比较巨大,因为每个用户都要加入这个 post_id。 如果以 vistited 作标记的话,每次都要比对 unvistited 的,这样效率就没那么高
貎似我暂时想到这个方案,不知如何……
最近看《数学之美》的时候,看到搜索引擎原理,也是用布尔代数去表示某个词是否在某个网站出现过,就是 010101...... 我觉得也可以将 某用户 是否 读过某编文章 弄成一串二进制串,例如 0101 表示 用户 没有读过第 1,3 文章,读过了第 2,4 的文章。
而增加文章的时候,将二进制串左移(实质就是后面补 0)就 ok 了。
识别单从 Ruby 上来说不难吧,很轻易就可以从二进制串中取出 随机抽出 的文章用户有没有读过。 不过效率来说上面这种多了二进制 -> 字符串的操作多少浪费了空间和时间。
irb(main):005:0> 4.to_s(2)
=> "100"
irb(main):007:0> 4.to_s(2)[0]
=> "1"
想了一下,用朴素的双主键表方法,一般情况下数据量万以下,in 操作复杂度应该是 n 级别的。不过不需要用真的 random,取搜索到的前 10 个 random 就好,那么复杂度会减低到 log(n),问题不大。
我换一个表述方式,是否有一个 random 算法,可以支持(1..n)集合中的不重复随机,并且还支持集合数据插入的?就我所知,不支持插入的随机算法是有的。