java实现样本方差的计算

来源:互联网 发布:网络商品交易监测系统 编辑:程序博客网 时间:2024/04/30 13:41

转载自:http://my.oschina.net/BreathL/blog/41063

在一些统计或者排序的算法中,常常要用到样本方差这个东西,来判断一组数据的离散程度。

这是样本方差的公式

s^2=\frac{\sum(X_i-\bar X)^2}{N-1}

      然而,在计算机编程中,往往需要计算运行方差(running variance),因为样本的个数总是的在不断变化的,确切将是不断递增;如果每次增加,都要重新计算平均值,再按次公式,计算出方差;虽可以实现,但计算量会随着数据的增长变的太大。

      因此,递推的公式就显得格外重要;通过n-1个样本时的方差值,和新增的样本,就能得到此时这N个样本的方差;这样计算量不会变同时保持在一个很小的值,可大大提高程序的计算效率。递推公式如下:

      Mn = Mn-1+ (xn - Mn-1)/n 

      Sn = Sn-1 + (xn - Mn-1)*(xn - Mn)

      Mn为平均值,初始时: M1 = x1,  S1 = 0 (此等式的推导证明,我后面给出),而样本方差 s =Sn/(n - 1)

      下面是我自己给出的简单实现(若有更好的实现,请不吝赐教)

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
packagecom.mycode.math;
 
publicfinal class RunningVariance {
    privateint count;// 样本的个数
    privatedouble mk;// 平均值
    privatedouble sk;// Sn
    privatedouble runVar;// 样本方差
 
    publicRunningVariance() {
        this(0,0.0,0.0);
    }
 
    publicRunningVariance(intcount, doublemk, doublesk) {
        this.count = count;
        this.mk = mk;
        this.sk = sk;
        recomputeRunVar();
    }
 
    publicdouble getMk() {
        returnmk;
    }
 
    publicdouble getSk() {
        returnsk;
    }
 
    /**
     * 获取运行时样本方差
     *
     * @return
     */
    publicsynchronized double getRunningVariance() {
        returnrunVar;
    }
 
    /**
     * 增加样本
     *
     * @param sample
     */
    publicsynchronized void addSample(doublesample) {
        if(++count == 1) {
            mk = sample;
            sk = 0.0;
        }else{
            doubleoldmk = mk;
            doublediff = sample - oldmk;
            mk += diff / count;
            sk += diff * (sample - mk);
        }
        recomputeRunVar();
    }
 
    /**
     * 移除样本
     *
     * @param sample
     */
    publicsynchronized void removeSample(doublesample) {
        intoldCount = getCount();
        doubleoldmk = mk;
        if(oldCount == 0) {
            thrownew IllegalStateException();
        }
        if(--count == 0) {
            mk = Double.NaN;
            sk = Double.NaN;
        }else{
            mk = (oldCount * oldmk - sample) / (oldCount - 1);
            sk -= (sample - mk) * (sample - oldmk);
        }
        recomputeRunVar();
    }
 
    privatesynchronized void recomputeRunVar() {
        intcount = getCount();
        runVar = count > 1? sk / (count - 1) : Double.NaN;
        // 若需要计算标准差
        // runVar = count > 1 ? Math.sqrt(sk / (count - 1)) : Double.NaN;
    }
 
    publicsynchronized int getCount() {
        returncount;
    }
}

         对于递推公式  Sn = Sn-1 + (xn - Mn-1)*(xn - Mn),我自己做了个简单的推导证明,如下图:

        另:图中所提的 等式1  即是 平均数的递推公式:Mn = Mn-1+ (xn - Mn-1)/n 

0 0