您的位置:首页 > 其它

关于《海量用户积分算法探讨》的读后总结和扩展

2016-03-05 08:52 288 查看
周末在看《码农 第一期 算法》的时候看到了这样一篇文章:《海量用户积分算法探讨》以下是原文地址:

http://www.ituring.com.cn/article/62896

下面是我的一些总结和讨论

需要解决的问题是:

某海量用户网站,用户拥有积分,积分可能会在使用过程中随时更新。现在要为该网站设计一种算法,在每次用户登录时显示其当前积分排名。用户最大规模为2亿;积分为非负整数,且小于 100万。

问题就可以总结为:

1.更改积分

2.查询排名

设计算法的时候也就要着重考虑查询和更改的时间复杂度

算法

文章中提到了四种算法:
1. 简单的查询语句实现,直接进行全表的遍历扫描,得出结果。

2.均匀分区,如:[0, 1000), [1000, 2000), …, [999 000, 1 000 000)这样划分。每一个分区有count个用户。在分区积分表的辅助下查询积分为s的用户的排名,可以首先确定其所属区间,把高于s的积分区间的count值累加,然后再查询出该用户在本区间内的排名,二者相加即可获得用户的排名。

3.采用线段二叉树的方法,假设最多有1
000 000积分,我们可以把[0, 1 000 000)作为一级区间;再把一级区间分为两个2级区间[0, 500 000), [500 000, 1 000 000),然后把二级区间二分为4个3级区间[0, 250 000), [250 000, 500 000), [500 000, 750 000), [750 000, 1 000 000)。这样我们就将得到一棵平衡二叉树。以区间范围为节点,每个节点存储着这个区间范围用户的总数,count。
该平衡二叉树的非叶子节点的count恒等于子节点count之和,且左子结点代表低分区间,右子结点代表高分区间。
在这种树形分区积分表的辅助下查询积分为s的用户排名,实际上是一个在区间树上由上至下、由粗到细一步步明确s所在位置的过程。比如,对于积分499
000,我们用一个初值为0的排名变量来做累加;首先,它属于1级区间的左子树[0, 500 000),那么该用户排名应该在右子树[500 000, 1 000 000)的用户数count之后,我们把该count值累加到该用户排名变量,进入下一级区间;其次,它属于3级区间的[250 000, 500 000),这是2级区间的右子树,所以不用累加count到排名变量,直接进入下一级区间;再次,它属于4级区间的……如此往复,直到最后我们把用户积分精确定位在21级区间[499 000, 499 001),整个累加过程完成,得出排名!

4.使用积分排名数组的方法。由于:用户的积分从s变为s+n,积分小于s或者大于等于s+n的其他用户排名实际上并不会受到影响,只有积分在[s, s+n)区间内的用户排名会下降1位。我们可以用一个大小为100
000 000 的数组表示积分和排名的对应关系,其中rank[s]表示积分s所对应的排名。初始化时,rank数组可以由user_score表在O(n)的复杂度内计算而来。用户排名的查询和更新基于这个数组来进行。查询积分s所对应的排名直接返回rank[s]即可,复杂度为O(1);当用户积分从s变为s+n,只需要把rank[s]到rank[s+n-1]这n个元素的值增加1即可,复杂度为O(n)。

算法分析

第1种算法无疑除了实现简单之外,没有任何优点,全表遍历时间,无论更新还是查找复杂度都很大。
第2种算法做了些许优化,不用全表遍历了,这样比如搜索积分为1 000的用户排名,就只需要在对应的积分区间里面遍历。但是根据二八定律,80%的用户集中在了20%的低分区,所以可能查询积分为1000的用户的时候,对应的积分区间还是有海量的用户,遍历的表还是很大。均匀分区的方法就被pass了,但是不均匀分区可以吗?将低分区划分的密集一些,将高分区划分的稀疏。但是这种分发过于随性,且一种分法肯定会由于系统的使用而变得不适用。
第3种方法是标准的线段二叉树,由于是平衡二叉树,所以查询和更新的时间复杂度都是O(Log(n)),这样就高效了很多,但是有一个问题是,当n很小的时候,这种算法的优势并不明显。而且作为一个积分排名系统,查询的频率远大于更新的频率,所以就有了算法4.
第4种算法在初始化和更改排名的时候复杂度是O(n),但是由于可以直接通过数组下标(即积分的值)查询排名,所以查询的复杂度是O(n)。

那么第3和第4孰优孰劣呢?的确在,n较小且重视查询的时候,算法4比算法3有优势,且实现起来简单很多。但是当数据量非常大的时候,算法4的更新的时间复杂度O(n)还是照O(Log(n))差了太远。所以还是要看数据量的大小,分情况来决定孰优孰劣。

我的拓展

原文博主说了,这是开放问题,那么我就说出我的解决方案。
算法3、4的确已经将问题较为优化的解决了。但是这其中其实还是有问题的。我们一直假设整个数据结构存储在计算机的主存中。可是,假设数据量极大,主存装不下,那么就意味着必须把数据结构放到磁盘上,大O假设所有操作耗时相等,那么此时大O模型不再适用。为了节省一次磁盘访问时间,我们愿意进行大量的计算。不平衡的二叉树这时候就是一个灾难,最坏情形下它具有线性的深度,平衡二叉树会好很多,1千万次磁盘访问,也就是需要log(10000000)≈25.
解决办法很简单,如果有更多的分支,就会有更小的深度,也就是说我们可以用b树来实现算法。具体的算法与算法3类似,仍然以积分区间为节点,节点储存用户数为数据。
这种算法在处理更大规模的数据时,较算法3、4又有了一定优化,但是仍然会在遇到小数据量的时候,凸显出查询效率较低,实现复杂等问题。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: