2012-02-10 52 views
3

内收集的事件总数我面临的一个算法问题。算法在过去一段时间

我们有运行每隔10ms和运行过程中,一个事件可以发生或没有发生的任务。有没有简单的算法可以让我们跟踪事件在最近1秒钟内触发多少次?

是我唯一的想法就是实现一个数组,并保存所有的事件。正如我们在嵌入式系统编程,没有足够的空间...提前

感谢。

+0

您可以将每秒重置的变量存储为0,然后在发生的每个事件中增加一个变量。 – bdares 2012-02-10 10:28:31

+0

@bdares:我认为事件不是二进制文件,它们有属性......可能是错误的,OP应该澄清这一点。 – amit 2012-02-10 10:29:53

+0

感谢您的回答。随着时间窗口的移动,我无法通过这种方式重置计数器。我们希望在最近的时间间隔内跟踪事件总数......无论如何感谢:) – user1201566 2012-02-10 10:32:13

回答

0

你需要某种形式的列表/队列等,但ringbuffer有可能是最好的性能。 您需要存储100个计数器(在最后一秒内每个时间间隔为10毫秒)和一个当前计数器。

Ringbuffer解决方案: (我使用伪代码)。

创建100个计数器的counter_array(最初用0填充的)。

int[100] counter_array; 
current_counter = 0 

在10毫秒周期:

counter_array[current_counter] = 0; 
current_counter++; 

对于每一个事件:

counter_array[current_counter]++ 

要在过去的支票事件的数量,采取counter_array的总和

+0

感谢您的回答。正如我所说的......列表对于项目来说太昂贵了,我们必须每10ms计算一次列表的长度......这也是时间成本。 – user1201566 2012-02-10 10:35:52

+0

对于我来说,如果你的意思是列表中的所有事件信息或只有时间戳,我还不清楚。 – 2012-02-10 10:39:08

+0

既不......我们只需要知道事件在最近1秒内触发了多少次。无论如何,谢谢:) – user1201566 2012-02-10 10:41:50

0

你能买得起100个布尔值的数组吗?也许作为一个领域?只要你能负担得起的成本的空间,就可以跟踪恒定的时间事件的数量:

  1. 商店:
    1. 的计数器C,最初0
    2. 布尔B的阵列的大小等于你想跟踪的间隔数,即100,最初都是假的。
    3. 索引I,最初0
  2. 每个时间间隔:
    1. 阅读在B [i]于布尔,和减量c如果这是真的。
    2. 设置布尔在B [i]于为true,如果发生在这个间隔中的事件,否则为假。
    3. 增量C如果事件发生在这个时间间隔。
  3. 当I达到100时,复位为0。

这样,你至少避免扫描整个阵列的每个时间间隔。


编辑 - 好了,你想在最后3分钟(180秒,18000个区间)来跟踪事件。利用上述算法和填鸭式的布尔成位字段,需要总存储:

2 byte unsigned integer for C 
2 byte unsigned integer for I 
2250 byte bit-field for B 

,如果你需要有事件的数量的精确计数在最后180.0秒在所有这几乎是不可避免的倍。我不认为要证明您需要所有这些信息才能够随时提供准确答案并不困难。但是,如果您只能了解最近180 +/- 2秒内的事件数量,则可以改为缩短时间分辨率。这里有一个详细的例子,展开下面的评论。

上述算法概括:

  1. 商店:
    1. 的计数器C,最初0
    2. 计数器B的阵列,大小等于要跟踪间隔的数量的,即,100,最初所有0
    3. 索引I,最初0
  2. 每个时间间隔:
    1. 阅读B [i],并将C减少该量。
    2. 将发生此间隔的事件数写入B [i]。
    3. 按此间隔发生的事件数量递增C.
  3. 当我到达B的长度,将其重置为0。

如果要切换的时间间隔为2秒,然后在那个时候可能发生0-200事件。所以数组中的每个计数器都可以是一个无符号的单字节整数。在3分钟内你会有90个这样的间隔,所以你的数组需要90个元素= 90个字节。

如果将间隔切换为150ms,那么在此期间可能会发生0-15个事件。如果你按下空格键,你可以把它塞进一个半字节的无符号整数。你会在3分钟内有1200个这样的间隔,所以你的数组需要1200个元素= 600个字节。

+0

谢谢,这个解决方案已经实现了,但是我们买不起1800个布尔值(最新的3分钟):(我会看看我是否可以从代码的其他部分释放更多空间... – user1201566 2012-02-10 11:00:33

+0

你能交易严谨吗?例如,不是每10ms执行一次,你可以每2s执行一次:计算这段时间内的(0-200)事件(这需要2个字节的存储空间,一个用于计数事件,一个用于计数)然后用上述算法使用一个计数器数组 - 你需要180/2 = 90个计数器,那么在任何给定的时间,你的运行计数将在真实总数的+/- 200以内,并且空间需求是〜92字节 – Weeble 2012-02-10 11:13:58

+0

@ user1201566:你不应该在评论中改变问题的答案!其他人仍然会回答原来的问题。如果你想追踪超过三分钟而不是原来所说的一秒钟,那么你应该修改相应的问题,别的我们都在浪费时间!最后虽然“*滑动时间窗口*”需要历史记录,并且历史需要记忆;你无法从无到有。 – Clifford 2012-02-13 17:43:08

2

13个字节的数组,用于10ms步骤中的第二个事件。

考虑它的104位标记0毫秒的阵列,以104MS如果发生事件标记位和增量到下一个时间

,否则只递增到下一个比特/字节。

如果您希望...每秒钟后运行长度编码将事件位卸载到另一个值中。 或......将其视为循环缓冲区,并保持计数可用于查询。 或两者兼有

您可以减小阵列大小以匹配可用空间。

不清楚在任务运行时事件是否会多次发生,或者事件之间总是有10ms。

+1

更多...如果每次用1代替0计数器时递增计数器,并且当1用0代替时递减,则可以保持运行计数而不必明确计数比特 - 这将更快且更确定。这避免了存储历史数据的运行长度编码的任何需要,可以简单地复制计数器。 – Clifford 2012-02-11 11:53:26

+0

好点,我做了一个假设,你将不得不在阵列中保持你在每次通话中的位置。你的代码看起来是“好的”(我缺乏对它的评论),但我会做得有点不同......当然;)。该RLE将用于将信息卸载到其他地方以供历史参考,从而保持更长时间段的周期性和模式。 – Dtyree 2012-02-11 18:51:04

0

请问以下工作适合您吗?

滚动事件计数器,可以增加每个事件。

在每10ms运行一次的例程中,将当前事件计数器值与上次运行例程时存储的事件计数器值进行比较。

这会告诉您在10ms窗口期间发生了多少事件。

2

这是更多或更少什么Dtyree和Weeble所建议的,但是一个示例性实现可以帮助(为了图示的C代码):

#include <stdint.h> 
#include <stdbool.h> 

#define HISTORY_LENGTH 100 // 1 second when called every 10ms 

int rollingcount(bool event) 
{ 
    static uint8_t event_history[(HISTORY_LENGTH+7)/8] ; 
    static int next_history_bit = 0 ; 
    static int event_count = 0 ; 

    // Get history byte index and bit mask 
    int history_index = next_history_bit >> 3 ;    // ">> 3" is same as "/ 8" but often faster 
    uint8_t history_mask = 1 << (next_history_bit & 0x7) ; // "& 0x07" is same as "% 8" but often faster 

    // Get current bit value 
    bool history_bit = (event_history[history_index] & history_mask) != 0 ; 

    // If oldest history event is not the same as new event, adjust count 
    if(history_bit != event) 
    { 
     if(event) 
     { 
      // Increment count for 0->1 
      event_count++ ; 

      // Replace oldest bit with 1 
      event_history[history_index] |= history_mask ; 
     } 
     else 
     { 
      // decrement count for 1->0 
      event_count-- ; 

      // Replace oldest bit with 0 
      event_history[history_index] &= ~history_mask ; 
     } 
    } 

    // increment to oldest history bit 
    next_history_bit++ ; 
    if(next_history_bit >= HISTORY_LENGTH) // Could use "next_history_bit %= HISTORY_COUNT" here, but may be expensive of some processors 
    { 
     next_history_bit = 0 ; 
    } 

    return event_count ; 
} 

对于100样品历史,它需要13个字节加上两个静态分配内存的整数,我已经使用int作为通用性,但在这种情况下,uint8_t计数器就足够了。另外还有三个堆栈变量,如果需要真正优化内存使用,则不需要使用int。所以总共可以使用15个字节加上3个字节的堆栈。 event参数可能会或可能不会传递到堆栈上,然后是函数调用返回地址,但又取决于您的编译器/处理器的调用约定。