【问题标题】:C++ STL List calculate averageC++ STL List 计算平均值
【发布时间】:2010-02-01 13:29:49
【问题描述】:

我必须更正一些 C++/STL 代码。不幸的是,我几乎没有 C++ 经验,对 STL 一无所知。尽管如此,我完成了大部分工作,但下面的功能仍然给我带来了问题:

C++ 源代码:

double MyClass::CalculateAvg(const std::list<double> &list)
{
    double avg = 0;
    std::list<int>::iterator it;
    for(it = list->begin(); it != list->end(); it++) avg += *it;
    avg /= list->size();
}

C++ 头文件:

static double CalculateAvg(const std::list<int> &list);

这很可能是为了从列表中计算平均值,但它会产生很多错误。我试图在网上搜索解决方案,但我找不到任何东西。如果有人可以帮助我,我会很高兴。

更新: 感谢您的快速回复。接受的答案解决了我所有的问题。

【问题讨论】:

  • 你至少应该返回结果:"return avg"
  • 您没有在此处发布错误的任何特殊原因?你知道,我们不是通灵者。

标签: c++ list stl average


【解决方案1】:

几件事:

  1. 您不返回任何东西。 (加return avg;
  2. -&gt; 运算符用于指向对象的指针。你有一个列表的引用,所以你使用list.begin()而不是list-&gt;begin()(其他成员函数也一样)
  3. 迭代器应该是const_iterator,而不是iterator

在任何情况下,您都应该执行以下操作:

return std::accumulate(list.begin(), list.end(), 0.0) / list.size();

如果在您的用例中可能,可以选择检查list.size() == 0

【讨论】:

  • +1 表示 std::accumulate,请记住 list.size() 将遍历列表,因此您不想调用它两次
  • size() 在某些编译器上是一个常数时间操作,在 C++0x 中必须如此。
  • 我不知道为什么我放 will 而不是 may,但是是的,因为今天的标准尺寸只需要 O(n)
  • 是的,如果你调用它两次,它会是 2 的常数因子,它确实是 O(n),所以总体上仍然是 O(n),但它仍然值得考虑,尽管人们经常假设大小总是 O(1)
  • @Peter Alexander 使用 empty() 而不是 size 以防您检查是否为空:)。
【解决方案2】:

所以,第一个错误就在那里:

std::list<int>::iterator it;

您在整数列表上定义了一个迭代器,并使用它来迭代一个双精度列表。此外,迭代器只能用于非常量列表。您需要一个常量运算符。你应该写:

std::list<double>::const_iterator it;

最后,你忘记返回值了。

编辑:我没看到,但是您将列表作为参考传递,但将其用作指针。所以用list.替换所有list-&gt;

【讨论】:

    【解决方案3】:

    除了@PierreBdR 的回答, 您还应该检查 list->size() 是否大于 0,

    就在这之前

      avg /= list.size();
    

    添加

     if (list.size()>0) 
        //avg code here.
    

    或记录作为参数接收的列表不应为空。

       assert(list.size()>0)
    

    【讨论】:

    • 首选!list.empty() 而不是list.size() &gt; 0 - 一个具有常数,另一个具有线性复杂度-
    • 是的,我一直忘记...该死的Java
    【解决方案4】:

    与其进行编辑以确保您的迭代器引用相同类型的列表,不如将代码编写为通用算法,并将迭代器类型作为模板参数。

    我还要注意,对于std::list,您的原始代码大多数发布的答案都有一个相当严重的效率问题:给定一个典型的列表实现,它们会遍历列表一次将值相加,然后再次计算元素的数量。理论上,list.size()可以在不迭代元素的情况下以恒定的时间运行,但实际上这种情况很少见(list::size()list::splice 可以具有恒定的复杂性,但不能同时使用两者一次)。

    我会写这样的代码:

    template <class fwdit> 
    typename fwdit::value_type arithmetic_mean(fwdit begin, fwdit end) { 
    
        typedef typename fwdit::value_type res_type;
    
        res_type sum = res_type();
        size_t count = 0;
    
        for (fwdit pos = begin; pos!= end; ++pos) { 
            sum += *pos;
            ++count;
        }
        return sum/count;
    }
    

    这是通用的,所以当你意识到std::list 是一个糟糕的选择时,它会继续工作(不变),而你真的会更好地使用std::vector。同样,如果您想要一些 int 的算术平均值而不是 double 的,它也可以处理(同样,无需更改代码)。第三,即使(如上所述)您的库对 st::list::size() 的实现恰好是线性的,这仍然只遍历列表一次,因此它的速度可能是原始代码(工作版本)的两倍左右。

    当然,缓存可以(将)影响这一点 - 当您平均一个小列表时,第一次遍历会将整个列表拉入缓存,因此第二次遍历通常会快很多(因此消除了第二次遍历不会节省太多时间)。

    【讨论】:

    • 这不会按预期工作。例如对于vector,返回类型是int,但是平均值是double类型,结果会被截断
    • 是的,你可以将它用于vector&lt;int&gt;,但我可能会使用std::accumulate(v.begin(), v.end(), 0.0) / v.size();
    • 因为您将其命名为“arithmetic_mean”,但是这不会保留算术平均值的属性。特别是:如果数字 x_1, ..., x_n 的均值为 x,则 (x_1-x) + ... + (x_n-x) = 0。en.wikipedia.org/wiki/Arithmetic_mean
    • 即使您似乎期望这会提供您的第二个解决方案:std::accumulate(v.begin(), v.end(), 0.0) / v.size();是的,您应该期望算术平均值返回正确的结果。浮点数或整数数学与此无关
    • IMO 函数写错了。这会返回一些东西,但绝对不是平均平均值,也不是我知道的任何统计数据。您应该警告它的作用并更改其名称或更改其行为。
    【解决方案5】:

    你传递了一个std::list&lt;double&gt; 但你创建了一个std::list&lt;int&gt; 迭代器?你的原型也需要std::list&lt;int&gt;

    【讨论】:

    • 这些不应该是cmets吗?
    【解决方案6】:

    正如 Maurits Rijk 所说,您还没有返回 avg。另外,它编译时会出现什么错误?

    【讨论】:

    • 如果要评论,请使用 cmets!
    【解决方案7】:

    由于list 是引用而不是指针,因此您不需要-&gt; 取消引用运算符,而只需. 运算符,即it = list.begin() 等等。

    此外,正如其他人所指出的,列表及其迭代器的模板类型参数都需要匹配:&lt;int&gt;&lt;double&gt;。看起来该函数最初是为了接受doubles 的列表而编写的。

    【讨论】:

      【解决方案8】:

      作为自己的练习。一旦你让它工作,你应该把它转换成for_each。从长远来看更容易理解。

      -- 编辑--

      Accumulate 更好,因为它适用于二进制数字运算。

      【讨论】:

      • for_each 几乎从来都不是算法的好选择,这里累积会更好
      猜你喜欢
      • 2014-08-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-01-02
      • 2013-01-14
      • 2012-06-19
      相关资源
      最近更新 更多