При хранении массива в стеке с новой функцией C99, float x[n]
, опасайтесь переполнения стека, когда n
велико. См. Как объявлять и использовать огромные массивы из 1 миллиарда целых чисел в C? и https://wiki.sei.cmu.edu/confluence/display/c/MEM05-C.+Avoid+large+stack+allocations.
Если RAND_MAX
равно 2 147 483 647, x = rand()
возвращает [квази] равномерное распределение, E[x] = 1,073,741,823
. Предполагая, что IEEE 754
32-бит float
, можно точно хранить только 16,777,217
целых чисел, см. Какое первое целое число, которое с плавающей точкой IEEE 754 не в состоянии точно представить? . При добавлении n
реплик точность падает до 1/n
. Когда n
велико, это тоже важно. В физических лабораториях нам всегда напоминали, что почти всегда уместно использовать double
для манипулирования измерениями, это объясняет, почему это важно (для времени, но и в целом) https://randomascii.wordpress.com/2012/02/13/dont-store-that-in-a-float/.
Этот код представляет собой реализацию алгоритма Уэлфорда Онлайн как https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Welford%27s_Online_algorithm,, за исключением C. Преимущество этого заключается в том, что вам не нужно знать количество элементов и вам не нужно хранить элементы , (он-лайн и память O(1)
); Кроме того, он численно более стабилен, чем сложение потенциально большой суммы.
#include <stdlib.h> /* EXIT_ size_t */
#include <stdio.h> /* printf */
#include <math.h> /* sqrt */
/** Measurement. C version of Python
\url{ https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Welford's_Online_algorithm }. */
struct Mx {
size_t count;
double mean, ssdm;
};
static void mx_reset(struct Mx *const measure) {
if(!measure) return;
measure->count = 0;
measure->mean = 0;
measure->ssdm = 0;
}
static void mx_add(struct Mx *const measure, const double replica) {
size_t n;
double delta;
if(!measure) return;
n = ++measure->count;
delta = replica - measure->mean;
measure->mean += delta / n;
measure->ssdm += delta * (replica - measure->mean);
}
static double mx_mean(const struct Mx *const measure) {
if(!measure || !measure->count) return NAN;
return measure->mean;
}
static double mx_sample_variance(const struct Mx *const measure) {
if(!measure || measure->count <= 1) return NAN;
return measure->ssdm / (measure->count - 1);
}
static double mx_population_variance(const struct Mx *const measure) {
if(!measure || !measure->count) return NAN;
return measure->ssdm / measure->count;
}
/** This is the example from
\url{ https://en.wikipedia.org/wiki/Standard_deviation }. */
int main(void) {
const float fulmars_f[] = { 727.7f, 1086.5f, 1091.0f, 1361.3f, 1490.5f,
1956.1f }, fulmars_m[] = { 525.8f, 605.7f, 843.3f, 1195.5f, 1945.6f,
2135.6f, 2308.7f, 2950.0f };
const size_t fulmars_f_size = sizeof fulmars_f / sizeof *fulmars_f,
fulmars_m_size = sizeof fulmars_m / sizeof *fulmars_m;
struct Mx f, m;
size_t i;
mx_reset(&f), mx_reset(&m);
/* Converts float -> double. */
for(i = 0; i < fulmars_f_size; i++) mx_add(&f, fulmars_f[i]);
for(i = 0; i < fulmars_m_size; i++) mx_add(&m, fulmars_m[i]);
printf("female breeding Northern fulmars\nmean:\t%f.\nstddev:\t%f\n"
"population stddev: %f\n\nmale breeding Northern fulmars\n"
"mean:\t%f.\nstddev:\t%f\npopulation stddev: %f\n", mx_mean(&f),
sqrt(mx_sample_variance(&f)), sqrt(mx_population_variance(&f)),
mx_mean(&m), sqrt(mx_sample_variance(&m)),
sqrt(mx_population_variance(&m)));
return EXIT_SUCCESS;
}
В этом случае пользовательский интерфейс является константой, но выполнение for(i = 0; i < n; i++) mx_add(rand() % n);
для некоторого вмененного n
отделит ваш пользовательский интерфейс от статистических вычислений.