Обратите внимание, что tag :: variance вычисляет дисперсию по приблизительной формуле. tag :: variance (ленивый) вычисляет по точной формуле, а именно: second moment - squared meanкоторая даст неверный результат, если дисперсия очень мала из-за ошибок округления. Фактически это может привести к отрицательной дисперсии.
panda-34
Используйте рекурсивный (онлайн) алгоритм, если вы знаете, что у вас будет много чисел. Это решит проблемы как недостаточного, так и переполнения.
Кемин Чжоу
218
Я не знаю, есть ли у Boost более специфические функции, но вы можете сделать это с помощью стандартной библиотеки.
Учитывая std::vector<double> v, что это наивный способ:
#include<numeric>double sum = std::accumulate(v.begin(), v.end(), 0.0);
double mean = sum / v.size();
double sq_sum = std::inner_product(v.begin(), v.end(), v.begin(), 0.0);
double stdev = std::sqrt(sq_sum / v.size() - mean * mean);
Это подвержено переполнению или недостаточному значению для больших или маленьких значений. Чуть лучше рассчитать стандартное отклонение:
Да; очевидно, что нижняя часть зависит от значения, meanрассчитанного в верхней части.
musiphil
7
Первая система уравнений не работает. Я ввел int 10 & 2 и получил 4. На первый взгляд, я думаю, что это b / c, предполагается, что (ab) ^ 2 = a ^ 2-b ^ 2
Чарльз Л.
2
@CharlesL .: Это должно сработать, и 4 - правильный ответ.
musiphil
3
@StudentT: Нет, но вы можете заменить (v.size() - 1)на v.size()в последней строке выше: std::sqrt(sq_sum / (v.size() - 1)). (Для первого способа, это немного сложно: std::sqrt(sq_sum / (v.size() - 1) - mean * mean * v.size() / (v.size() - 1)).
musiphil
6
Использование std::inner_productсуммы квадратов очень удобно.
Paul R
65
Если для вас важна производительность и ваш компилятор поддерживает лямбда-выражения, расчет stdev можно сделать быстрее и проще: в тестах с VS 2012 я обнаружил, что следующий код более чем в 10 раз быстрее, чем код Boost, указанный в выбранном ответе ; это также в 5 раз быстрее, чем более безопасная версия ответа с использованием стандартных библиотек, предоставленных musiphil.
Спасибо, что поделились этим ответом даже год спустя. Теперь я пришел еще через год и сделал его универсальным как для типа значения, так и для типа контейнера. См. Здесь (Примечание: я предполагаю, что мой цикл for, основанный на диапазоне, работает так же быстро, как и ваш лямбда-код.)
leemes
2
в чем разница между использованием std :: end (v) вместо v.end ()?
spurra
3
std::end()Функция была добавлена в соответствии со стандартом C ++ 11 для случаев , когда нет ничего подобного v.end(). std::endМожет быть перегружен для менее стандартного контейнера - см en.cppreference.com/w/cpp/iterator/end
pepr
Вы можете объяснить, почему это быстрее?
dev_nut
4
Во-первых, «безопасный» ответ (который похож на мой ответ) делает 3 прохода через массив: один раз для суммы, один раз для среднего значения и один раз для возведения в квадрат. В моем коде всего 2 прохода - он объединяет вторые два прохода в один. И (когда я последний раз смотрел, довольно давно!) Вызовы inner_product не были оптимизированы. Кроме того, «безопасный» код копирует v в совершенно новый массив различий, что увеличивает задержку. На мой взгляд, мой код также более читабелен - и его легко портировать на JavaScript и другие языки :)
Джош Грейфер
5
Улучшив ответ musiphil , вы можете написать функцию стандартного отклонения без временного вектора diff, просто используя один inner_productвызов с возможностями лямбда- выражения C ++ 11:
Я подозреваю, что выполнять вычитание несколько раз дешевле, чем использовать дополнительное промежуточное хранилище, и я думаю, что это более читабельно, но я еще не тестировал производительность.
Я думаю, это вычисление дисперсии, а не стандартного отклонения.
sg_man 08
Стандартное отклонение рассчитывается делением на N, а не на N-1. Почему вы делите sq_sum на func.size () - 1?
pocjoc
Думаю, я
вычисляю
2
Кажется, следующее элегантное рекурсивное решение не упоминалось, хотя оно существует уже давно. Ссылаясь на "Искусство компьютерного программирования Кнута",
Мой ответ аналогичен ответу Джоша Грейфера, но обобщен на выборочную ковариацию. Вариация выборки - это просто ковариация выборки, но с двумя идентичными входными данными. Это включает корреляцию Бесселя.
В 2 раза быстрее, чем в предыдущих версиях, в основном потому, что циклы transform () и inner_product () объединены. Извините за мой ярлык / typedefs / macro: Flo = float. CR const ref. VFlo - вектор. Протестировано в VS2010
#define fe(EL, CONTAINER) for each (auto EL in CONTAINER) //VS2010Flo stdDev(VFlo CR crVec){
SZ n = crVec.size(); if (n < 2) return0.0f;
Flo fSqSum = 0.0f, fSum = 0.0f;
fe(f, crVec) fSqSum += f * f; // EDIT: was Cit(VFlo, crVec) {
fe(f, crVec) fSum += f;
Flo fSumSq = fSum * fSum;
Flo fSumSqDivN = fSumSq / n;
Flo fSubSqSum = fSqSum - fSumSqDivN;
Flo fPreSqrt = fSubSqSum / (n - 1);
returnsqrt(fPreSqrt);
}
Можно ли записать цикл Cit () как for( float f : crVec ) { fSqSum += f * f; fSum += f; } ?
Elfen Dew
1
Да в C ++ 11. Попытка использовать макросы, которые делают его независимым от версии. Обновил код. PS. Для удобства чтения я обычно предпочитаю 1 действие на LOC. Компилятор должен видеть, что это постоянные итерации, и присоединяться к ним, если он «думает», что выполнить итерацию один раз быстрее. Выполнение этого небольшими короткими шагами (например, без использования std :: inner_product ()), своего рода ассемблерного стиля, объясняет новому читателю, что это означает. Двоичный размер будет меньше из-за побочного эффекта (в некоторых случаях).
slyy2048 07
«Пытаться использовать макросы, которые делают его независимым от версии» - но вы ограничиваете себя нестандартным Visual C ++ «для каждой» конструкции ( stackoverflow.com/questions/197375/… )
кодирование
-3
Создайте свой собственный контейнер:
template <classT>
classstatList :publicstd::list<T>
{
public:
statList() : std::list<T>::list() {}
~statList() {}
T mean(){
return accumulate(begin(),end(),0.0)/size();
}
T stddev(){
T diff_sum = 0;
T m = mean();
for(iterator it= begin(); it != end(); ++it)
diff_sum += ((*it - m)*(*it -m));
return diff_sum/size();
}
};
У него есть некоторые ограничения, но он прекрасно работает, когда вы знаете, что делаете.
Чтобы ответить на вопрос: потому что совершенно незачем. Создание собственного контейнера не имеет абсолютно никаких преимуществ по сравнению с написанием бесплатной функции.
Конрад Рудольф
1
Я даже не знаю, с чего начать. Вы используете список в качестве базовой структуры данных, вы даже не кэшируете значения, что было бы одной из немногих причин, по которым я могу придумать, чтобы использовать структуру, подобную контейнеру. Особенно, если значения случайны нечасто, а среднее / стандартное отклонение требуется часто.
Creat
-7
// означает отклонение в c ++
/ Отклонение, которое представляет собой разницу между наблюдаемым значением и истинным значением представляющей интерес величины (например, средним по совокупности), является ошибкой, а отклонение - разницей между наблюдаемым значением и оценкой истинного значения (например, оценка может быть выборочным средним) является остатком. Эти концепции применимы к данным на интервальном и относительном уровнях измерения. /
#include<iostream>#include<conio.h>usingnamespacestd;
/* run this program using the console pauser or add your own getch, system("pause") or input loop */intmain(int argc, char** argv){
int i,cnt;
cout<<"please inter count:\t";
cin>>cnt;
float *num=newfloat [cnt];
float *s=newfloat [cnt];
float sum=0,ave,M,M_D;
for(i=0;i<cnt;i++)
{
cin>>num[i];
sum+=num[i];
}
ave=sum/cnt;
for(i=0;i<cnt;i++)
{
s[i]=ave-num[i];
if(s[i]<0)
{
s[i]=s[i]*(-1);
}
cout<<"\n|ave - number| = "<<s[i];
M+=s[i];
}
M_D=M/cnt;
cout<<"\n\n Average: "<<ave;
cout<<"\n M.D(Mean Deviation): "<<M_D;
getch();
return0;
Ответы:
Использование аккумуляторов - это способ вычисления средних значений и стандартных отклонений в Boost .
accumulator_set<double, stats<tag::variance> > acc; for_each(a_vec.begin(), a_vec.end(), bind<void>(ref(acc), _1)); cout << mean(acc) << endl; cout << sqrt(variance(acc)) << endl;
источник
second moment - squared mean
которая даст неверный результат, если дисперсия очень мала из-за ошибок округления. Фактически это может привести к отрицательной дисперсии.Я не знаю, есть ли у Boost более специфические функции, но вы можете сделать это с помощью стандартной библиотеки.
Учитывая
std::vector<double> v
, что это наивный способ:#include <numeric> double sum = std::accumulate(v.begin(), v.end(), 0.0); double mean = sum / v.size(); double sq_sum = std::inner_product(v.begin(), v.end(), v.begin(), 0.0); double stdev = std::sqrt(sq_sum / v.size() - mean * mean);
Это подвержено переполнению или недостаточному значению для больших или маленьких значений. Чуть лучше рассчитать стандартное отклонение:
double sum = std::accumulate(v.begin(), v.end(), 0.0); double mean = sum / v.size(); std::vector<double> diff(v.size()); std::transform(v.begin(), v.end(), diff.begin(), std::bind2nd(std::minus<double>(), mean)); double sq_sum = std::inner_product(diff.begin(), diff.end(), diff.begin(), 0.0); double stdev = std::sqrt(sq_sum / v.size());
ОБНОВЛЕНИЕ для C ++ 11:
Вызов
std::transform
может быть записан с использованием лямбда-функции вместоstd::minus
иstd::bind2nd
(теперь не рекомендуется):std::transform(v.begin(), v.end(), diff.begin(), [mean](double x) { return x - mean; });
источник
mean
рассчитанного в верхней части.(v.size() - 1)
наv.size()
в последней строке выше:std::sqrt(sq_sum / (v.size() - 1))
. (Для первого способа, это немного сложно:std::sqrt(sq_sum / (v.size() - 1) - mean * mean * v.size() / (v.size() - 1))
.std::inner_product
суммы квадратов очень удобно.Если для вас важна производительность и ваш компилятор поддерживает лямбда-выражения, расчет stdev можно сделать быстрее и проще: в тестах с VS 2012 я обнаружил, что следующий код более чем в 10 раз быстрее, чем код Boost, указанный в выбранном ответе ; это также в 5 раз быстрее, чем более безопасная версия ответа с использованием стандартных библиотек, предоставленных musiphil.
Обратите внимание: я использую образец стандартного отклонения, поэтому приведенный ниже код дает немного разные результаты ( почему в стандартных отклонениях стоит минус один )
double sum = std::accumulate(std::begin(v), std::end(v), 0.0); double m = sum / v.size(); double accum = 0.0; std::for_each (std::begin(v), std::end(v), [&](const double d) { accum += (d - m) * (d - m); }); double stdev = sqrt(accum / (v.size()-1));
источник
std::end()
Функция была добавлена в соответствии со стандартом C ++ 11 для случаев , когда нет ничего подобногоv.end()
.std::end
Может быть перегружен для менее стандартного контейнера - см en.cppreference.com/w/cpp/iterator/endУлучшив ответ musiphil , вы можете написать функцию стандартного отклонения без временного вектора
diff
, просто используя одинinner_product
вызов с возможностями лямбда- выражения C ++ 11:double stddev(std::vector<double> const & func) { double mean = std::accumulate(func.begin(), func.end(), 0.0) / func.size(); double sq_sum = std::inner_product(func.begin(), func.end(), func.begin(), 0.0, [](double const & x, double const & y) { return x + y; }, [mean](double const & x, double const & y) { return (x - mean)*(y - mean); }); return std::sqrt(sq_sum / ( func.size() - 1 )); }
Я подозреваю, что выполнять вычитание несколько раз дешевле, чем использовать дополнительное промежуточное хранилище, и я думаю, что это более читабельно, но я еще не тестировал производительность.
источник
Кажется, следующее элегантное рекурсивное решение не упоминалось, хотя оно существует уже давно. Ссылаясь на "Искусство компьютерного программирования Кнута",
тогда для списка
n>=2
значений оценка стандартного отклонения:stddev = std::sqrt(variance_n / (n-1)).
Надеюсь это поможет!
источник
Мой ответ аналогичен ответу Джоша Грейфера, но обобщен на выборочную ковариацию. Вариация выборки - это просто ковариация выборки, но с двумя идентичными входными данными. Это включает корреляцию Бесселя.
template <class Iter> typename Iter::value_type cov(const Iter &x, const Iter &y) { double sum_x = std::accumulate(std::begin(x), std::end(x), 0.0); double sum_y = std::accumulate(std::begin(y), std::end(y), 0.0); double mx = sum_x / x.size(); double my = sum_y / y.size(); double accum = 0.0; for (auto i = 0; i < x.size(); i++) { accum += (x.at(i) - mx) * (y.at(i) - my); } return accum / (x.size() - 1); }
источник
В 2 раза быстрее, чем в предыдущих версиях, в основном потому, что циклы transform () и inner_product () объединены. Извините за мой ярлык / typedefs / macro: Flo = float. CR const ref. VFlo - вектор. Протестировано в VS2010
#define fe(EL, CONTAINER) for each (auto EL in CONTAINER) //VS2010 Flo stdDev(VFlo CR crVec) { SZ n = crVec.size(); if (n < 2) return 0.0f; Flo fSqSum = 0.0f, fSum = 0.0f; fe(f, crVec) fSqSum += f * f; // EDIT: was Cit(VFlo, crVec) { fe(f, crVec) fSum += f; Flo fSumSq = fSum * fSum; Flo fSumSqDivN = fSumSq / n; Flo fSubSqSum = fSqSum - fSumSqDivN; Flo fPreSqrt = fSubSqSum / (n - 1); return sqrt(fPreSqrt); }
источник
for( float f : crVec ) { fSqSum += f * f; fSum += f; }
?Создайте свой собственный контейнер:
template <class T> class statList : public std::list<T> { public: statList() : std::list<T>::list() {} ~statList() {} T mean() { return accumulate(begin(),end(),0.0)/size(); } T stddev() { T diff_sum = 0; T m = mean(); for(iterator it= begin(); it != end(); ++it) diff_sum += ((*it - m)*(*it -m)); return diff_sum/size(); } };
У него есть некоторые ограничения, но он прекрасно работает, когда вы знаете, что делаете.
источник
// означает отклонение в c ++
/ Отклонение, которое представляет собой разницу между наблюдаемым значением и истинным значением представляющей интерес величины (например, средним по совокупности), является ошибкой, а отклонение - разницей между наблюдаемым значением и оценкой истинного значения (например, оценка может быть выборочным средним) является остатком. Эти концепции применимы к данным на интервальном и относительном уровнях измерения. /
#include <iostream> #include <conio.h> using namespace std; /* run this program using the console pauser or add your own getch, system("pause") or input loop */ int main(int argc, char** argv) { int i,cnt; cout<<"please inter count:\t"; cin>>cnt; float *num=new float [cnt]; float *s=new float [cnt]; float sum=0,ave,M,M_D; for(i=0;i<cnt;i++) { cin>>num[i]; sum+=num[i]; } ave=sum/cnt; for(i=0;i<cnt;i++) { s[i]=ave-num[i]; if(s[i]<0) { s[i]=s[i]*(-1); } cout<<"\n|ave - number| = "<<s[i]; M+=s[i]; } M_D=M/cnt; cout<<"\n\n Average: "<<ave; cout<<"\n M.D(Mean Deviation): "<<M_D; getch(); return 0;
}
источник