Вопрос

На самом деле я не так часто использовал расчет дисперсии и не совсем знаю, чего ожидать.На самом деле я совсем не силен в математике.

У меня есть массив из 1000000 случайных числовых значений в диапазоне 0-10000.

Массив может вырасти еще больше, поэтому для суммы я использую 64-битное целое число.

Я пытался найти код расчета дисперсии, но не знаю, получу ли я правильный результат.

Среднее значение составляет 4692, а медиана — 4533.Я получаю отклонение 1483780.469308, используя следующий код:

// size is the element count, in this case 1000000
// value_sum is __int64

double p2 = pow( (double)(value_sum - (value_sum/size)), (double)2.0 );
double variance = sqrt( (double)(p2 / (size-1)) );

Получаю ли я разумную ценность?

Что-то не так с расчетом?

Это было полезно?

Решение

Примечание: Не похоже, что вы рассчитываете дисперсию.

Дисперсия рассчитывается путем вычитания среднего значения из каждого элемента и расчета взвешенной суммы этих различий.

Итак, что вам нужно сделать, это:

// Get mean
double mean = static_cast<double>(value_sum)/size;

// Calculate variance
double variance = 0;
for(int i = 0;i<size;++i) 
{
  variance += (MyArray[i]-mean)*(MyArray[i]-mean)/size;
}

// Display
cout<<variance;

Обратите внимание, что это выборочная дисперсия, которая используется, когда основное распределение неизвестно (поэтому мы предполагаем равномерное распределение).

Кроме того, после некоторого копания я обнаружил, что это не беспристрастная оценка. вольфрам Альфа есть что сказать по этому поводу, но в качестве примера, когда МАТЛАБ вычисляет дисперсию и возвращает «выборочную дисперсию с поправкой на систематическое отклонение».

Дисперсия с поправкой на смещение может быть получена путем деления каждого элемента на size-1, или:

//Please check that size > 1
variance += (MyArray[i]-mean)*(MyArray[i]-mean)/(size-1); 

Также обратите внимание, что значение mean остается такой же.

Другие советы

Прежде всего, если вы просто хотите понять, что такое «разумная» дисперсия, имейте в виду, что дисперсия, по сути, представляет собой квадрат стандартного отклонения.Стандартное отклонение примерно измеряет типичное расстояние от точки данных до ее ожидаемого значения.

Итак, если среднее значение ваших данных равно 4692, а расчетная дисперсия равна 1483780, это означает, что ваше стандартное отклонение составляет около 1218, что предполагает, что ваши числа, как правило, находятся где-то в диапазоне 3474–5910.Так что эта дисперсия на самом деле кажется мне немного низкой, если диапазон ваших чисел составляет 0–10 000;но это, очевидно, зависит от распределения ваших данных.

Что касается самого расчета:Вы можете рассчитать дисперсию, используя текущий расчет, когда вы читаете данные в первый раз (вам не обязательно заранее знать среднее значение), используя Метод Уэлфорда:

Инициализируйте M1 = x1 и S1 = 0.

Для последующих х используйте повторение формулы

Mk = Mk-1 (xk - Mk-1)/k Sk = Sk-1 (xk - Mk-1)*(xk - Mk).

Для 2 ≤ k ≤ n - kth оценка дисперсия s2 = Sk/(k - 1).

Просто ради интереса, немного другой путь к тому же результату с использованием std::valarray вместо std::vector и (различных) алгоритмов:

template <class T>
T const variance(std::valarray<T> const &v) {
    if (v.size() == 0)
        return T(0.0);
    T average = v.sum() / v.size();
    std::valarray<T> diffs = v-average;
    diffs *= diffs;
    return diffs.sum()/diffs.size();
}

Как намекнул Джейкоб, на самом деле существует две возможные версии расчета дисперсии.В нынешнем виде предполагается, что ваши входные данные являются «вселенной».Если вы взяли только образец всей вселенной, в последней строке следует использовать: (diffs.size()-1) вместо diffs.size().

Может быть, использовать другую формулу?

#include <functional>
#include <algorithm>
#include <iostream>
int main()
{
 using namespace std;

 vector<double> num( 3 );
 num[ 0 ] = 4000.9, num[ 1 ] = 11111.221, num[ 2 ] = -2;


 double mean = std::accumulate(num.begin(), num.end(), 0.0) / num.size();
 vector<double> diff(num.size());
 std::transform(num.begin(), num.end(), diff.begin(), 
                std::bind2nd(std::minus<double>(), mean));
 double variance = std::inner_product(diff.begin(), diff.end(), 
                                     diff.begin(), 0.0) / (num.size() - 1);
 cout << "mean = " << mean << endl
      << "variance = " << variance << endl;
}

Выходы:среднее значение = 5036,71 дисперсия = 3,16806e 07

Пример расчета отклонения:

#include <math.h>
#include <vector>

double Variance(std::vector<double>);

int main()
{
     std::vector<double> samples;
     samples.push_back(2.0);
     samples.push_back(3.0);
     samples.push_back(4.0);
     samples.push_back(5.0);
     samples.push_back(6.0);
     samples.push_back(7.0);

     double variance = Variance(samples);
     return 0;
}

double Variance(std::vector<double> samples)
{
     int size = samples.size();

     double variance = 0;
     double t = samples[0];
     for (int i = 1; i < size; i++)
     {
          t += samples[i];
          double diff = ((i + 1) * samples[i]) - t;
          variance += (diff * diff) / ((i + 1.0) *i);
     }

     return variance / (size - 1);
}

Поскольку вы работаете с большими числами, а затем выполняете над ними операции с плавающей запятой, возможно, вам захочется сделать все в двойном формате;это сэкономит вам много забросов.

С использованием pow .. 2 вычисление квадрата кажется немного неудобным.Вы можете сначала вычислить свое число, а затем умножить его само на себя, чтобы получить квадрат.

Если вы выполняете деление и чувствуете необходимость притворения, примените операнды (т.е.числитель и/или знаменатель), а не результат.Вы теряете точность, если делите целые числа.

Я не уверен, верна ли ваша формула дисперсии.Вы можете посмотреть объяснение, например, в Википедии.Но я тоже не эксперт по математике, поэтому не уверен, что вы ошиблись.

Поскольку дисперсия представляет собой квадрат стандартного отклонения, ответы на СО 1174984 должно помочь.Краткий диагноз заключается в том, что вам нужно вычислить сумму квадратов значений, а также сумму значений, а вы, похоже, этого не делаете.

Поскольку у тебя есть 106 значений, а квадрат любого значения может достигать 108, вы можете получить сумму квадратов до 1014;ваши 64-битные целые числа могут хранить до 1018, поэтому вы по-прежнему можете обрабатывать в десять тысяч раз больше входных данных или значения в диапазоне до одного миллиона вместо десяти тысяч, не сталкиваясь при этом с переполнением.Поэтому нет острой необходимости переходить к чистым двойным вычислениям.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top