一、問題背景
\quad 給你一個數組x = [ 1 , 2 , 3 , 6 ] x=[1,2,3,6] x = [ 1 , 2 , 3 , 6 ] ,如何快速計算其前綴數組x [ 0 ⋯ n ] x[0\cdots n] x [ 0 ⋯ n ] 的均值和方差,即需要返回均值數組m = [ 1 , 1.5 , 2 , 3 ] m=[1,1.5,2,3] m = [ 1 , 1 . 5 , 2 , 3 ] ,m [ 2 ] = 2 m[2]=2 m [ 2 ] = 2 表示數組x [ 0 ⋯ 2 ] = [ 1 , 2 , 3 ] x[0 \cdots 2]=[1,2,3] x [ 0 ⋯ 2 ] = [ 1 , 2 , 3 ] 的均值爲2;同時返回方差數組S = [ 0 , 0.25 , 2 3 , 3.5 ] ] S=[0, 0.25, \frac{2}{3}, 3.5]] S = [ 0 , 0 . 2 5 , 3 2 , 3 . 5 ] ] ,S [ 2 ] = 2 / 3 S[2]=2/3 S [ 2 ] = 2 / 3 表示數組x [ 0 ⋯ 2 ] = [ 1 , 2 , 3 ] x[0 \cdots 2]=[1,2,3] x [ 0 ⋯ 2 ] = [ 1 , 2 , 3 ] 的方差爲2 3 \frac{2}{3} 3 2 。
\quad 對於這個問題,我們很容易找到O ( n 2 ) O(n^2) O ( n 2 ) 級別的算法暴力計算,那有沒有O ( n ) O(n) O ( n ) 級別的算法呢?
\quad 我們嘗試思考這樣一個問題,假設我求解出了數組前n − 1 n-1 n − 1 項的均值和方差,能否求出一個遞推式子直接算出前n n n 項的均值和方差呢?
二、理論推導
\quad 定義均值數組m m m 和方差乘上當前長度 n n n 的數組S S S :m n = ∑ i = 1 n x i n , S n = ∑ i = 1 n ( x i − m n ) 2 m_n = \frac{\sum_{i=1}^nx_i}{n}, S_n=\sum_{i=1}^n(x_i-m_n)^2 m n = n ∑ i = 1 n x i , S n = i = 1 ∑ n ( x i − m n ) 2
首先容易得到均值的遞推式子:m n = ∑ i = 1 n x i n = ∑ i = 1 n − 1 x i + x n n = n − 1 n m n − 1 + 1 n x n m_n= \frac{\sum_{i=1}^nx_i}{n}= \frac{\sum_{i=1}^{n-1}x_i+x_n}{n}=\frac{n-1}{n}m_{n-1}+\frac{1}{n}x_n m n = n ∑ i = 1 n x i = n ∑ i = 1 n − 1 x i + x n = n n − 1 m n − 1 + n 1 x n
將上述式子代入可以得到x i − m n = x i − ( n − 1 n m n − 1 + 1 n x n ) = x i − m n − 1 − 1 n ( x n − m n − 1 ) x_i-m_n=x_i-(\frac{n-1}{n}m_{n-1}+\frac{1}{n}x_n)=x_i-m_{n-1}-\frac{1}{n}(x_n-m_{n-1}) x i − m n = x i − ( n n − 1 m n − 1 + n 1 x n ) = x i − m n − 1 − n 1 ( x n − m n − 1 ) ,當i = n i=n i = n 時得到x n − m n = n − 1 n ( x n − m n − 1 ) x_n-m_n=\frac{n-1}{n}(x_n-m_{n-1}) x n − m n = n n − 1 ( x n − m n − 1 )
有了這些輔助,接下來我們嘗試推到S S S 的遞推式:
S n = ∑ i = 1 n ( x i − m n ) 2 = ∑ i = 1 n − 1 ( x i − m n ) 2 + ( x n − m n ) 2 = ∑ i = 1 n − 1 ( x i − m n ) 2 + ( n − 1 n ) 2 ( x n − m n − 1 ) 2 = ∑ i = 1 n − 1 [ x i − m n − 1 − 1 n ( x n − m n − 1 ) ] 2 + ( n − 1 n ) 2 ( x n − m n − 1 ) 2 = ∑ i = 1 n − 1 ( x i − m n − 1 ) 2 + [ n − 1 n 2 + ( n − 1 ) 2 n 2 ] ( x n − m n − 1 ) 2 = S n − 1 + n − 1 n ( x n − m n − 1 ) 2 S_n=\sum_{i=1}^n(x_i-m_n)^2 \\
=\sum_{i=1}^{n-1}(x_i-m_n)^2+(x_n-m_n)^2 \\
=\sum_{i=1}^{n-1}(x_i-m_n)^2+(\frac{n-1}{n})^2(x_n-m_{n-1})^2 \\
=\sum_{i=1}^{n-1}[x_i-m_{n-1}-\frac{1}{n}(x_n-m_{n-1})]^2+(\frac{n-1}{n})^2(x_n-m_{n-1})^2 \\
=\sum_{i=1}^{n-1}(x_i-m_{n-1})^2+[\frac{n-1}{n^2}+\frac{(n-1)^2}{n^2}](x_n-m_{n-1})^2 \\
=S_{n-1}+\frac{n-1}{n}(x_n-m_{n-1})^2
S n = i = 1 ∑ n ( x i − m n ) 2 = i = 1 ∑ n − 1 ( x i − m n ) 2 + ( x n − m n ) 2 = i = 1 ∑ n − 1 ( x i − m n ) 2 + ( n n − 1 ) 2 ( x n − m n − 1 ) 2 = i = 1 ∑ n − 1 [ x i − m n − 1 − n 1 ( x n − m n − 1 ) ] 2 + ( n n − 1 ) 2 ( x n − m n − 1 ) 2 = i = 1 ∑ n − 1 ( x i − m n − 1 ) 2 + [ n 2 n − 1 + n 2 ( n − 1 ) 2 ] ( x n − m n − 1 ) 2 = S n − 1 + n n − 1 ( x n − m n − 1 ) 2
至此,我們得到了利用數組前n − 1 n-1 n − 1 項的均值和方差推出前n n n 項的均值和方差的遞推式子,如下:
m n = n − 1 n m n − 1 + 1 n x n S n = S n − 1 + n − 1 n ( x n − m n − 1 ) 2 m_n = \frac{n-1}{n}m_{n-1}+\frac{1}{n}x_n \\
S_n=S_{n-1}+\frac{n-1}{n}(x_n-m_{n-1})^2 m n = n n − 1 m n − 1 + n 1 x n S n = S n − 1 + n n − 1 ( x n − m n − 1 ) 2
三、程序
\quad 這裏給出Python程序求解實例,給出數組x x x ,返回其均值數組m m m 和方差數組S S S 。
def meanAndSquare ( x) :
x = [ 0 ] + x
m = [ 0 for _ in range ( len ( x) ) ]
S = [ 0 for _ in range ( len ( x) ) ]
for i in range ( 1 , len ( x) ) :
m[ i] = ( ( i - 1 ) * m[ i - 1 ] + x[ i] ) / i
S[ i] = S[ i - 1 ] + ( i - 1 ) / i * ( x[ i] - m[ i - 1 ] ) ** 2
for i in range ( 1 , len ( S) ) :
S[ i] /= i
m, S = m[ 1 : ] , S[ 1 : ]
return m, S
if __name__ == '__main__' :
x = [ 1 , 2 , 3 , 6 ]
print ( meanAndSquare( x) )