咨询关于快速排序取排名的问题

yangyunzhao 2015-06-24 01:10:28
最近做游戏服服务端,新增一个排名的需求,需要显示经验值前100名玩家,同时也需要显示自己的排名。

我分析了一下,其实是三个问题:
1、排序:服务启动的时候,从mysql读入到内存,如何根据经验值快速排序。玩家规模在10万到100万之间。经验值是一个uint32类型数据。

2、插入:在经验值改变后,如果更新这个排序。

3、查找:根据玩家的经验值,找到自己的排名。

这三个问题,如果不考虑效率问题,我自己可以解决。
这里想请教一下,有没有最优或者近似最优的算法,用于解决我的问题,谢谢!!
...全文
402 9 打赏 收藏 转发到动态 举报
写回复
用AI写文章
9 条回复
切换为时间正序
请发表友善的回复…
发表回复
yangyunzhao 2015-06-25
  • 打赏
  • 举报
回复
问题似乎没有那么复杂,如果不放数据库,而是在内存中计算的话。 利用堆排序(stl自带的make_heap、sort_heap),处理1千万条数据,大概需要1400毫秒左右。 组合使用lower_bound和upper_bound,千万人中查询,时间可以忽略(精确到毫秒时,为0)。 完全可以单独一个线程处理排序,N分钟刷新一次总榜。 前N名排行榜,则可以实时计算。
JiangWenjie2014 2015-06-24
  • 打赏
  • 举报
回复
引用 5 楼 yangyunzhao 的回复:
[quote=引用 4 楼 JiangWenjie2014 的回复:] 1) 现在有100万个数据需要排序,从大到小,如何得到前100个? 堆排序(最小堆),堆的大小是100,100万个输入依次尝试进入堆。如果堆顶(最小值)的值小于当前的值,那么用当前的数据替换堆顶,然后调整堆(lg100),时间复杂度是NlgM,其中N是100万,N是100。 2) 如何显示自己的排名? 假设同 1),还是100万个数据,假如自己的经验值是99999,从数据库中找到经验值小于99999的玩家个数N,然后再从数据库中查找经验值是99999的玩家的个数M,那么自己的排名近似值就是(N+M/2) 。 3) 经验值改变后如何更新排名? 假设经验值只增不减,那么如果用户A经验值变了,那么把他的经验值再次尝试进入堆就可以了。如果经验值会减的话,堆排序就不灵了。
第1和第3个问题,应该没有问题。 建立最小堆,应该就启动的时候建立一次即可,后期不需要处理。效率不存在问题。 插入的时候,基数很小,效率也不存在问题。 问题就在于2:在数据库中查询所有大于9999和所有等于9999的玩家,即使有索引,效率也不高吧?[/quote] 我觉得游戏里面的排名不都是这样的吗:前9999名的玩家都可以查询自己的名字,如果一个玩家排名在10000之外的话,就会显示“您的排名9999+”(万名开外)。如果要实时高效查询的话,那准确性就没法保证了,比如分区,每个分区区间是1024,2^32/2^10 = 2^22 = 4MB,数组中每个元素只记录玩家的数量(4字节),总共8MB的内存空间。玩家查询排名的时候,可以再查一次数据库(比如当前经验值是88,那么查询经验值大于88且小于1024玩家个数,查询经验值等于88玩家个数),也可以不查数据库,算个平均值。然后这个排名每隔5分钟更新一次。
yangyunzhao 2015-06-24
  • 打赏
  • 举报
回复
引用 6 楼 zhao4zhong1 的回复:
名次超过1000的都显示1000名以外。
我也想这样,但是策划说这样不行。 但是可以显示一个不太正确的数字(近似排名)
赵4老师 2015-06-24
  • 打赏
  • 举报
回复
名次超过1000的都显示1000名以外。
yangyunzhao 2015-06-24
  • 打赏
  • 举报
回复
引用 4 楼 JiangWenjie2014 的回复:
1) 现在有100万个数据需要排序,从大到小,如何得到前100个? 堆排序(最小堆),堆的大小是100,100万个输入依次尝试进入堆。如果堆顶(最小值)的值小于当前的值,那么用当前的数据替换堆顶,然后调整堆(lg100),时间复杂度是NlgM,其中N是100万,N是100。 2) 如何显示自己的排名? 假设同 1),还是100万个数据,假如自己的经验值是99999,从数据库中找到经验值小于99999的玩家个数N,然后再从数据库中查找经验值是99999的玩家的个数M,那么自己的排名近似值就是(N+M/2) 。 3) 经验值改变后如何更新排名? 假设经验值只增不减,那么如果用户A经验值变了,那么把他的经验值再次尝试进入堆就可以了。如果经验值会减的话,堆排序就不灵了。
第1和第3个问题,应该没有问题。 建立最小堆,应该就启动的时候建立一次即可,后期不需要处理。效率不存在问题。 插入的时候,基数很小,效率也不存在问题。 问题就在于2:在数据库中查询所有大于9999和所有等于9999的玩家,即使有索引,效率也不高吧?
JiangWenjie2014 2015-06-24
  • 打赏
  • 举报
回复
1) 现在有100万个数据需要排序,从大到小,如何得到前100个? 堆排序(最小堆),堆的大小是100,100万个输入依次尝试进入堆。如果堆顶(最小值)的值小于当前的值,那么用当前的数据替换堆顶,然后调整堆(lg100),时间复杂度是NlgM,其中N是100万,N是100。 2) 如何显示自己的排名? 假设同 1),还是100万个数据,假如自己的经验值是99999,从数据库中找到经验值小于99999的玩家个数N,然后再从数据库中查找经验值是99999的玩家的个数M,那么自己的排名近似值就是(N+M/2) 。 3) 经验值改变后如何更新排名? 假设经验值只增不减,那么如果用户A经验值变了,那么把他的经验值再次尝试进入堆就可以了。如果经验值会减的话,堆排序就不灵了。
yangyunzhao 2015-06-24
  • 打赏
  • 举报
回复
引用 1 楼 flyrack 的回复:
你可以做个缓冲 比如1万以上的多少人 2万以上的多少人 然后就可以取区间排序了
分区是个思路,但是不能线性分区,因为玩家很有可能大量聚集在某个区间段,所谓的二八定律。 前期,用户基本集中在低分段。后期,用户基本集中在高分段。
mymtom 2015-06-24
  • 打赏
  • 举报
回复
经验值有个特点,通常只会增加
flyrack 2015-06-24
  • 打赏
  • 举报
回复
你可以做个缓冲 比如1万以上的多少人 2万以上的多少人 然后就可以取区间排序了

64,648

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧