std::reduce
From cppreference.com
| ヘッダー <numeric> で定義 |
||
| template< class InputIt > typename std::iterator_traits<InputIt>::value_type |
(1) | (C++17以降) (C++20 以降 constexpr) |
| template< class ExecutionPolicy, class ForwardIt > typename std::iterator_traits<ForwardIt>::value_type |
(2) | (C++17以降) |
| template< class InputIt, class T > T reduce( InputIt first, InputIt last, T init ); |
(3) | (C++17以降) (C++20 以降 constexpr) |
| template< class ExecutionPolicy, class ForwardIt, class T > T reduce( ExecutionPolicy&& policy, |
(4) | (C++17以降) |
| template< class InputIt, class T, class BinaryOp > T reduce( InputIt first, InputIt last, T init, BinaryOp op ); |
(5) | (C++17以降) (C++20 以降 constexpr) |
| template< class ExecutionPolicy, class ForwardIt, class T, class BinaryOp > |
(6) | (C++17以降) |
1) reduce(first, last, typename std::iterator_traits<InputIt>::value_type{}) と等価。
3) reduce(first, last, init, std::plus<>()) と等価。
5) 未指定の方法で並べ替えられ、集計された範囲
[first, last) を、初期値 init と共に op を使用して削減する。2,4,6) (1,3,5) と同じですが、policy に従って実行されます。
これらのオーバーロードは、以下のすべての条件が満たされた場合にのみオーバーロード解決に参加する。
|
std::is_execution_policy_v<std::decay_t<ExecutionPolicy>> は true です。 |
(C++20まで) |
|
std::is_execution_policy_v<std::remove_cvref_t<ExecutionPolicy>> は true です。 |
(C++20以降) |
実際のバイナリ操作として binary_op が与えられた場合
- binary_op が結合法則を満たさない、または交換法則を満たさない場合(浮動小数点数加算など)、結果は非決定的になる。
- 以下のいずれかの値が
Tに変換できない場合、プログラムは不正となる
- binary_op(init, *first)
- binary_op(*first, init)
- binary_op(init, init)
- binary_op(*first, *first)
- 次のいずれかの条件が満たされる場合、動作は未定義です。
-
Tは MoveConstructible ではない。 - binary_op が
[first,last)のいずれかの要素を変更する。 - binary_op が
[first,last]の任意のイテレータまたはサブレンジを無効にする。
-
目次 |
[編集] パラメーター
| first, last | - | アルゴリズムを適用する要素の 範囲 を定義するイテレータのペア |
| init | - | 一般化された合計の初期値 |
| policy | - | 使用する 実行ポリシー |
| op | - | 入力イテレータの参照解除の結果、他の op の結果、および init に対して未指定の順序で適用される二項 FunctionObject。 |
| 型要件 | ||
-InputIt は LegacyInputIterator の要件を満たす必要があります。 | ||
-ForwardIt は LegacyForwardIterator の要件を満たさなければなりません。 | ||
[編集] 戻り値
5,6) init と
[first, last) の要素の、op による一般化された合計。二項演算 binary_op による要素のグループの一般化された合計は、次のように定義される。
- グループに1つの要素しかない場合、合計はその要素の値となる。
- そうでない場合、次の操作を順に実行します。
- グループから任意の2つの要素 elem1 と elem2 を取る。
- binary_op(elem1, elem2) を計算し、結果をグループに戻す。
- グループに1つの要素しか残らないまで、ステップ1と2を繰り返す。
[編集] 計算量
std::distance(first, last) を N とする
5,6) op の適用回数は O(N)。
[編集] 例外
ExecutionPolicy というテンプレートパラメータを持つオーバーロードは、次のようにエラーを報告します。
- アルゴリズムの一部として呼び出された関数の実行が例外をスローし、
ExecutionPolicyが 標準ポリシー のいずれかである場合、std::terminate が呼び出されます。その他のExecutionPolicyの場合、動作は実装定義です。 - アルゴリズムがメモリの割り当てに失敗した場合、std::bad_alloc がスローされます。
[編集] 注釈
std::reduce は std::accumulate と同様に動作するが、範囲の要素が任意の順序でグループ化および再配置される可能性がある点が異なる。
[編集] 例
std::reduce と std::accumulate の比較
このコードを実行
#if PARALLEL #include <execution> #define SEQ std::execution::seq, #define PAR std::execution::par, #else #define SEQ #define PAR #endif #include <chrono> #include <iomanip> #include <iostream> #include <locale> #include <numeric> #include <utility> #include <vector> int main() { std::cout.imbue(std::locale("en_US.UTF-8")); std::cout << std::fixed << std::setprecision(1); auto eval = [](auto fun) { const auto t1 = std::chrono::high_resolution_clock::now(); const auto [name, result] = fun(); const auto t2 = std::chrono::high_resolution_clock::now(); const std::chrono::duration<double, std::milli> ms = t2 - t1; std::cout << std::setw(28) << std::left << name << "sum: " << result << '\t' << "time: " << ms.count() << " ms\n"; }; { const std::vector<double> v(100'000'007, 0.1); eval([&v]{ return std::pair{"std::accumulate (double)", std::accumulate(v.cbegin(), v.cend(), 0.0)}; }); eval([&v]{ return std::pair{"std::reduce (seq, double)", std::reduce(SEQ v.cbegin(), v.cend())}; }); eval([&v]{ return std::pair{"std::reduce (par, double)", std::reduce(PAR v.cbegin(), v.cend())}; }); } { const std::vector<long> v(100'000'007, 1); eval([&v]{ return std::pair{"std::accumulate (long)", std::accumulate(v.cbegin(), v.cend(), 0l)}; }); eval([&v]{ return std::pair{"std::reduce (seq, long)", std::reduce(SEQ v.cbegin(), v.cend())}; }); eval([&v]{ return std::pair{"std::reduce (par, long)", std::reduce(PAR v.cbegin(), v.cend())}; }); } }
実行結果の例
// POSIX: g++ -std=c++23 ./example.cpp -ltbb -O3; ./a.out std::accumulate (double) sum: 10,000,000.7 time: 356.9 ms std::reduce (seq, double) sum: 10,000,000.7 time: 140.1 ms std::reduce (par, double) sum: 10,000,000.7 time: 140.1 ms std::accumulate (long) sum: 100,000,007 time: 46.0 ms std::reduce (seq, long) sum: 100,000,007 time: 67.3 ms std::reduce (par, long) sum: 100,000,007 time: 63.3 ms // POSIX: g++ -std=c++23 ./example.cpp -ltbb -O3 -DPARALLEL; ./a.out std::accumulate (double) sum: 10,000,000.7 time: 353.4 ms std::reduce (seq, double) sum: 10,000,000.7 time: 140.7 ms std::reduce (par, double) sum: 10,000,000.7 time: 24.7 ms std::accumulate (long) sum: 100,000,007 time: 42.4 ms std::reduce (seq, long) sum: 100,000,007 time: 52.0 ms std::reduce (par, long) sum: 100,000,007 time: 23.1 ms
[編集] 関連項目
| 範囲の要素を合計または畳み込む (関数テンプレート) | |
| 範囲内の要素に関数を適用し、結果を結果範囲に格納する (関数テンプレート) | |
| (C++17) |
呼び出し可能オブジェクトを適用し、順序不同に縮約する (関数テンプレート) |
| (C++23) |
要素の範囲を左畳み込みする (アルゴリズム関数オブジェクト) |