W odpowiedzi Jasona R jest błąd, który jest omawiany w „Art of Computer Programming” Knutha, t. 2. Problem pojawia się, gdy masz odchylenie standardowe, które stanowi niewielki ułamek średniej: obliczenie E (x ^ 2) - (E (x) ^ 2) cierpi z powodu dużej wrażliwości na błędy zaokrąglania zmiennoprzecinkowego.
Możesz nawet spróbować tego samodzielnie w skrypcie Python:
ofs = 1e9
A = [ofs+x for x in [1,-1,2,3,0,4.02,5]]
A2 = [x*x for x in A]
(sum(A2)/len(A))-(sum(A)/len(A))**2
Otrzymuję -128,0 jako odpowiedź, co oczywiście nie jest poprawne obliczeniowo, ponieważ matematyka przewiduje, że wynik powinien być nieujemny.
Knuth przytacza podejście (nie pamiętam nazwiska wynalazcy) do obliczania średniej bieżącej i odchylenia standardowego, które wygląda mniej więcej tak:
initialize:
m = 0;
S = 0;
n = 0;
for each incoming sample x:
prev_mean = m;
n = n + 1;
m = m + (x-m)/n;
S = S + (x-m)*(x-prev_mean);
a następnie po każdym kroku wartość m
jest średnią, a odchylenie standardowe można obliczyć jako sqrt(S/n)
lub w sqrt(S/n-1)
zależności od ulubionej definicji odchylenia standardowego.
Równanie, które piszę powyżej, jest nieco inne niż w Knuth, ale jest obliczeniowo równoważne.
Kiedy mam jeszcze kilka minut, koduję powyższą formułę w Pythonie i pokażę, że otrzymasz nieujemną odpowiedź (która, mam nadzieję, jest zbliżona do poprawnej wartości).
aktualizacja: oto jest.
test1.py:
import math
def stats(x):
n = 0
S = 0.0
m = 0.0
for x_i in x:
n = n + 1
m_prev = m
m = m + (x_i - m) / n
S = S + (x_i - m) * (x_i - m_prev)
return {'mean': m, 'variance': S/n}
def naive_stats(x):
S1 = sum(x)
n = len(x)
S2 = sum([x_i**2 for x_i in x])
return {'mean': S1/n, 'variance': (S2/n - (S1/n)**2) }
x1 = [1,-1,2,3,0,4.02,5]
x2 = [x+1e9 for x in x1]
print "naive_stats:"
print naive_stats(x1)
print naive_stats(x2)
print "stats:"
print stats(x1)
print stats(x2)
wynik:
naive_stats:
{'variance': 4.0114775510204073, 'mean': 2.0028571428571427}
{'variance': -128.0, 'mean': 1000000002.0028572}
stats:
{'variance': 4.0114775510204073, 'mean': 2.0028571428571431}
{'variance': 4.0114775868357446, 'mean': 1000000002.0028571}
Zauważysz, że nadal występuje błąd zaokrąglania, ale nie jest zły, podczas gdy naive_stats
po prostu wymiotuje.
edycja: Właśnie zauważyłem komentarz Belizariusza cytujący Wikipedię, która wspomina o algorytmie Knuth.