在C++中,如何使用向量视图和gsl_stats_mean计算整数向量的平均值?

7

我的程序操作的是整数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)。有什么想法吗?先谢谢了。

1
对于一个良好的首次提问,给予加1分。 - aschepler
6个回答

4
double* 强制转换的做法非常可疑。
每当你想使用强制转换时,请再次考虑。然后寻找一种可以避免使用强制转换的方法(如果转换是隐式的,可以通过引入临时变量来实现)。最后,在进行强制转换之前三思。
由于内存区域实际上并不包含 double 值,因此代码简单地将那里的位模式解释为表示双精度数,从而产生了可预见的不良影响。将 int* 强制转换为 double* 与对数组的每个元素进行转换大不相同。

好的,从现在开始我会更加谨慎地使用类型转换。Mark B也提出了一个使用临时向量的解决方案,但这是否意味着我的初始向量必须被暂时复制?如果这个向量非常大怎么办? - tflutre

3

使用整数统计函数:

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

请注意使用gsl_stats_int_mean而不是gsl_stats_mean

2

除非你要进行比平均数更复杂的大量统计工作,否则我建议忽略gsl,只使用标准算法:

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

如果使用统计库是有正当理由的,你的首选可能应该是寻找更好设计的库(例如Boost Accumulators)。

如果出于某种原因您决定确实需要使用gsl,则看起来您将不得不先将int数组复制到double数组中,然后对结果使用gsl。这显然非常低效,特别是在处理大量数据时--因此建议您改用其他替代品。


1
我正在编写一个模拟器,因此我需要一个随机数生成器以及计算平均值、方差、标准差、分位数等函数。这就是为什么我想继续使用GSL而不使用std::accumulate的原因。 - tflutre
@wfoolhill:标准库已经提供了伪随机数生成器,而Boost累加器(以及许多其他库)可以计算您提到的所有函数——比gsl更加清晰。 - Jerry Coffin
我不了解Boost,因此我遵循您的建议,快速查看了Boost.Accumulator的文档。它似乎确实非常强大,但我认为我并不需要它的所有功能。到目前为止,使用GSL就已经满足了我的需求。 - tflutre
@wfoolhill:除了工作之外,你需要的一切。即使你解决了这个问题,它仍然只是“除了工作良好”。 - Jerry Coffin

1
根据http://www.gnu.org/software/gsl/manual/html_node/Mean-and-standard-deviation-and-variance.htmlgsl_stats_mean函数需要一个double数组。你正在使用int向量,并告诉它使用原始字节作为double,这样做是不正确的。
你需要设置一个临时的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());

编辑:您也可以使用标准库算法来计算整数平均值:

// 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();

谢谢,但是通过使用向量视图,我想避免使用临时向量,因为如果初始向量非常大,将其复制到临时向量中会浪费时间...或者"tempForStats(stl_v.begin(), stl_v.end());"非常高效吗? - tflutre

1

虽然我不熟悉GSL,但表达式(double*) gsl_v.vector.data看起来非常可疑。您确定使用reinterpret_cast转换指针以获取double数据是正确的吗?


1
将数据类型转换为double*会破坏您的数据。它不是将数据转换为double,而只是将int二进制数据作为double使用。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接