在线算法计算标准差

时间:2012-08-15 23:22:09

标签: java algorithm statistics standard-deviation online-algorithm

通常情况下,我有一个更技术性的问题,但我会以计算球为例简化它。

假设我有不同颜色的球和为每种颜色保留的阵列的一个索引(初始化为全0)。每当我选择一个球时,我都会将相应的索引增加1。

随机挑选球,我一次只能挑一个球。我唯一的目的是计算每种颜色的球数,直到我用完球。

我想计算不同颜色的球数的标准偏差,,而我正在计算它们。在完成对所有球的计数之后,我不想再次遍历数组来计算它。

要想象:

随机顺序的球:BBGRRYYBBGGGGGGB(每个字母代表一个颜色的第一个字母) 从0到3的数组索引分别对应于颜色B,G,R和Y. 当我完成拾球时,我的阵列看起来像[5,7,2,2]

在得到最终数组之后计算标准偏差非常简单,但我想在填充此数组时这样做。

我想用Java做,我有大约1000种颜色。

实施该方法的最有效方法是什么?或者在掌握最终阵列之前是否有办法做到这一点?

2 个答案:

答案 0 :(得分:7)

您不需要数组来计算标准偏差。

简单地跟踪点数,总和和总平方和。您可以随时计算平均值和标准差,而无需保留数组。

如果我了解您的要求,您将需要一个颜色为关键字的地图,而统计数据的实例就是值。

这是一个为你做的课程。

package statistics;

/**
 * Statistics
 * @author Michael
 * @link http://stackoverflow.com/questions/11978667/online-algorithm-for-calculating-standrd-deviation/11978689#11978689
 * @since 8/15/12 7:34 PM
 */
public class Statistics {

    private int n;
    private double sum;
    private double sumsq;

    public void reset() {
        this.n = 0;
        this.sum = 0.0;
        this.sumsq = 0.0;
    }

    public synchronized void addValue(double x) {
        ++this.n;
        this.sum += x;
        this.sumsq += x*x;
    }

    public synchronized double calculateMean() {
        double mean = 0.0;
        if (this.n > 0) {
            mean = this.sum/this.n;
        }
        return mean;
    }

    public synchronized double calculateVariance() {
       double deviation = calculateStandardDeviation();
        return deviation*deviation;
    }

    public synchronized double calculateStandardDeviation() {
        double deviation = 0.0;
        if (this.n > 1) {
            deviation = Math.sqrt((this.sumsq - this.sum*this.sum/this.n)/(this.n-1));
        }
        return deviation;
    }
}

这是它的单元测试:

package statistics;

import org.junit.Assert;
import org.junit.Test;

/**
 * StatisticsTest
 * @author Michael
 * @link http://www.wolframalpha.com/input/?i=variance%281%2C+2%2C+3%2C+4%2C+5%2C+6%29&a=*C.variance-_*Variance-
 * @since 8/15/12 7:42 PM
 */
public class StatisticsTest {

    private static final double TOLERANCE = 1.0E-9;

    @Test
    public void testCalculateMean() {
        double [] values = new double[] {
            1.0, 2.0, 3.0, 4.0, 5.0, 6.0
        };
        Statistics stats = new Statistics();
        for (double value : values) {
            stats.addValue(value);
        }
        double expected = 3.5;
        Assert.assertEquals(expected, stats.calculateMean(), TOLERANCE);
    }

    @Test
    public void testCalculateVariance() {
        double [] values = new double[] {
                1.0, 2.0, 3.0, 4.0, 5.0, 6.0
        };
        Statistics stats = new Statistics();
        for (double value : values) {
            stats.addValue(value);
        }
        double expected = 3.5;
        Assert.assertEquals(expected, stats.calculateVariance(), TOLERANCE);
    }


    @Test
    public void testCalculateStandardDeviation() {
        double [] values = new double[] {
                1.0, 2.0, 3.0, 4.0, 5.0, 6.0
        };
        Statistics stats = new Statistics();
        for (double value : values) {
            stats.addValue(value);
        }
        double expected = Math.sqrt(3.5);
        Assert.assertEquals(expected, stats.calculateStandardDeviation(), TOLERANCE);
    }

}

答案 1 :(得分:1)

由于平均值和标准差是使用总和计算的,因此您可以轻松地为这些实现适当的累加器。然后,当您想要实际值时,完成剩余的计算(特别是除法)。

由于您为每个输入增加一个频率,因此平方和是棘手的部分。解决此问题的一种方法是保持到目前为止所看到的每种颜色的计数(使用适当的数据结构)。然后,当您在输入中看到颜色时,可以减去其先前的方块并将新方块添加回来(或等效地将两个方块的差异添加到累加器中)。

我会留给读者来实现这里描述的算法。