Как в C++ вычислить среднее значение вектора целых чисел, используя векторное представление и gsl_stats_mean?

моя программа манипулирует векторами STL целых чисел, но время от времени мне нужно вычислить по ним некоторую статистику. Поэтому я использую функции GSL. Чтобы избежать копирования вектора STL в вектор GSL, я создаю векторное представление GSL и передаю его функциям GSL, как в этом фрагменте кода:

#include <iostream>
#include <vector>
#include <gsl/gsl_vector.h>
#include <gsl/gsl_statistics.h>
using namespace std;

int main( int argc, char* argv[] )
{
  vector<int> stl_v;
  for( int i=0; i<5; ++i )
    stl_v.push_back( i );

  gsl_vector_int_const_view gsl_v = gsl_vector_int_const_view_array( &stl_v[0], stl_v.size() );

  for( int i=0; i<stl_v.size(); ++i )
    cout << "gsl_v_" << i << "=" << gsl_vector_int_get( &gsl_v.vector, i ) << endl;

  cout << "mean=" << gsl_stats_mean( (double*) gsl_v.vector.data, 1, stl_v.size() ) << endl;
}

После компиляции (gcc -lstdc++ -lgsl -lgslcblas test.cpp) этот код выводит следующее:

gsl_v_0=0
gsl_v_1=1
gsl_v_2=2
gsl_v_3=3
gsl_v_4=4
mean=5.73266e-310

Векторный вид создан правильно, но я не понимаю, почему среднее неверно (оно должно быть равно 10/5=2). Есть идеи? Заранее спасибо.


person tflutre    schedule 31.01.2011    source источник
comment
+1 за хорошо заданный первый вопрос.   -  person aschepler    schedule 31.01.2011


Ответы (6)


Используйте функции целочисленной статистики:

cout << "mean=" << gsl_stats_int_mean( gsl_v.vector.data, 1, stl_v.size() ) << endl;

Обратите внимание на gsl_stats_int_mean вместо gsl_stats_mean.

person Philipp    schedule 31.01.2011

Приведение к double* очень подозрительно.

Каждый раз, когда у вас возникает соблазн использовать гипс, подумайте еще раз. Затем поищите способ сделать это без приведения (возможно, путем введения временной переменной, если преобразование неявное). Тогда подумайте в третий раз, прежде чем бросать.

Поскольку область памяти на самом деле не содержит значений double, код просто интерпретирует битовые комбинации там, как если бы они представляли двойные значения, с предсказуемо нежелательными эффектами. Приведение int* к double* ОЧЕНЬ отличается от приведения каждого элемента массива.

person Ben Voigt    schedule 31.01.2011
comment
Хорошо, с этого момента я буду осторожнее с гипсом. Марк Б также предложил решение с временным вектором, но значит ли это, что мой исходный вектор нужно временно копировать? Что, если этот вектор очень большой? - person tflutre; 31.01.2011

Если вы не делаете много статистических данных, значительно более сложных, чем среднее значение, я бы проигнорировал gsl и просто использовал стандартные алгоритмы:

double mean = std::accumulate(stl_v.begin(), stl_v.end(), 0.0) / stl_v.size();

Когда / если использование статистической библиотеки оправдано, ваш первый выбор, вероятно, должен состоять в том, чтобы искать что-то еще, что лучше разработано (например, Boost Accumulators).

Если вы по какой-либо причине решите, что вам действительно нужно использовать gsl, похоже, вам придется сначала скопировать массив ints в массив doubles, а затем использовать gsl для результата. Очевидно, что это довольно неэффективно, особенно если вы имеете дело с большим количеством данных — таким образом, предыдущий совет использовать вместо этого что-то другое.

person Jerry Coffin    schedule 31.01.2011
comment
Я пишу симулятор, поэтому мне нужен RNG, а также функции для вычисления среднего, var, sd, квантилей и так далее. Вот почему я хотел бы продолжать использовать GSL, а не использовать std::accumulate. - person tflutre; 31.01.2011
comment
@wfoolhill: стандартная библиотека уже предоставляет PRNG, а аккумуляторы Boost (среди многих других) могут вычислять все упомянутые вами функции - гораздо чище, чем на это надеется gsl. - person Jerry Coffin; 31.01.2011
comment
Я не знаю Boost, поэтому, следуя вашему совету, я быстро просмотрел документацию Boost.Accumulator. Он кажется действительно очень мощным, но я не думаю, что мне нужны все его функции. У меня есть все, что мне нужно с GSL до сих пор. - person tflutre; 01.02.2011
comment
@wfoolhill: Все, что вам нужно, кроме работы, конечно. И даже если вы это исправите, это все равно не работает хорошо. - person Jerry Coffin; 01.02.2011

Хотя я не знаком с GSL, выражение (double*) gsl_v.vector.data выглядит крайне подозрительно. Вы уверены, что правильно использовать reinterpret_cast этот указатель для получения double данных?

person aschepler    schedule 31.01.2011

Приведение к double* испортит ваши данные. Это не преобразование данных в double, а просто использование int двоичных данных как double

person Elalfer    schedule 31.01.2011

Согласно http://www.gnu.org/software/gsl/manual/html_node/Mean-and-standard-deviation-and-variance.html функция gsl_stats_mean принимает массив double. Вы берете vector из int и говорите ему использовать необработанные байты как double, что не будет работать правильно.

Вам нужно настроить временный vector из double для передачи:

// Assumes that there's at least one item in stl_v.
std::vector<double> tempForStats(stl_v.begin(), stl_v.end());
gsl_stats_mean(&tempForStats[0], 1, tempForStats.size());

РЕДАКТИРОВАТЬ: вы также можете использовать стандартные алгоритмы библиотеки, чтобы сделать int самостоятельно:

// Assumes that there's at least one item in stl_v.
double total = std::accumulate(stl_v.begin(), stl_v.end(), 0);
double mean = total / stl_v.size();
person Mark B    schedule 31.01.2011
comment
Спасибо, но, используя векторное представление, я хотел избежать временного вектора, потому что, если исходный вектор очень большой, я потеряю время, копируя его во временный... Или tempForStats(stl_v.begin(), stl_v .конец()); очень эффективный? - person tflutre; 31.01.2011