C++ Learning

第57回 数値系アルゴリズム ★目玉

<numeric> ヘッダの数値計算アルゴリズム。accumulate(総和)、reduce(並列対応)、inner_product(内積)、partial_sum(累積和)、iota(連番生成)、adjacent_difference(差分)。

このページで押さえること
✅ 最低限
  • std::accumulate ― 総和(Python の sum/reduce)
  • std::reduce ― 並列可能な accumulate (C++17)
  • std::iota ― 連番で埋める
  • std::partial_sum ― 累積和
⭐ 余裕があれば
  • inner_product ― 内積
  • adjacent_difference ― 差分
  • 並列実行ポリシー std::execution::par
  • 初期値に注意(double vs int)

1. 一覧

関数ヘッダ用途
std::accumulate<numeric>総和・総積など畳み込み
std::reduce<numeric>accumulate の並列可能版 (C++17)
std::inner_product<numeric>2 つの列の内積
std::partial_sum<numeric>累積和
std::adjacent_difference<numeric>隣接要素の差分
std::iota<numeric>初期値から連番で埋める

2. accumulate ― 畳み込み

accumulate総和
#include <numeric> std::vector<int> v = {1,2,3,4,5}; // 総和(初期値 0) int s = std::accumulate(v.begin(), v.end(), 0); // 15 // 総積(初期値 1 + 乗算) int p = std::accumulate(v.begin(), v.end(), 1, [](int acc, int x){ return acc * x; }); // 120 // 文字列の連結 std::vector<std::string> words = {"Hello", " ", "World"}; auto joined = std::accumulate(words.begin(), words.end(), std::string{}); // "Hello World"
初期値の型に注意: accumulate(v.begin(), v.end(), 0) だと初期値 0int なので、double の vector でも結果が int に丸められる。0.0 を渡すこと。

3. reduce ― C++17 の並列版

reduceC++17 / 並列可
#include <numeric> #include <execution> // 並列実行(順序非依存の演算のみ) auto s = std::reduce(std::execution::par, v.begin(), v.end(), 0); // accumulate との違い: // - 順序が保証されない(並列化のため) // - 結合則を満たす演算のみ(+ / * は OK、- / 文字列連結は NG)

4. inner_product / partial_sum / adjacent_difference

inner_product内積
std::vector<int> a = {1,2,3}; std::vector<int> b = {4,5,6}; int dot = std::inner_product( a.begin(), a.end(), b.begin(), 0); // 1*4 + 2*5 + 3*6 = 32
partial_sum累積和
std::vector<int> v = {1,2,3,4}; std::vector<int> out(4); std::partial_sum(v.begin(), v.end(), out.begin()); // out = {1, 3, 6, 10}
adjacent_difference差分
std::vector<int> v = {1,3,6,10}; std::vector<int> out(4); std::adjacent_difference(v.begin(), v.end(), out.begin()); // out = {1, 2, 3, 4} (先頭はそのまま、以降は前との差)

5. iota ― 連番で埋める

iota連番
std::vector<int> v(10); std::iota(v.begin(), v.end(), 1); // v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10} // インデックスの配列を作るときによく使う std::vector<size_t> idx(data.size()); std::iota(idx.begin(), idx.end(), 0); // idx を使って他の値に基づくソート等

確認クイズ

Q1. std::accumulate(v.begin(), v.end(), 0)(v は vector<double>)の結果は?

double の総和
初期値が int なので結果が int に丸められる
コンパイルエラー
自動で double に推論される
初期値の型が結果の型を決めるので、0.0 を渡すのが正解。vector<double>0 を渡すと結果が int に丸められ、情報が欠落します。

Q2. 並列実行で総和を取りたい場合に使うのは?

std::accumulate
std::reduce(std::execution::par, ...)
std::for_each
std::sort
C++17 の std::reduce は並列実行ポリシーを渡せます。ただし演算が結合則を満たす必要あり。accumulate は順序依存なので並列化できない。

Q3. {1,2,3,4} の累積和を出力するには?

accumulate
adjacent_difference
partial_sum
iota
partial_sum が累積和(プレフィックス合計)。結果は {1, 3, 6, 10}。accumulate は最終値だけ返す。

Q4. std::iota の用途は?

総和計算
配列を 0 で埋める
連番(初期値から +1 ずつ)で埋める
逆順にする
iota はギリシャ文字 ι(イオタ)から。配列を {n, n+1, n+2, ...} で埋めます。インデックス配列の生成や循環チェック等に活用。
← 前の講座
第56回 変換系