您为Zynga工作,并希望计算不同游戏的当前活跃玩家数量。您的Web服务器处理来自许多不同游戏的ping,每个用户都有一个唯一的GUID。必须能够一次查询一个游戏的活跃用户数。活跃用户是那些在最后一刻获得ping的用户。
日志行连续进入Web服务器:
10.1.12.13 - - "http://zynga.com/ping?guid=<guid>&game=<gameID>" -
计算活跃用户的最快捷/最简单的方法是什么? 请使用一些代码建议45分钟的答案。
我的版本
// web server interface, every time ping comes in count() will be called
// void count(String gameId, String guid)
// int getNumberActivePlayers(String gameId)
struct Record{
String gameID;
String guid;
};
class PingStorage{
private:
max_heap<long, Record> storage;
public:
// O(log(n))
// n = total number of elements in storage
void count(String gameId, String guid){
long currentTimeStamp = getUnixTimeStamp();
Record rec ;
rec.gameId = gameId;
rec.guid = guid;
storage.add(currentTimeStamp, rec);
}
//N = numner of records in last ,minutes in storage
//O(N)
int getNumberActivePlayers(String gameId){
map<String, Set<string> > game2user;
long tillTimeStamp = getUnixTimeStampNow() - 60;
while(true){
pair<long, Record> rec = storage.getMax(); //O(1)
if(rec.first <= tillTimeStamp) break;
Set<String> temp = game2user[rec.gameid]; //O(1)
temp.add(rec.userid); //O(log(N)) - O(1)
}
return game2user[gameID].size();
}
};
答案 0 :(得分:7)
假设这是一个实时解决方案,您可以在O(1)中处理ping请求,在O(1)中生成当前播放器统计信息,并通过牺牲一些准确性来使用O(num_player)空间。关键是要时间安排。
<强>概述强>
基本思想是将离散时间间隔表示为对象,并在这些对象中存储以下属性:在此时间间隔内未执行ping的不同玩家的数量。要查询活动用户数,请计算构成最后一分钟的最后x个时间间隔的加权总和。
<强>详情
首先,选择可接受的时间分辨率。在这个例子中,我选择15秒的间隔。
维护五个PingInterval数据结构以表示其中五个间隔(跨越1个间隔而不是1分钟)。 PingInterval包含一个属性:一个计数器。这些PingIntervals在PingMonitor中维护。每次玩家ping,都会更新PingMonitor中的地图,将每个玩家映射到当前时间间隔。执行此映射时,请执行以下步骤,以维护PingIntervals中的计数(根据概述部分中描述的特征)。
(如果尚未存在表示当前时间的PingInterval,请将最旧的PingInterval设置为null,以线程安全的方式创建新的PingInterval,然后照常继续。)
如果要查询活动用户数,请计算最后五个时间间隔的时间过去加权总和。例如,如果您距离当前时间间隔仅5秒(意味着该间隔的下一个10秒尚未发生),请计算此值:2/3 *最早的间隔+ 4个最新间隔的总和。
其他想法
五个间隔非常保守;我们可以大幅度扩大数量以获得更高的准确度(可能是每秒一次),它仍然可以为我们节省大量成本。重要的是,我们的时代现在是不连续的时间间隔。这意味着当我们计算活跃用户的数量时,我们不必查看每个单独的时间(这等于用户数);相反,我们可以查看我们预定义的x个时间段。
答案 1 :(得分:3)
我的方法是使用deque(在本文的其余部分中称为队列)将所有GUID推送到观察到的,即按年龄排序。另外,我会使用一个hashmap,其中包含指向队列中任何GUID条目的指针。
当新的GUID被推送到队列时,旧的条目(如果有的话)将在hashmap中被查找,从队列中删除,而新的条目被分配给hashmap。
随着时间的推移,队列中超过年龄阈值的所有条目都将被弹出(并从散列图中删除)。
队列的长度(也就是活动用户数)可以作为单独的变量跟踪,以避免每次查询跳过队列。
要支持多个游戏,只需为每个游戏ID添加此类结构。
复杂性:O(1)插入/删除观察(给定完美的散列,即没有冲突),O(1)查询,O(n)空间。
答案 2 :(得分:0)
编辑:我认为这个问题不是为了获得“有多少用户活跃现在”的问题的实时答案,而是获取历史价值 - 有多少用户活跃于3:25 PM。我将旧解决方案保留在新解决方案之下:
所以,你想知道现在有多少用户活跃,每场比赛保持一个队列。每当您看到新的日志条目时,找出它所属的游戏,并将其添加到游戏的队列中。每次添加后,清理队列开头的旧条目(清理时超过1分钟的所有条目)。
当被问及游戏中活跃用户的数量时,在游戏队列中进行相同的清理,并返回队列的深度。
保持哈希映射游戏到队列,你得到一个O(N)操作,其中N是日志中的行数 - 每行最多处理两次 - 一次用于添加,一次用于删除。你还可以在每次添加和查找时进行额外的比较(当确定队列条目不足够老时),但这是N的常数时间。所以O(N)都是。
以前对其他问题的回答: 看到没有那么多分钟(每天1440),我会为每场比赛创建一个矢量,每分钟都有一个插槽。
遍历日志文件,为每一行获取时间,将其四舍五入到最近的分钟,并将1添加到阵列中的相应插槽。一旦完成,您就会确切知道每分钟每个游戏的活跃用户数量。
复杂性 - O(N),其中N是日志文件中的行数。
要支持多个游戏,只需使用哈希从游戏名称映射到其矢量。
现在,假设您只检查整个分钟边界(1:00:00,1:01:00等)的活跃用户。这可能是你需要做的事情。
答案 3 :(得分:0)
这将是我的答案序列:
zcount
命令查询最后一分钟或最后一小时内有多少客户处于活动状态。这是有用和可靠的。