给定时间范围内的事件近似计数

时间:2013-09-04 14:12:52

标签: algorithm math data-structures

我希望能够有效地计算给定时间范围内重复事件的(近似)计数。

示例:我正在尝试重复从主机下载文件。它通常工作正常,但有时在网络拥塞时会发生错误。我不关心这些单一错误。但是,每隔一段时间,主机就会脱机,所以我的所有尝试都会失败。在这种情况下,我想自动停止我的程序再次尝试。

所以我需要找出最近x分钟发生了多少错误。当数字低于某个阈值时,没有任何反应。当它在上面时,我想采取行动。计数不必100%准确,只能足够准确地告诉我是否达到了阈值。

一种简单但无效(O(n))的方法是只存储事件的时间戳,然后为每个新事件通过迭代它们并比较它们来确定先前事件的数量。时间戳(直到达到时间范围)。 [旁白]我想这就是SQL引擎为WHERE timestamp BETWEEN NOW() AND INTERVAL X MINUTES所做的事情,除非它们在列上有索引。 [/撤销]

我想要一个具有常量(O(1))复杂度的解决方案。到目前为止,我认为我将保持对每个事件增加1的事件的反击。我还将存储最近发生的时间戳。然后,当一个新的事件发生时,通过一些数学魔术,我可以使用当前时间和存储的时间戳来减少计数器,以反映过去x分钟内发生的事件数量。

不幸的是,我的数学技能不能胜任这项任务。有人可以提供一些提示吗?

3 个答案:

答案 0 :(得分:2)

如果您要在最后x分钟内确定故障计数的阈值,为什么不将故障时间戳存储在容量等于阈值的循环缓冲区中?插入显然是O(1),并检查是否有足够的故障,测试最近插入的时间戳是否在最后x分钟内。

答案 1 :(得分:1)

解决此问题的一种简单方法是让每个错误增加一个阈值计数器,并为每个 ok 下载重置为零。这将跟踪许多下载连续失败的问题,并且可能足以解决您的问题。

或者你可以做某种移动平均线。以下代码是一种简单的方法:

errorRate = errorRate * 0.8
if (error) {
   errorRate = errorRate + 0.2
}

给出了这样的进展:

Download#   Status  errorRate
     1      ok      0.000
     2      ok      0.000        <=
     3      error   0.200        <= Low rate of errors
     4      ok      0.160        <= 
     5      ok      0.128        
     6      error   0.302        
     7      error   0.442        
     8      ok      0.354
     9      ok      0.283
    10      ok      0.226
    11      error   0.381
    12      error   0.505
    13      error   0.603
    14      ok      0.483
    15      error   0.586
    16      error   0.669        <= High rate of errors shows 
    17      ok      0.535
    18      ok      0.428
    19      ok      0.343
    20      ok      0.274
    21      ok      0.219        <= goes back down after some ok downloads
    etc..

您可以使用因子0.8和0.2来获得您喜欢的进展

答案 2 :(得分:0)

基于@ ebbe-m-pedersen的评论,这就是使用Redis作为数据存储在PHP中的解决方案:

function error_handler() {
    $threshold = 100; // how many errors may occur
    $timeframe = 60 * 5; // 5 minutes, how many minutes may pass
    $now = time();

    // get error stats from redis
    $key_base = 'errors:';
    $count = $redis->get($key_base . 'count'); // calculated count
    $last = $redis->get($key_base . 'last'); // timestamp

    // calculate damping factor
    $rate = ($now - $last) / $timeframe;
    $rate = min($rate, 1.0); // $rate may not be larger than 1
    $rate = 1 - $rate; // we need the inverse for multiplying

    // calculate new error count
    $count = (int)($count * $rate);

    if ($count > $threshold) {
        // take action
    }

    // increase error
    $count++;

    // write error stats back to redis
    $redis->set($key_base . 'count', $count);
    $redis->set($key_base . 'last'. $now);
}