Java 计算标准差的在线算法

Java 计算标准差的在线算法,java,algorithm,statistics,standard-deviation,online-algorithm,Java,Algorithm,Statistics,Standard Deviation,Online Algorithm,通常,我有一个更技术性的问题,但我会用一个数球的例子来简化它 假设我有不同颜色的球和为每种颜色保留的一个数组索引(初始化为所有0)。每次我拾取一个球,我都会将相应的索引增加1 球是随机挑选的,我一次只能挑选一个球。我唯一的目的是计算每种颜色的球数,直到球用完为止 我想计算不同颜色的球的数量的标准偏差,而我正在计算它们。我不想在计算完所有的球之后,通过再次迭代数组来计算它 要可视化: 按随机顺序排列的球:bbgryybgggggb(每个字母代表一种颜色的第一个字母) 从0到3的数组索引分别对应于颜

通常,我有一个更技术性的问题,但我会用一个数球的例子来简化它

假设我有不同颜色的球和为每种颜色保留的一个数组索引(初始化为所有0)。每次我拾取一个球,我都会将相应的索引增加1

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

我想计算不同颜色的球的数量的标准偏差,而我正在计算它们。我不想在计算完所有的球之后,通过再次迭代数组来计算它

要可视化:

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

在得到最终数组后,计算标准偏差非常简单,但我想在填充此数组时进行计算

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


实现这一目标最有效的方法是什么?或者,在得到最终的数组之前,是否有办法做到这一点?

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

只需记录点数、总和和平方和。您可以随时计算平均值和标准偏差,而无需保留数组

如果我了解您的需求,您将需要一个地图,其中颜色是关键,统计信息的实例是值

这是一个为你做这件事的课程

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);
    }

}

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

平方和是一个棘手的部分,因为每个输入增加一个频率。处理这种情况的一种方法是保持迄今为止看到的每种颜色的计数(使用适当的数据结构)。然后,当您在输入中看到一种颜色时,您可以减去它以前的方块,然后将新的方块添加回(或者将两个方块的差值等效地添加到累加器中)


我将让读者来实现这里描述的算法。

这被称为an。我不太明白标准偏差对颜色意味着什么。绿色与红色的距离比蓝色的距离远吗?这个意思不相关,我只是举个例子来简化我的情况。@meriton我相信OP是指每种颜色频率的stddev。正确,我正在计算每种颜色的球数。在不知道每种颜色有多少个球的情况下,我如何跟踪总平方和?您需要一个数组来跟踪每种颜色的计数。但是,您不需要从头开始重新计算平均值和STDEV。因为你知道当前球的颜色,你可以减去它之前的方块,然后加上新的方块。这很聪明,谢谢代码大师。我还认为我需要数组,这就是我在问题中使用它的原因。听起来你需要一个映射,其中键是颜色,值是我现在为你编写的类的实例。再给我几分钟。@duffymo我认为你的答案不完整。问题的一部分是OP需要在读取下一个输入时更改上一个值。两个答案都很好,但我将选择这一个,因为您在另一个答案中给出了有用的注释。因为另一个答案实际上说明了如何执行此操作,我认为您做出了错误的选择。我同意。这与其说是有用的答案,不如说是同义反复。@duffymo,我真的很感谢你的努力,我相信这将在将来帮助其他人,但这是最适合我的情况的答案,也是最简单的答案,我可以将其放入我的代码中,所以这是我个人的选择。谢谢大家尊重这一点。JonathanB,我真的没想到代码会被复制粘贴,所以这个答案(连同他在duffymo的答案中的评论)仍然非常清楚地解释了我该怎么做。不用担心。我很高兴这样做。我再说一遍——我的比他的早四分钟。你没有读我的。