2013/11/12

std::accumulateでvectorの合計や平均値を出す(std::accumulateの第3引数について考察もあり)

配列やVectorなどの合計や平均値を求めたい場合に"std::accumulate"という関数を使うと比較的簡単に求められます.

これから,基本的な使い方と,自分がはまったところを説明します.
まず,簡単な使い方は下のコードを見てください.

#include <iostream>
#include <vector>
#include <numeric>

int main(void)
{
  std::vector vec;

  for( int i = 0; i <= 10; i++){
    vec.push_back( i );
  }
    std::cout << "sum = " << std::accumulate(vec.begin(), vec.end(), 0) << std::endl;
    std::cout << "avg = " << std::accumulate(vec.begin(), vec.end(), 0) / vec.size() << std::endl;
}

実行結果はこのようになります.

sum = 55
avg = 5

これを見るとわかると思いますが,
"std::accumulate"の第一引数には配列やVectorの足し始め,
第二引数には配列やVectorの足し終わりを書くと,
その間をすべて足した値が戻り値で帰ってくる.
for文を回して自分で足していくのはミスの元だし,コードが長くなってしまうので,こっちのほうがいいでしょう.

しかし,この関数には第三引数も入れる必要があり,上の例では"0"が入っています.
ネットのリファレンスなどを見るとこのように書いて有ります.
init           initial value of the sum 
std::accumulate - cppreference.com
http://en.cppreference.com/w/cpp/algorithm/accumulate

accumulate - C++ Reference
http://www.cplusplus.com/reference/numeric/accumulate/

つまり,「初期値」です,
この扱いについてはまってしまいました.

説明の前に次のコードを見てください.

#include <iostream>
#include <vector>
#include <numeric>

int main(void)
{
  std::vector<double> vec;

  for( double i = 0.9; i < 5;i++){
    vec.push_back( i );
    std::cout << i << std::endl;
  }

  std::cout << "##########" << std::endl;

  std::cout << "sum1 = " << std::accumulate(vec.begin(), vec.end(), 0) << std::endl;
  std::cout << "sum2 = " << std::accumulate(vec.begin(), vec.end(), 0.0) << std::endl;
  std::cout << "avg1 = " << std::accumulate(vec.begin(), vec.end(), 0) / vec.size() << std::endl;
  std::cout << "avg2 = " << std::accumulate(vec.begin(), vec.end(), 0.0) / vec.size() << std::endl;
}

これを実行すると次のようになります.
0.9
1.9
2.9
3.9
4.9
##########
sum1 = 10
sum2 = 14.5
avg1 = 2
avg2 = 2.9

まず,作っている配列についてですが,はじめに表示された数字がVectorに入っているので,
"std::accumulate"で合計を出したときに出てきて欲しい答えは
0.9 + 1.9 + 2.9 + 3.9 + 4.9 = "14.5"
です.
"sum2"は予想通り14.5になっていますが,
"sum1"は"10"になっています.
この2つの違いは第三引数が"0"か"0.0"かという事です.

つまり,第三引数のinitの値は合計を計算する際の型に関係するという事です.
"0.0"を入れると小数まで計算するという事なので,14.5という戻り値が帰ってきますが,
"0"を入れると整数で計算してしまうので,
0.9 + 1.9 + 2.9 + 3.9 + 4.9
ではなく,整数で丸めた
0 + 1 + 2 + 3 + 4 = 10
という答えが帰ってきてしまうのです.

なので,平均を出すときも合計が間違っているのでavg1は出てきて欲しい答えとは違ってきてしまいます.

よって,合計求める配列やVectorの要素が"int"なら0で良いですが,
"double"や"float"のときは0.0としましょう.

0 件のコメント:

コメントを投稿