全服排行榜算法思路
阿新 • • 發佈:2017-08-05
set range 包含 設計 update name private ear ret
排行榜是遊戲中一個常見的系統,不過要做好並不容易,比如要對全服幾W名玩家做實時排行的話,性能需要花心思去優化的問題。
這裏設計了一個基於桶排序的面向全服玩家的通用排行榜
一個簡單的思路就是按排序的KEY的分值進行分桶,但是一個弊端就是隨著遊戲生命推進,會出現一個分數段類的玩家大規模集中,導致分桶失去意義。
所以這裏采用了對桶的容量做上限,然後達到上限就分裂桶然後動態計算桶的上下限的做法
桶的定義如下(代碼采用c#編寫):
class sub_rank_list { public Hashtable low;public Hashtable up; private HashSet<String> mem_set; public List<Hashtable> rank_list; private IComparer<Hashtable> comparer; }
使用Hashtable描述排序的信息是為了排行榜的通用性。
每個Hashtable中需要包含對應玩家的unionid和排序信息
玩家的unionid保存在mem_set中(在mem_set中可以找到玩家的id,表示玩家在這個桶內)
要獲取一個玩家在排行榜上的排名,則先查找到玩家在哪個桶內,然後計算 這個桶前有多少玩家和玩家在該桶內的排名之和就是玩家的排名
public int get_rank(String uuid) { int rank = 0; foreach(var sub_rank_list in rank_data) { if (!sub_rank_list.in_rank(uuid)) { rank+= sub_rank_list.count(); continue; } rank += sub_rank_list.rank(uuid); break; } return rank; }
整個排行榜的代碼如下:
using System; using System.Collections; using System.Collections.Generic; namespace rank { //升序排行 class rank { public rank(IComparer<Hashtable> _comparer) { rank_data = new List<sub_rank_list>(); comparer = _comparer; } public void clear_rank() { rank_data = new List<sub_rank_list>(); } public void update_rank(Hashtable data) { String uuid = (String)data["uuid"]; foreach(var sub_list in rank_data) { if (!sub_list.in_rank(uuid)) { continue; } if (sub_list.in_range(data)) { sub_list.update_rank(data); } else { sub_list.del_rank(uuid); insert_rank(data); } break; } } public void insert_rank(Hashtable data) { sub_rank_list sub_list = null; sub_rank_list up_list = null; sub_rank_list low_list = null; foreach (var _sub_list in rank_data) { if (up_list == null) { up_list = _sub_list; } else { if (comparer.Compare(up_list.up, _sub_list.up) < 0) { up_list = _sub_list; } } if (low_list == null) { low_list = _sub_list; } else { if (comparer.Compare(low_list.low, _sub_list.low) > 0) { low_list = _sub_list; } } if (!_sub_list.in_range(data)) { continue; } sub_list = _sub_list; break; } if (sub_list == null) { if (low_list == null && up_list == null) { sub_list = new sub_rank_list(data, data, comparer); } else { if (comparer.Compare(low_list.low, data) > 0) { sub_list = low_list; sub_list.low = data; } if (comparer.Compare(up_list.up, data) < 0) { sub_list = up_list; sub_list.up = data; } } } sub_list.insert_rank(data); if (sub_list.count() > 2000) { var new_rank = sub_list.split_rank(); rank_data.Add(new_rank); for(int i = 0; i < rank_data.Count - 1; i++) { var rank1 = rank_data[i]; var rank2 = rank_data[i + 1]; if (comparer.Compare(rank1.low, rank2.up) < 0) { rank_data[i] = rank2; rank_data[i + 1] = rank1; } } } } public int get_rank(String uuid) { int rank = 0; foreach(var sub_rank_list in rank_data) { if (!sub_rank_list.in_rank(uuid)) { rank += sub_rank_list.count(); continue; } rank += sub_rank_list.rank(uuid); break; } return rank; } public Hashtable get_rank(int rank) { int rank_low = 0; int rank_up = 1; foreach(var sub_rank_list in rank_data) { rank_low = rank_up; rank_up += sub_rank_list.count(); if (rank >= rank_low && rank < rank_up) { return sub_rank_list.rank_list[rank - rank_low]; } } return null; } class sub_rank_list { public sub_rank_list(Hashtable _low, Hashtable _up, IComparer<Hashtable> _comparer) { low = _low; up = _up; mem_set = new HashSet<String>(); rank_list = new List<Hashtable>(); comparer = _comparer; } public bool in_range(Hashtable data) { int c1 = comparer.Compare(data, up); int c2 = comparer.Compare(data, low); return c1 <= 0 && c2 > 0; } public bool in_rank(String uuid) { return mem_set.Contains(uuid); } public int count() { return mem_set.Count; } public int rank(String uuid) { int rank = 0; foreach(var mem in rank_list) { rank++; if (uuid == (String)mem["uuid"]) { break; } } return rank; } public void update_rank(Hashtable data) { foreach(var _data in rank_list) { if ((String)_data["uuid"] != (String)data["uuid"]) { continue; } rank_list.Remove(_data); break; } rank_list.Add(data); rank_list.Sort(comparer); } public void del_rank(String uuid) { foreach (var _data in rank_list) { if ((String)_data["uuid"] != uuid) { continue; } mem_set.Remove(uuid); rank_list.Remove(_data); break; } } public void insert_rank(Hashtable data) { mem_set.Add((String)data["uuid"]); rank_list.Add(data); rank_list.Sort(comparer); } public sub_rank_list split_rank() { sub_rank_list new_rank = new sub_rank_list(low, up, comparer); int _count = count() / 2; Hashtable data = null; for (int i = 0; i < _count; i++) { data = rank_list[i]; del_rank((String)data["uuid"]); new_rank.insert_rank(data); } new_rank.low = data; up = rank_list[0]; return new_rank; } public Hashtable low; public Hashtable up; private HashSet<String> mem_set; public List<Hashtable> rank_list; private IComparer<Hashtable> comparer; } private List<sub_rank_list> rank_data; private IComparer<Hashtable> comparer; } }
全服排行榜算法思路