基于积分的游戏排行榜实现方式探讨

竞技场往往是游戏中不可缺少的系统,根据不同的需求类型,竞技场排行榜的类型可以有如下划分:

1.按照排名更新的方式划分:依靠积分进行排行的竞技场,当玩家完成挑战后根据一定的规则进行双方积分的增减,依据新的积分进行重新排名;不依靠积分进行排行的竞技场,只有当排名较低玩家战胜了排名较高玩家时,会交换双方排名。前者一次积分变化会导致较多的玩家排名发生变动,后者一次最多只会影响两名玩家的排名,但要考虑相同玩家同时被挑战的情况。

2.按照排行是否严格划分:并列排行榜允许积分相同的玩家处于同一名次,严格排行榜需要处理玩家相同积分情况下决定排名的规则。

此处我们只讨论积分排行榜。

积分排行榜的关键在于对于一个有序数据结构的维护更新,而对于该数据结构的实现主要有两种思路:

1.自己使用语言实现有序的数据结构

2.借助现成的工具,如MySQL,Redis实现有序排序

自己制定数据结构的方法

1.有序列表

按照积分大小将列表进行排序。

查询TopN:只需获取列表前TopN个元素,时间复杂度O(1)。

查询指定玩家排名:计算积分大于该玩家的人数,时间复杂度O(n)。

更新排名:玩家积分发生变化,需要将旧元素删除,把积分变化后的新元素插入列表中并进行重新排序,若使用快速排序,则时间复杂度为O(n²)。不过大多数情况下列表是基本有序的,比起对单纯打乱的列表进行排序会好很多。

排行榜较小,对效率没有极端要求的时候可以使用这种方法。

2.数组

构建大小为M的数值rank[],此时积分s对应的排名为rank[s]。

查询TopN:获取列表rank[1:N]的元素即可。

查询玩家自己排名:查询积分s对应的排名直接查询rank[s]即可,时间复杂度O(1)。

更新:若用户积分从s变为s+n,则把rank[s]到rank[s+n-1]这n个元素值加1即可,时间复杂度为O(n)。

很明显,游戏中排行榜积分相同的情况并不少见,引入桶可以解决积分相同的情况,但积分更新和查询玩家自身排行的策略需要改变,改变之后的策略可能并不比有序列表效率高。

3.平衡二叉树(AVL)

查询TopN:获取树的前N个元素即可,时间复杂度O(1)。

查询自己的排名:需要对数进行遍历,找出积分高于自己的玩家数量,时间复杂度O(n)。

更新:需要将原元素删除,将新元素插入树中,时间复杂度O(logn)。

4.跳表(skiplist)

跳表本质是可以实现二分查找的有序链表。

个人了解不深,直接给出参考文章:

https://yuerblog.cc/2019/02/13/skiplist-rank/

感觉在计算个人排名的时候效率会比平衡二叉树高,其他地方与平衡二叉树相差不大。

5.树形分区算法

详见

https://cloud.tencent.com/developer/article/1621522

本质是平衡二叉树,但与上面单纯使用平衡二叉树的方法不同,作者预先构建了一棵包含所有积分范围的树,树的每一层会根据范围划分,再将每一个需要统计排名的积分通过范围的比较放入树中,更新树的时候会更新每一层所包含的积分数量。当需要获取积分排名的时候,只需要获取每一层大于该积分的积分数量相加即可,时间复杂度可达到O(logn)。具体的实现方法建议直接进入文章查看。

使用现有工具的方法

1.使用SQL查询。

当数据量不大的时候可以直接使用数据库的查询。使用INSERT,UPDATE插入与更新玩家,使用SELECT COUNT查询分数高于某个玩家的人数,即是该玩家的排名。

好处是数据操作的细节被数据库隐藏了,在数量较少的时候能够提供方便的操作,缺点是当数据量大的时候,相较于操作放置于内存的数据结构,其性能会非常低下。

2.使用Redis的sorted set

其内部使用了Hash table和skip list,用来做排行榜有着天生的优势:提供了丰富的排行榜查询接口,例如zrank()用于查询指定元素的排名(下标),zrevrange()用于获取指定区间的元素。

3.使用Erlang有序异键形式(sorted set)的ETS

也有许多团队选择使用Erlang语言进行游戏开发,此时Erlang自带的ETS也是不错的选择。ETS的有序异键表是用平衡二叉树实现的。

一些需要考量的其他因素

排行榜可以定时更新,还是需要同步更新?

排行榜是严格排行榜是可以存在并列排名的排行榜?

如果是同步更新的严格排行榜,则需要自己制定在分数相同情况下的排行规则,一些 有着默认排行规则的排行榜用不了。

除此之外,需要进行排行的玩家数量也是很关键的因素,如果玩家数量不多,或者对于排行过低的玩家排名的准确性(甚至是是否展示排名)不做太强制的要求,除却上面的方法可能还有更适合的实现方式。

备忘参考:

海量用户积分排名算法探讨

https://www.cnblogs.com/weidagang2046/archive/2012/03/01/massive-user-ranking.html

游戏排行榜 – 基于skiplist计算rank排名

https://yuerblog.cc/2019/02/13/skiplist-rank/

在C#中使用二叉树实时计算海量用户积分排名的实现

https://cloud.tencent.com/developer/article/1621522

简单高效的排行榜算法——树状数组

https://www.owenzhang.net/blog/325.html

谈谈陌陌争霸在数据库方面踩过的坑(排行榜篇)

https://blog.codingnow.com/2014/03/mmzb_db_2.html

排行榜算法设计实现比较

https://gameinstitute.qq.com/community/detail/101951

使用Redis Zset来处理活动常用排行榜(精确排行)

https://segmentfault.com/a/1190000011737336

如何对游戏服务器全服玩家进行排名?

https://www.zhihu.com/question/27933771

海量用户的积分排序问题算法的分析

https://segmentfault.com/a/1190000004268386

你可能感兴趣的:(游戏开发)