Perhatikan, tag :: variance menghitung varians dengan rumus perkiraan. tag :: variance (lazy) menghitung dengan rumus yang tepat, khususnya: second moment - squared meanyang akan menghasilkan hasil yang salah jika varians sangat kecil karena kesalahan pembulatan. Ini sebenarnya dapat menghasilkan varian negatif.
panda-34
Gunakan algoritme rekursif (daring) jika Anda tahu Anda akan memiliki banyak angka. Ini akan menangani masalah underflow dan overflow.
Kemin Zhou
219
Saya tidak tahu apakah Boost memiliki fungsi yang lebih spesifik, tetapi Anda dapat melakukannya dengan pustaka standar.
Diberikan std::vector<double> v, ini adalah cara yang naif:
#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);
Ini rentan terhadap overflow atau underflow untuk nilai besar atau kecil. Cara yang sedikit lebih baik untuk menghitung deviasi standar adalah:
Iya; Jelas, bagian bawah bergantung pada nilai meanhitung di bagian atas.
musiphil
7
Kumpulan persamaan pertama tidak berfungsi. Saya meletakkan int 10 & 2, dan mendapatkan output 4. Sekilas saya pikir itu b / c diasumsikan bahwa (ab) ^ 2 = a ^ 2-b ^ 2
Charles L.
2
@CharlesL .: Seharusnya berhasil, dan 4 adalah jawaban yang benar.
musiphil
3
@StudentT: Tidak, tapi Anda dapat menggantikan (v.size() - 1)untuk v.size()di baris terakhir di atas: std::sqrt(sq_sum / (v.size() - 1)). (Untuk metode pertama, itu sedikit rumit: std::sqrt(sq_sum / (v.size() - 1) - mean * mean * v.size() / (v.size() - 1)).
musiphil
6
Penggunaan std::inner_productuntuk jumlah kotak sangat rapi.
Paul R
65
Jika kinerja penting bagi Anda, dan kompilator Anda mendukung lambda, penghitungan stdev dapat dibuat lebih cepat dan sederhana: Dalam pengujian dengan VS 2012, saya telah menemukan bahwa kode berikut lebih dari 10 X lebih cepat daripada kode Peningkatan yang diberikan dalam jawaban yang dipilih ; Ini juga 5 X lebih cepat daripada versi jawaban yang lebih aman menggunakan pustaka standar yang diberikan oleh musiphil.
Terima kasih telah membagikan jawaban ini bahkan setahun kemudian. Sekarang saya datang satu tahun kemudian dan membuat yang satu ini generik untuk tipe nilai dan tipe wadah. Lihat di sini (Catatan: Saya rasa perulangan for berbasis jangkauan saya secepat kode lambda Anda.)
leemes
2
apa perbedaan antara menggunakan std :: end (v) daripada v.end ()?
spurra
3
The std::end()Fungsi ditambahkan oleh C ++ 11 standar untuk kasus-kasus ketika tidak ada yang seperti v.end(). The std::enddapat kelebihan beban untuk kontainer kurang standar - lihat en.cppreference.com/w/cpp/iterator/end
pepr
Bisakah Anda menjelaskan mengapa ini lebih cepat?
dev_nut
4
Nah untuk satu hal, jawaban "aman" (yang seperti jawaban saya) membuat 3 melewati array: Sekali untuk jumlah, sekali untuk diff-mean, dan sekali untuk kuadrat. Dalam kode saya hanya ada 2 lintasan - Ini menggabungkan dua lintasan kedua menjadi satu. Dan (ketika saya terakhir melihat, beberapa waktu yang lalu sekarang!) Panggilan produk_ dalam tidak dioptimalkan. Selain itu, kode "aman" menyalin v ke dalam array diff yang benar-benar baru, yang menambahkan lebih banyak penundaan. Menurut pendapat saya, kode saya juga lebih mudah dibaca - dan mudah di-porting ke JavaScript dan bahasa lain :)
Josh Greifer
5
Memperbaiki jawaban musiphil , Anda dapat menulis fungsi deviasi standar tanpa vektor sementara diff, hanya menggunakan satu inner_productpanggilan dengan kapabilitas lambda C ++ 11:
Saya menduga melakukan pengurangan beberapa kali lebih murah daripada menggunakan penyimpanan perantara tambahan, dan menurut saya ini lebih mudah dibaca, tetapi saya belum menguji kinerjanya.
Jawaban saya mirip dengan Josh Greifer tetapi digeneralisasikan ke kovarian sampel. Varians sampel hanyalah kovarians sampel tetapi dengan dua input identik. Ini termasuk korelasi Bessel.
2x lebih cepat dari versi yang disebutkan sebelumnya - kebanyakan karena loop transform () dan inner_product () digabungkan. Maaf tentang pintasan / typedefs / makro saya: Flo = float. CR const ref. VFlo - vektor. Diuji di 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);
}
Bisakah loop Cit () ditulis sebagai for( float f : crVec ) { fSqSum += f * f; fSum += f; } ?
Elfen Dew
1
Ya di C ++ 11. Mencoba menggunakan makro yang membuatnya tidak bergantung pada versi. Memperbarui kode. PS. Untuk keterbacaan, saya biasanya lebih suka 1 tindakan per LOC. Compiler harus melihat bahwa itu adalah iterasi yang konstan dan menggabungkannya jika "berpikir" lebih cepat untuk melakukan iterasi sekali. Melakukannya dalam langkah-langkah kecil yang singkat (tanpa menggunakan std :: inner_product () eg), jenis assembly-style, menjelaskan kepada pembaca baru apa artinya. Biner akan menjadi lebih kecil karena efek sampingnya (dalam beberapa kasus).
slyy2048
"Mencoba menggunakan makro yang menjadikannya versi independen" - namun Anda membatasi diri Anda pada Visual C ++ non-standar "untuk setiap" konstruksi ( stackoverflow.com/questions/197375/… )
kode
-3
Buat wadah Anda sendiri:
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();
}
};
Itu memang memiliki beberapa batasan, tetapi bekerja dengan baik ketika Anda tahu apa yang Anda lakukan.
Untuk menjawab pertanyaan: karena sama sekali tidak perlu. Membuat wadah Anda sendiri sama sekali tidak memiliki manfaat dibandingkan dengan menulis fungsi gratis.
Konrad Rudolph
1
Aku bahkan tidak tahu harus mulai dari mana. Anda menggunakan daftar sebagai struktur data yang mendasarinya, Anda bahkan tidak menyimpan nilai-nilainya, yang akan menjadi salah satu dari beberapa alasan saya dapat memikirkan untuk menggunakan struktur seperti wadah. Terutama jika nilainya jarang terjadi dan mean / stddev sering dibutuhkan.
Creat
-7
// Berarti deviasi dalam c ++
/ Penyimpangan yang merupakan perbedaan antara nilai yang diamati dan nilai sebenarnya dari sejumlah kepentingan (seperti rata-rata populasi) adalah kesalahan dan penyimpangan yang merupakan perbedaan antara nilai yang diamati dan perkiraan nilai sebenarnya (seperti perkiraan mungkin mean sampel) adalah sisa. Konsep-konsep ini berlaku untuk data pada tingkat interval dan rasio pengukuran. /
#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;
Jawaban:
Menggunakan akumulator adalah cara untuk menghitung rata-rata dan deviasi standar di 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;
sumber
second moment - squared mean
yang akan menghasilkan hasil yang salah jika varians sangat kecil karena kesalahan pembulatan. Ini sebenarnya dapat menghasilkan varian negatif.Saya tidak tahu apakah Boost memiliki fungsi yang lebih spesifik, tetapi Anda dapat melakukannya dengan pustaka standar.
Diberikan
std::vector<double> v
, ini adalah cara yang naif:#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);
Ini rentan terhadap overflow atau underflow untuk nilai besar atau kecil. Cara yang sedikit lebih baik untuk menghitung deviasi standar adalah:
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());
UPDATE untuk C ++ 11:
Panggilan ke
std::transform
dapat ditulis menggunakan fungsi lambda sebagai gantistd::minus
danstd::bind2nd
(sekarang tidak digunakan lagi):std::transform(v.begin(), v.end(), diff.begin(), [mean](double x) { return x - mean; });
sumber
mean
hitung di bagian atas.(v.size() - 1)
untukv.size()
di baris terakhir di atas:std::sqrt(sq_sum / (v.size() - 1))
. (Untuk metode pertama, itu sedikit rumit:std::sqrt(sq_sum / (v.size() - 1) - mean * mean * v.size() / (v.size() - 1))
.std::inner_product
untuk jumlah kotak sangat rapi.Jika kinerja penting bagi Anda, dan kompilator Anda mendukung lambda, penghitungan stdev dapat dibuat lebih cepat dan sederhana: Dalam pengujian dengan VS 2012, saya telah menemukan bahwa kode berikut lebih dari 10 X lebih cepat daripada kode Peningkatan yang diberikan dalam jawaban yang dipilih ; Ini juga 5 X lebih cepat daripada versi jawaban yang lebih aman menggunakan pustaka standar yang diberikan oleh musiphil.
Catatan Saya menggunakan sampel deviasi standar, jadi kode di bawah ini memberikan hasil yang sedikit berbeda ( Mengapa ada Minus Satu dalam Standar Deviasi )
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));
sumber
std::end()
Fungsi ditambahkan oleh C ++ 11 standar untuk kasus-kasus ketika tidak ada yang sepertiv.end()
. Thestd::end
dapat kelebihan beban untuk kontainer kurang standar - lihat en.cppreference.com/w/cpp/iterator/endMemperbaiki jawaban musiphil , Anda dapat menulis fungsi deviasi standar tanpa vektor sementara
diff
, hanya menggunakan satuinner_product
panggilan dengan kapabilitas lambda 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 )); }
Saya menduga melakukan pengurangan beberapa kali lebih murah daripada menggunakan penyimpanan perantara tambahan, dan menurut saya ini lebih mudah dibaca, tetapi saya belum menguji kinerjanya.
sumber
Sepertinya solusi rekursif elegan berikut ini belum pernah disebutkan, meski sudah ada sejak lama. Mengacu pada Seni Pemrograman Komputer Knuth,
kemudian untuk daftar
n>=2
nilai, perkiraan standar deviasi adalah:stddev = std::sqrt(variance_n / (n-1)).
Semoga ini membantu!
sumber
Jawaban saya mirip dengan Josh Greifer tetapi digeneralisasikan ke kovarian sampel. Varians sampel hanyalah kovarians sampel tetapi dengan dua input identik. Ini termasuk korelasi Bessel.
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); }
sumber
2x lebih cepat dari versi yang disebutkan sebelumnya - kebanyakan karena loop transform () dan inner_product () digabungkan. Maaf tentang pintasan / typedefs / makro saya: Flo = float. CR const ref. VFlo - vektor. Diuji di 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); }
sumber
for( float f : crVec ) { fSqSum += f * f; fSum += f; }
?Buat wadah Anda sendiri:
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(); } };
Itu memang memiliki beberapa batasan, tetapi bekerja dengan baik ketika Anda tahu apa yang Anda lakukan.
sumber
// Berarti deviasi dalam c ++
/ Penyimpangan yang merupakan perbedaan antara nilai yang diamati dan nilai sebenarnya dari sejumlah kepentingan (seperti rata-rata populasi) adalah kesalahan dan penyimpangan yang merupakan perbedaan antara nilai yang diamati dan perkiraan nilai sebenarnya (seperti perkiraan mungkin mean sampel) adalah sisa. Konsep-konsep ini berlaku untuk data pada tingkat interval dan rasio pengukuran. /
#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;
}
sumber