第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) だと初期値 0 が int なので、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, ...} で埋めます。インデックス配列の生成や循環チェック等に活用。