如何在C++中计算平均值?

33

我有一个作业要读取一个文件并输出平均测试分数。

这很简单,但我不喜欢现有计算平均分的方法。

average = (test1 + test2 + test3 + test4 + test5) / 5.0;

有没有办法让它只除以测试分数的数量?我在书或谷歌上都找不到类似的东西。就像这样:

average = (test + test + test + test) / ntests;

如果测试的数量在读取输入之前是未知的,那么你需要使用循环对 n 个数字进行平均。 - FrustratedWithFormsDesigner
5
非常好的问题和观察。硬编码“5”是a)使用魔法,b)多余的。 - Robᵩ
@FrustratedWithFormsDesigner,请不要帮助那些试图规避问题禁令的人。 - Michael Myers
你知道如何边进行计算边计分吗?你熟悉“累加和”的概念吗? - Beta
6个回答

67

如果你的值存储在向量或数组中,只需使用来自<numeric>std::accumulate

std::vector<double> vec;
// ... fill vec with values (do not use 0; use 0.0)
double average = std::accumulate(vec.begin(), vec.end(), 0.0) / vec.size();

1
详细说明一下@RiaD所说的:在C(++)中,当你对整数进行除法运算时,默认只使用整数运算——例如,除法的结果会被截断,导致小数点后没有数据。如果你的数据集的值足够大,这不是太大的问题,但是当处理较小的数字时,你不想失去那种精度,否则可能会导致0.75变成一个平淡无奇的0。 - Richard J. Ross III
这应该是'0.'而不仅仅是'0'吧?因为如果在范围(-0.5, 0.5)内相加数字,没有小数点的话它将保持为零。 - ikku100
1
对于vec.size()的值足够大或者vec中存在大量级的值,考虑在累加之前使用std::transform将每个值除以vec.size(),作为更稳定的数值解决方案。 - jwm

2
步骤1. 通过迭代(如果您想完成)或递归(如果您想勇敢尝试)将所有测试分数放入数组(如果您想要简单和快速)或链接列表(如果您想要灵活性但慢速)中。
步骤2. 遍历数组/列表,直到达到末尾;在遍历过程中将每个单元格/节点的内容相加。同时,在遍历过程中也要记录当前所在的单元格/节点。
步骤3. 将第一个变量的总和除以记录位置的第二个变量。这将得出平均值。请保留HTML标签。

链表不太可能有用。它可能会使构建列表稍微更有效(但摊销复杂度与大多数向量一样,如果您过度分配),但它使求和和查找长度更加复杂,而1/3是相当糟糕的得分。 - user395760
2
这对我来说有点难度。我只知道cin、cout和其他一些基础知识。 - user1174868
@Jordan 那么这将为你提供一个学习比你所知道的更多的绝佳机会。数组或列表和迭代对于所有计算机语言都是基础 -- 你越早学习它们,就越好。 - Caleb

1

想知道为什么没有人提到boost::accumulators。它不是已经发布的解决方案中最短的,但可以更容易地扩展到更一般的统计值,如标准差或更高阶矩。

#include <iostream>
#include <boost/accumulators/accumulators.hpp>
#include <boost/accumulators/statistics/stats.hpp>
#include <boost/accumulators/statistics/mean.hpp>
#include <algorithm>
#include <vector>

double mean(const std::vector<double>& values) {
    namespace bo = boost::accumulators;

    if (values.empty()) return 0.;
    bo::accumulator_set<double, bo::stats<bo::tag::mean>> acc;
    acc=std::for_each(values.begin(), values.end(), acc);
    return bo::mean(acc);
}

int main()
{
    std::vector<double> test = { 2.,6.,4.,7. };
    std::cout << "Mean:   " << mean(test) << std::endl;
    std::cout << "Mean:   " << mean({}) << std::endl;

    return 0;
}

0

这是我关于如何通过指定一个lambda函数获取每个值并将它们加起来来计算容器元素平均值的概括:

template <typename ForwardIterator, typename F>
double inline averageOf (ForwardIterator first, ForwardIterator last, F function) {
    std::vector<typename std::result_of<F(typename ForwardIterator::value_type)>::type> values;
    while (first != last) {
        values.emplace_back (function(*first));
        ++first;
    }
    return static_cast<double>(std::accumulate (values.begin(), values.end(), 0)) / values.size();
}

我测试过的客户端代码如下:

const std::list<CharmedObserver*> devotees =
    charmer->getState<CharmerStateBase>(CHARMER)->getDevotees();
const int averageHitPointsOfDevotees = averageOf (devotees.begin(), devotees.end(),
    [](const CharmedObserver* x)->int {return x->getCharmedBeing()->getHitPoints();});

0

C++11提供了很好的解决方案:

constexpr auto countArguments() -> size_t
{
    return 0;
}

template<class T1, class ... Ti>
constexpr auto countArguments(T1, Ti ...xi) -> size_t
{
    return 1 + countArguments(xi...);
}

template<class T>
constexpr auto sumAruguments(T x) -> double
{
    return x;
}

template<class T1, class ... Ti>
constexpr auto sumAruguments(T1 x1, Ti ...xi) -> double // decltype(x1 + sumAruguments(xi...))
{
    return x1 + sumAruguments(xi...);
}

template<class...T>
constexpr auto avarage(T...xi) -> double
{
    return sumAruguments(xi...) / countArguments(xi...);
}

我无法编写它以使其自动推断返回类型。 当我尝试时,average(-2) 的结果很奇怪。

https://wandbox.org/permlink/brssPjggn64lBGVq


我建议自动推断返回类型是毫无意义的。即使您提供了一个 int 参数列表,平均值也应该是浮点类型,并且在现代硬件上通常应该是 double - jwm

-1

您还可以使用可变数量的参数来计算平均值。这个函数的原理是将未知数量的参数存储在堆栈中,然后我们可以取出它们。

double average(int n, ...)          // where n - count of argument (number)
{  
    int *p = &n;                   // get pointer on list of number in stack
    p++;                           // get first number
    double *pp = (double *)p;      // transformation of the pointer type
    double sum = 0; 
    for ( int i = 0; i < n; pp++, i++ ) //looking all stack
       sum+=(*pp);                 // summarize
    return sum/n;              //return average
}

你可以这样使用这个函数:

double av1 = average( 5, 3.0, 1.5, 5.0, 1.0, 2.0 );
double av2 = average( 2, 3.0, 1.5 );

但是参数的数量必须与 n 匹配。


7
注意这段代码。由于它依赖于实现细节,不能保证p的工作方式符合您的期望。在64位机器上,一些参数是通过寄存器传递的,因此该代码将无法正常工作。要正确实现,请查看 va_start 等函数。另外,使用 C++11,也可以使用可变参数模板来解决。 - MJD
1
@MJD太仁慈了。这是一个糟糕的建议,而且这段代码永远不会通过我的代码审查。-1 - jwm
1
我离开这个答案已经大约10年了。 我换了4家公司,参与了许多项目,写了很多行代码,还进行了更多的审查。 因此,现在以适当的责任心,我可以说这个答案完全是愚蠢的。 - zifter

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