计算标准差的在线算法
Posted
技术标签:
【中文标题】计算标准差的在线算法【英文标题】:Online algorithm for calculating standard deviation 【发布时间】:2012-08-12 07:13:30 【问题描述】:通常,我有一个技术性问题,但我会用一个数球的例子为你简化它。
假设我有不同颜色的球和为每种颜色保留的数组的一个索引(初始化为全 0)。每次我拿起一个球,我都会将相应的索引增加 1。
球是随机挑选的,我一次只能挑选一个球。我唯一的目的是计算每种颜色的球数,直到我用完球。
我想计算不同颜色的球的数量的标准偏差,在我数它们的时候。在计算完所有球后,我不想通过再次遍历数组来计算它。
可视化:
随机排列的小球:BBGRRYYBBGGGGGGB
(每个字母代表一种颜色的第一个字母)
从 0 到 3 的数组索引分别对应于颜色 B、G、R 和 Y。
当我完成挑选球后,我的数组看起来像[5,7,2,2]
。
在获得最终数组后计算标准偏差非常简单,但我想在填充此数组时进行计算。
我想用 Java 做,我有大约 1000 种颜色。
实现它的最有效方法是什么?或者在得到最终数组之前有没有办法做到这一点?
【问题讨论】:
这称为en.wikipedia.org/wiki/Online_algorithm。 我不太明白标准差对颜色意味着什么。绿色离红色比离蓝色更远吗? 意义不相关,我只是举个例子来简化我的情况。 @meriton 我相信 OP 表示每种颜色的 频率 的标准差。 正确,我正在数每种颜色的球。 【参考方案1】:由于使用总和计算平均值和标准差,您可以轻松地为这些实现适当的累加器。然后,当您需要实际值时,完成其余的计算(尤其是除法)。
平方和是棘手的部分,因为您要为每个输入增加一个频率。解决这个问题的一种方法是保持迄今为止看到的每种颜色的计数(使用适当的数据结构)。然后,当您在输入中看到一种颜色时,您可以减去它之前的平方并重新添加新的平方(或等效地将两个平方的差添加到您的累加器中)。
我将把它留给读者来实现这里描述的算法。
【讨论】:
“减去之前的正方形,然后重新添加新的正方形”。所以你做-=n*n; += (n+1)*(n+1)
。效率有点低,你可以改用+= 2*n+1
。
@MSalters 阅读:“或等效地......”【参考方案2】:
您不需要数组来计算标准差。
只需跟踪点数、总和和总平方和。您可以随时计算均值和标准差,而无需保留数组。
如果我了解您的要求,您将需要一个 Map,其中颜色是键,Statistics 实例是值。
这是一个为你做这件事的课程。
package statistics;
/**
* Statistics
* @author Michael
* @link http://***.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);
【讨论】:
如何在不知道每种颜色有多少个球的情况下跟踪总平方和? 您需要一个数组来跟踪每种颜色的计数。但是,您不需要从头开始重新计算 avg 和 stddev。由于您知道当前球的颜色,您可以减去它的前一个方块,然后添加新的方块。 @duffymo 我不认为你的答案是完整的。部分问题是 OP 需要在读取下一个输入时更改以前的值。 @Code-Guru,这完全正确。当我计算标准偏差时,我没有准备好数组。这是我的问题的核心,这个答案没有解决这个问题。以上是关于计算标准差的在线算法的主要内容,如果未能解决你的问题,请参考以下文章