对最近X秒内收到的值的平均值进行采样

时间:2009-02-17 10:02:49

标签: algorithm statistics

我有一个调度成功和失败事件的类,我需要维护一个关于该类最后X秒内的平均失败次数/事件总数的统计数据。

我正在考虑使用循环链接列表并为每个事件附加成功或失败节点。然后计算列表中的故障节点数与总节点数,但这有两个主要缺点:

  1. 我需要不断扩大/缩小列表大小以考虑“最后X秒”要求(每秒事件数量可以更改)
  2. 我需要经常循环遍历列表并计算所有事件(可能很昂贵,因为我每秒可能会有100个这样的事件)
  3. 有没有人知道从最近X秒收到的样本列表中计算平均值的另一种方法?

6 个答案:

答案 0 :(得分:7)

您应该使用采样频率(a-la MRTG)。假设您只需要一秒精度并保持过去一分钟的平均值,您将拥有一个固定的表,其中包含过去60秒(包括当前的60秒)的60个条目。并保持目前的全球进入。

每个条目都包含一个平均值和一些事件。两个值的每个条目都从0开始。

当您收到新事件时,您可以更改当前和全局条目:

average = ((number * average) + 1) / (number + 1)
number = number + 1

在每个采样间隔,您使用最旧的条目更改全局条目:

global.average = ((global.number * global.average) - (oldest.number * oldest.average)) / (global.number - oldest.number)
global.number = global.number - oldest.number

然后将最旧的条目重置为0并开始将其用作当前条目。

答案 1 :(得分:5)

您可以使用queue,这将允许您将新事件添加到队列的末尾,并从队列的开头删除过期事件,假设事件是按时间顺序添加的。例如,在Java中,您可以使用LinkedListArrayDeque,它们都实现Queue接口。

如果未按时间顺序添加事件,则可以使用priority queue。元素将按其时间戳排序,并且最高优先级元素(即,要删除的下一个元素)将是具有最小时间戳的元素。在Java中,此数据结构由PriorityQueue提供。

我们可以保留两个计数器,一个用于事件总数,另一个用于成功事件的数量,而不是定期计算事件。每当我们从队列中添加或删除事件时,这些计数器都会更新。

答案 2 :(得分:1)

将事件保存在Queue中。只需追加到最后并删除前面太旧的所有事件。这至少可以消除问题1。

答案 3 :(得分:1)

通常对于这些类型的采样器,您通常会指定一个额外的东西,那就是采样器分辨率

在您的情况下,假设您的描述,采样器分辨率可以是1秒或1滴。

如果你想要的采样器的分辨率是1秒,那么这里的算法命题可能就足够了。

  • 创建链接列表。列表节点包含[timestamp,Success count,Failure count,previousNode]
  • 将对列表最后一个节点的引用存储为lastNode,对第一个节点firstNode的引用(lastNode将是列表的尾部,{{1是最新添加的节点,头部)
  • 保留两个全局变量gSuccess,gFail,即最后X秒时间范围内成功与失败的总和。

收到新活动时:

  • 将事件[timestamp]与firstNode firstNode

    进行比较

    IF(eventTimestamp.TotalSeconds> firstNode.TotalSeconds)

    • 在列表的开头添加一个新节点a(在firsNode之前插入),其中Succes和Failure计数为0.
    • firstNode.Previous = newNode
    • firsNode = newNode;

    END IF

    • 将firstNode.Success或firstNode.Failure计数增加1
    • *将gSuccess或gFail增加1

    (每次加入活动后) REMOVE_EXPIRED_NODES

  • WHILE(lastNode!= nil AND curentTime.TotalSeconds - lastNode.TotalSeconds> X)

    • gSuccess - = lastNode.Succes(按要删除的节点减少gSuccess成功次数)
    • gFail - = lastNode.Fail(按要删除的节点减少gFail失败计数)
    • 删除lastNode

    结束时

获取gFail和gSuccess时应始终以REMOVE_EXPIRED_NODES开头。

这种方法的优点:

  • 不会从所有事件重新计算失败和成功的全局计数器,而是添加逐渐递增的事件,并在删除列表中早于X秒的节点时递减。

  • 它使用1秒的采样器分辨率而不是存储所有事件的列表(可能是每秒数百个事件,确保每秒执行总共2个列表操作(添加+删除操作))< / p>

  • 无论事件数量多少,平均每秒列表操作次数为2次(1次添加操作,1次删除操作)

答案 4 :(得分:1)

您的要求有多具体?如果你允许在盒子外思考一点,一个简单的盖革计数器算法,即无限脉冲响应(IIR)数字滤波器计算移动的“平均值”(取决于你如何定义“平均值”),有一个最小的内存占用,只需几行代码。

答案 5 :(得分:0)

维护两个单独的列表会更有效,一个用于成功,一个用于失败。新条目总是附加在列表的末尾(即按增加的时间戳排序)。

现在,当您想要在最后n秒获得成功/失败的数量时,您可以为now() - n创建时间戳并处理列​​表。一旦找到大于此值的时间戳,就可以消除当前时间之前的所有元素。列表的长度为您提供成功或失败的数字。

如果需要优化,请查看通过减少时间戳(即预先设置新值)对列表进行排序更有效,并查看列表,直到找到时间戳小于比较值的元素。丢弃此以及所有以下成员。

事先很难说哪种情况会更有效,所以你必须尝试一下。 OTOH如果运作良好,没有理由进行优化; - )。