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

C 数组中最后n个整数的运行和,c,algorithm,C,Algorithm,让我们假设一个进程每60秒接收一个新的整数。我想保留最后5个数字的总数。例如: 3 1 99 10 8 0 7 9 --> running total is 10+8+0+7+9==34 <---------> 3 1 99 10 8 0 7 9-->运行总数为10+8+0+7+9==34 六十秒后,我们收到一个新的整数。接收到的整数列表现在如下所示: 3 1 99 10 8 0 7 9 2 --> running total is now 8+0+7+

让我们假设一个进程每60秒接收一个新的整数。我想保留最后5个数字的总数。例如:

3 1 99 10 8 0 7 9 --> running total is 10+8+0+7+9==34
       <--------->
3 1 99 10 8 0 7 9-->运行总数为10+8+0+7+9==34
六十秒后,我们收到一个新的整数。接收到的整数列表现在如下所示:

3 1 99 10 8 0 7 9 2 --> running total is now 8+0+7+9+2==26
          <-------->
3 1 99 10 8 0 7 9
//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;
            }
        }
    }
}
3 1 99 10 8 0 7 9 2-->运行总数现在为8+0+7+9+2==26

如果您有存储空间来保存最后5个整数,那么很容易实现这一点。我正试图想出一种比这更节省内存的算法。有人有什么想法吗?

因为你可以重构最后n个数字,例如,如果你输入n个零,你所做的任何事情都相当于存储最后n个数字


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

我认为您无法解决上述问题

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

给定第一个整数:

a1

最后两个索引i和j的运行和si,j可以在整数a2等进入流时迭代计算,重用之前的运行和:

s1,2=a1+a2

s2,3=s1,2-a1+a3

s3,4=s2,3-(s1,2-a1)+a4

s4,5=s3,4-(s2,3-(s1,2-a1))+a5

等等,以递归的方式

正如您所看到的,两个整数的运行和至少需要a1和运行和si-2,i-1来重构下一个到最后一个元素

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

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

a1、a2

最后三个索引i、j和k的运行和si、j、k可以迭代计算为进入流的整数a3等,重用之前的运行和:

s1,2,3=a1+a2+a3

s2,3,4=s1,2,3-a1+a4

s3,4,5=s2,3,4-a2+a5

s4,5,6=s3,4,5-(s1,2,3-a1-a2)+a5


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

如果您的输入有一些限制,可能有一些方法可以做到这一点

char
需要1个字节。给定输入示例,如果整数为正值且长度小于三位数,即0到99之间,则可以通过将整数减少为由分隔符分割的
char
流来节省一些空间

给定如下数字流的尾随和:

3 1 99 10 8 0 7 9 2 --> running total is now 8+0+7+9+2==26
          <-------->
3 1 99 10 8 0 7 9
//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;
            }
        }
    }
}
也许这可以简化为存储两个元素:最后五个元素作为持续的
realloc
-ed
char*
和作为
int
的总和:

"10|8|0|7|9" (10 bytes)
34 (4 bytes)
这总共需要14个字节,比存储五个
int
值所需的20个字节少6个字节

您需要编写代码来标记和提取
char*
中的元素,以重新计算总和,并且您需要
realloc
并在新元素加入和缓冲区长度更改时重写字符缓冲区,以便始终最大限度地节省潜在的空间

另请注意,
char*
上缺少空终止符-您不希望将其视为字符串,以最大限度地提高存储效率。空值是浪费的字节

您还需要仔细地重写
char*
,这样就不必在中间存储上浪费空间。对于一个非常大的
char*
,您可能会在一个四字节的
size\u t
上浪费空间来记录流真正开始的偏移量,这样您就不会浪费时间重写它,并在一个四字节的
size\u t
值上浪费空间,这样您就可以知道何时结束并需要换行(或者在NULL上浪费一个字节,并测试该值)

一个包含五个一位或两位整数、四个分隔符且没有空值的流最多需要16个字节,最多需要9个字节。存储为
int
的累积和需要4个字节。最坏的情况是,您使用的存储与五个
int
变量相同。在最好的情况下,您使用的是13个字节,比最坏的情况少7个字节

假设不是所有的整数都是两位数,您可能会看到一些空间节省。然而,给定一个从0到99的均匀随机整数流,你会期望其中90%的随机数是两位数。因此,平均而言,在大多数情况下,这可能会使用接近20个字节

如果你真的想成为一个小气鬼,请将累积总和存储为三字节
char*
。最大总和(给定相同的约束)将为99+99+99+99+99=495。值
“495”
可以存储在三个字节中。这是一个额外的节省字节

请注意,这并没有考虑操作系统的字长和其他可能填充数据结构的优化等。因此,这个非常简单且受限的示例最终可能不会真正节省预期的空间

如果您处理的是非常大的流,考虑类似的方法,使用块级压缩算法,如BZIP2或GZIP。根据数据的规模,您可能会获得比压缩开销更多的存储节省。您可能希望避免使用需要提取整个流以仅恢复第一个整数的编码方案。

我认为不可能