运行数组中最后n个整数的总和

And*_*ndy 9 c algorithm

假设一个进程每60秒收到一个新的整数.我想保留最后5个数字的总计.例如:

3 1 99 10 8 0 7 9 --> running total is 10+8+0+7+9==34
       <--------->
Run Code Online (Sandbox Code Playgroud)

六十秒后,我们收到一个新的整数.收到的整数列表现在看起来像这样:

3 1 99 10 8 0 7 9 2 --> running total is now 8+0+7+9+2==26
          <-------->
Run Code Online (Sandbox Code Playgroud)

如果你有存储空间来保存最后5个整数,这很容易实现.我正在尝试提出一种比这更有效的内存算法.有人有什么想法吗?

Hen*_*nry 22

由于您可以重建最后n个数字,例如,如果您输入n个零,则您执行的任何操作都相当于存储最后n个数字.

假设数字可以是真正随机的并且每个数字是b位长,则任何正确的算法因此可以精确地再现nb随机位.这需要至少nb位存储.


Ale*_*lds 10

我不认为你能解决这个问题.

对于最近两个最近整数的运行总和,必须至少存储第一个整数和当前运行总和,以重建第二个(或最后一个)整数.这意味着存储两个整数.

给出第一个整数:

一个1

最后两个索引ij的运行总和s i,j可以迭代计算为整数a 2,依此类推进入流中,重用前一个运行总和:

s 1,2 = a 1 + a 2

s 2,3 = s 1,2 - a 1 + a 3

s 3,4 = s 2,3 - (s 1,2 - a 1)+ a 4

s 4,5 = s 3,4 - (s 2,3 - (s 1,2 - a 1))+ a 5

...

等等,以递归的方式.

如您所见,两个整数的运行总和至少需要1和运行总和s i-2,i-1来重建倒数第二个元素.

同样,对于最近三个最近整数的运行总和,您必须至少存储前两个整数和当前运行总和,以重建第三个(或倒数第二个)整数.

给定第一个和第二个整数:

a 1,a 2

最后三个索引i,jk的运行和s i,j,k可以迭代计算为整数a 3等进入流,重新使用先前运行的和:

s 1,2,3 = a 1 + a 2 + a 3

s 2,3,4 = s 1,2,3 - a 1 + a 4

s 3,4,5 = s 2,3,4 - a 2 + a 5

s 4,5,6 = s 3,4,5 - (s 1,2,3 - a 1 - a 2)+ a 5

...

同样,您必须为运行总和存储尽可能多的整数,以便重建缺少的整数.通过归纳,如果您要消除任何一个变量,您将无法概括缺失的值.


Art*_*ius 9

为了讨论,我正在简化这个问题.在实践中,将有8000个左右的这样的列表,我将需要保留最后5,60和3600个元素的运行总和.

听起来你想要在过去5秒,60秒和1小时内的总数.

你真的需要你的60秒总计准确到秒吗?或者可以每5秒更新一次?同样,您是否需要每小时总计精确到秒,或者每分钟更新一次是否正常?

如果您不需要每分钟和每小时的总计精确到秒,那么您可以节省很多存储空间.在这种情况下,5 + 12 + 60 = 77,而不是3600.

然后算法运行如下:

//these are the running totals that will be displayed
int last1 = 0;    //updated every second
int last5 = 0;    //updated every second
int last60 = 0;   //updated every 5 seconds
int last3600 = 0; //updated every minute

// 3 circular buffers:
// last 5 1-second periods (updated every second)
int period1[5] = {0};
// last 12 5-second periods (updated every 5 seconds)
int period5[12] = {0};
// last 60 1-minute periods (updated every minute)
int period60[60] = {0};

//indexes for the circular buffers
int index1 = 0;
int index5 = 0;
int index60 = 0;
while (1) {
    printf("1s 5s 1m 1h\n");
    printf("%2d %2d %2d %2d\n", last1, last5, last60, last3600);

    sleep(1);
    last1 = getNewValue();

    //update last5 by subtracting the expiring period and adding the new one
    last5 -= period1[index1];
    last5 += last1;
    //and save the new period to circular buffer
    period1[index1] = last1;
    index1++;

    //if we get to the end of the circular buffer we must go to the start
    //we have also completed a 5s period so we can update last60
    if (index1 >= 5) {
        index1 = 0;

        //similar to before
        last60 -= period5[index5];
        last60 += last5;

        period5[index5] = last5;
        index5++

        //similar to above, but now we have completed a 60s period
        //so we can update last3600
        if (index5 >= 12) {
            index5 = 0;

            //similar to before
            last3600 -= period60[index60];
            last3600 += last60;

            period60[index60] = last60;
            index60++

            if (index60 >= 60) {
                index60 = 0;
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,只需要84个整数,并且不会进行循环,因此性能会很好.

如果您希望每秒更新60秒,而不是每5秒更新一次,则可以执行此操作.你也可以变得更加繁琐,例如每20秒更新1小时.但是,代码如此简洁的部分原因是每次完成每个句点时都会更新.

请注意,3600秒的总计是使用最多内存的那个,所以这是你需要最小心的.


Nic*_*rey 6

我不相信你能做到这一点.你需要一个能够容纳最后n值的滑动窗口.

关于你可以做的最好的事情是使用模运算来将数组视为循环缓冲区,保持运行总和并随着计数进行计数,以避免迭代整个缓冲区来计算值的总和.像这样的东西:

#include <stdlib.h>
#include <string.h>
#include <stdio.h>

#define WINDOW_SIZE 5

static int   *window      ;
static int    i           ;
static double sum         ;
static double cnt         ;

double record_value( int value )
{
  double mean ;

  i          = (i+1) % WINDOW_SIZE ;
  sum        = sum - window[i] + value ;
  cnt       += cnt < WINDOW_SIZE ? 1 : 0 ;
  window[i]  = value ;

  mean = sum/cnt ;
  return mean ;
}

void log_message( double avg )
{
  int x = 0 ;

  printf( "%f = ( " , avg ) ;
  for ( int x = 0 ; x < cnt ; ++x )
  {
    printf( "%s%d" , x > 0 ? " + " : "" , window[x] ) ;
  }
  printf( " ) / %d\r\n" , (int)cnt ) ;
  return ;
}

int main( int argc, char* argv[] )
{
  int j ;

  window      = calloc( WINDOW_SIZE , sizeof(window[0]) ) ;
  i           = WINDOW_SIZE - 1 ;
  sum         = 0 ;
  cnt         = 0 ;

  for ( j = 0 ; j < 100 ; ++j )
  {
    int    v   = rand() ;
    double avg = record_value( v ) ;

    log_message( avg ) ;

  }

  return 0 ;
}
Run Code Online (Sandbox Code Playgroud)