【问题标题】:Computing standard deviation over a circular buffer计算循环缓冲区的标准差
【发布时间】:2011-10-03 03:52:10
【问题描述】:

我需要计算标准差的值被存储在一个循环缓冲区中。最终算法将在资源受限的设备上运行,因此我希望它尽可能轻巧。幼稚的方法是每次推入新值时重新评估整个缓冲区的标准偏差,但这会非常慢。理想情况下,我想要一种算法,当新值被推入时,动态更新标准偏差的当前值。

Wikipedia reports some techniques for rapid calculation,但它们可以用在流上:在我的例子中,当一个新值被推入时,标准偏差应该被计算为好像最后一个被弹出的值不存在一样。

tl;dr:如何以最少的计算量计算循环缓冲区的标准偏差?

【问题讨论】:

    标签: algorithm math language-agnostic statistics


    【解决方案1】:

    标准差可以表示为:

     stddev = sqrt(1/N * SUM(x[i]^2) - 1/N^2 * SUM(x[i])^2)
    

    对于未校正的样本标准差。对于校正后的样本标准差,可以这样写:

     stddev = sqrt(1/(N-1) * SUM(x[i]^2) - 1/(N^2-N) * SUM(x[i])^2)
    

    如果您维护两个累加器,一个用于SUM(x[i]^2),一个用于SUM(x[i]),那么对于每个新值更新这些累加器是微不足道的(减去最旧值的影响,并添加最新值的影响)。 N 当然是缓冲区的长度。

    当然,如果您在浮点中执行此操作,那么您可能会遇到舍入错误(通常是(x + y) - x != y)。我不认为有任何简单的解决方法。

    【讨论】:

    • 有修复。可以使用Kahan summation 来缓解问题。这将消除由于删除一个元素而导致的舍入误差。还有其他方法需要更多存储空间。
    • @Alexandre:我的意思是真正意义上的修复。 Kahan summataion 减少了问题,但并没有消除它。如果您知道零错误修复,我会真的有兴趣阅读它们。
    • 如果 Kahan 不够,还有 Shewchuk 算法。但要注意空间要求,因为您对资源有限制。 Kahan 对于每个总和只需要一个额外的变量。
    • 有(但 O(n^2) 时间和 n 空间)! This question 地址 Shewchuk 的算法。这个想法是存储所有的部分和(Kahan 只存储最后的部分和)。但它需要n 额外空间。 Kahan 是一个很好的权衡恕我直言,我在这里全心全意地推荐它。
    • 无论如何,code.activestate.com/recipes/… 是一本很棒的书。对于 n^2 运行时,我可能错了,这应该值得一试。无论如何,这个问题很有趣。
    【解决方案2】:

    最简单的做法是反转 Knuth 的公式(来自Wikipedia article 的方差计算:

    Mk-1 = Mk - (xk - Mk)/(k- 1)

    Sk-1 = Sk - (xk - Mk-1)(x k - Mk)

    但是,请注意浮点错误会在整个运行过程中累积!这意味着您的均值和方差数会趋于漂移,并且这种方法不能真正用作准确的在线方法。

    每个样本需要 O(log N) 次操作(其中 N 是队列中的元素数)的稳定在线方法是使用统计项的二元合并树,使用维基百科文章中“并行合并”的公式,如下(C++ 形式):

    struct Statistic {
      int k;
      Element M;
      Element S;
    
      Statistic(Element x)
        : k(1)
        , M(x)
        , S(0)
      {}
      Statistic(Statistic a, Statistic b)
        : k(a.k + b.k)
        , M(a.M*a.k + b.M*b.k)/float(k)
        , S(a.S + b.S + (a.M-b.M)*(a.M-b.M)*(a.k*b.k/float(k)))
      {}
    };
    

    对于稳定的 O(log N) 在线算法,保持上述统计的平衡二叉树,叶子代表单个元素;根将产生所需的在线统计信息。当您更新元素时​​(以旋转缓冲区样式),将需要 O(log N) 操作将每个更新从叶传播到根。

    【讨论】:

    【解决方案3】:

    为集合保留三个数字:值的计数、值的总和以及值的平方和。为方便起见,我们将它们称为 k、sn 和 sn2。

    如果,正如我认为您所暗示的那样,新值总是替换循环队列中的旧值,那么计数可能是恒定的。或者,也许队列可能不够满。无论哪种方式:

    每次给队列加一个值,计数加一,这个值加到总和上,再把这个值的平方加到平方和上。也就是说,如果添加一个新值“n”,则 k=k+1、sn=sn+n、sn2=sn2+n^2。

    每次从队列中删除一个值时,从计数中减去一个,从总和中减去这个值,然后从平方和中减去这个值的平方。也就是说,如果你删除一个值“n”,那么 k=k-1、sn=sn-n 和 sn2=sn2-n^2。

    您可以轻松地在每次更改后重新计算标准偏差,而无需重新计算所有内容。

    请注意,这意味着您必须能够“捕获”一个值,然后才能真正删除它。

    注意:我怀疑我的袖珍计算器就是这样做的,因为它具有转储 sum(n) 和 sum(n^2) 的功能,而且我可以像我一样“删除”一个我从未添加过的值说将 2 和 4 添加到集合中,然后删除 3,它说没关系。所以我不认为它保留了一个列表:它必须只保留计数和总和。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-09-01
      • 2013-02-07
      • 1970-01-01
      • 1970-01-01
      • 2013-09-26
      • 1970-01-01
      • 1970-01-01
      • 2010-10-01
      相关资源
      最近更新 更多