C++ 標準ライブラリ<numeric>

日頃の C++ 開発では、以下のような要件に遭遇することがよくあります。

  • 配列の合計 / 総乗を求める
  • 区間クエリのための前方和(プレフィックス和)を計算する
  • 一括累積による新しいシーケンスの生成
  • ベクトル間の内積(ドット積)を計算する

もし今でも手書きでforループを書いているなら、<numeric>こそが、あなたが身につけるべき標準ライブラリのツールです。

C++ 標準ライブラリの<numeric>ヘッダーファイルは、数値計算用の関数テンプレート群を提供します。これらの関数は、コンテナ内の要素に対して合計、積、最小値、最大値などのさまざまな数値操作を行うことができます。これらの関数テンプレートは非常に強力で、配列、ベクトル、リストなどのあらゆるタイプのコンテナに適用できます。

を使用する際は、<numeric>ヘッダーファイル内の関数を使用する前に、C++ プログラムにこのヘッダーファイルを含める必要があります:

#include <numeric>

よく使う関数

  • <numeric>は C++ における数値シーケンス計算の中心となるヘッダーファイルであり、リダクション、累積、差分などの操作に特化し、すべての STL コンテナと互換性があります;
  • 基本関数(accumulate/partial_sum/iota)は C++98/11 の中核であり、通常の数値計算の大半のニーズを満たします;
  • C++17 で追加されたreduce/inclusive_scanなどの並列関数は、大規模データでの計算効率を大幅に向上させることができ、並列実行ポリシーと組み合わせて使用する必要があります。
関数名 C++ バージョン コア機能 簡略化した関数プロトタイプ(vector<int>を例に) 代表的な適用シナリオ
accumulate C++98 シーケンスの累積和を計算します(二項演算をカスタマイズ可能) int accumulate(iterator beg, iterator end, int init); 配列の合計を求める、累積値の統計
inner_product C++98 2つのシーケンスの内積を計算します(対応する要素を掛けてから累積、操作をカスタマイズ可能) int inner_product(iter1 b1, iter1 e1, iter2 b2, int init); ベクトルのドット積、加重合計
partial_sum C++98 シーケンスの前方和を計算します(n番目の結果 = 先頭から n 個の要素の合計、操作をカスタマイズ可能) void partial_sum(iter b, iter e, iter res); 累積と配列の生成、累積統計
adjacent_difference C++98 シーケンスの隣接要素間の差を計算します(n番目の結果 = elem[n] - elem[n-1]、操作をカスタマイズ可能) void adjacent_difference(iter b, iter e, iter res); 差分の計算、シーケンスの変化量の検出
iota C++11 連続的に増加する値でシーケンスを埋めます(init から始めて、1つずつ +1) void iota(iter b, iter e, T init); 連続する整数シーケンスの生成、順序付きコンテナの初期化
reduce C++17 並列版の累積(accumulate に類似、並列実行をサポート、順序なしリダクション) T reduce(iter b, iter e, T init = T{}); 大規模データの並列合計、計算効率の向上
transform_reduce C++17 変換+リダクション(要素を変換してから累積、並列をサポート) T transform_reduce(iter1 b1, iter1 e1, iter2 b2, T init); 並列での内積計算、変換後の合計
inclusive_scan C++17 並列版の前方和(現在の要素を含む、partial_sum に類似、並列をサポート) void inclusive_scan(iter b, iter e, iter res); 累積と配列の並列生成
exclusive_scan C++17 並列版の前方和(現在の要素を含まない、n番目の結果 = 先頭から n-1 個の要素の合計) void exclusive_scan(iter b, iter e, iter res, T init); 「自身を含まない」前方和の並列生成

1. accumulate

accumulate関数は、コンテナ内のすべての要素の合計を計算するために使用されます。3つの引数(コンテナの開始イテレータ、終了イテレータ、初期値)を受け取ります。

構文:

template <InputIterator Iter, class T>
T accumulate(Iter first, Iter last, T init);

実例:

実例

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

int main() {
    std::vector<int> v = {1, 2, 3, 4, 5};
    int sum = std::accumulate(v.begin(), v.end(), 0);
    std::cout << "Sum: " << sum << std::endl; // 出力: Sum: 15
    return 0;
}

2. inner_product

inner_product関数は、2つのコンテナ内の対応する要素の積の合計を計算するために使用されます。

構文:

template <InputIterator1 Iter1, InputIterator2 Iter2, class T>
T inner_product(Iter1 first1, Iter1 last1, Iter2 first2, T init);

実例:

実例

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

int main() {
    std::vector<int> v1 = {1, 2, 3};
    std::vector<int> v2 = {4, 5, 6};
    int product_sum = std::inner_product(v1.begin(), v1.end(), v2.begin(), 0);
    std::cout << "Product Sum: " << product_sum << std::endl; // 出力: Product Sum: 32
    return 0;
}

3. partial_sum

partial_sum関数は、コンテナ内の要素の部分和を計算し、結果を別のコンテナに格納するために使用されます。

構文:

template <InputIterator InIter, OutputIterator OutIter>
OutIter partial_sum(InIter first, InIter last, OutIter result);

実例:

実例

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

int main() {
    std::vector<int> v = {1, 2, 3, 4};
    std::vector<int> result(v.size());
    std::partial_sum(v.begin(), v.end(), result.begin());
    for (int i : result) {
        std::cout << i << " "; // 出力: 1 3 6 10
    }
    return 0;
}

4. adjacent_difference

adjacent_difference関数は、コンテナ内の隣接要素間の差を計算し、結果を別のコンテナに格納するために使用されます。

構文:

template <InputIterator InIter, OutputIterator OutIter>
OutIter adjacent_difference(InIter first, InIter last, OutIter result);

実例:

実例

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

int main() {
    std::vector<int> v = {1, 2, 3, 4};
    std::vector<int> result(v.size() - 1);
    std::adjacent_difference(v.begin(), v.end(), result.begin());
    for (int i : result) {
        std::cout << i << " "; // 出力: 1 1 1
    }
    return 0;
}

5. std::gcd

std::gcd を使用して2つの整数の最大公約数を計算します:

実例

#include <iostream>
#include <numeric>

int main() {
    int a = 48;
    int b = 18;
    int result = std::gcd(a, b);  // 48 と 18 の最大公約数を計算
    std::cout << "GCD: " << result << std::endl;  // 6 を出力
    return 0;
}

6. std::lcm

std::lcm を使用して2つの整数の最小公倍数を計算します:

実例

#include <iostream>
#include <numeric>

int main() {
    int a = 48;
    int b = 18;
    int result = std::lcm(a, b);  // 48 と 18 の最小公倍数を計算
    std::cout << "LCM: " << result << std::endl;  // 144 を出力
    return 0;
}

7. std::iota

実例

#include <iostream>
#include <numeric> // numeric ヘッダーファイルを含める
#include <vector>

int main() {
    std::vector<int> v(5); // 5つの要素を含むベクトルを作成

    // std::iota を使用してベクトルを埋める、開始値は1
    std::iota(std::begin(v), std::end(v), 1);

    // 埋めた後のベクトルを出力
    for (int i : v) {
        std::cout << i << " ";
    }
    std::cout << std::endl; // 出力: 1 2 3 4 5

    return 0;
}

std::iota を使用して、範囲内のシーケンス値を埋めます。

template<class ForwardIt, class T>
void iota(ForwardIt first, ForwardIt last, T value);

8. 最大値と最小値の検索

min_elementとmax_element関数は、コンテナ内の最大値と最小値を見つけるために使用されます。

実例

#include <iostream>
#include <numeric>
#include <vector>
#include <algorithm> // std::min_element と std::max_element を使用するため

int main() {
    // 整数を含むベクトルを定義
    std::vector<int> v = {3, 1, 4, 1, 5, 9};

    // 最小値と最大値を計算
    int min_val = *std::min_element(v.begin(), v.end());
    int max_val = *std::max_element(v.begin(), v.end());

    // 合計を計算
    int sum_val = std::accumulate(v.begin(), v.end(), 0);

    // 平均値を計算
    double avg_val = static_cast<double>(sum_val) / v.size();

    // 結果を出力
    std::cout << "Min: " << min_val << std::endl;
    std::cout << "Max: " << max_val << std::endl;
    std::cout << "Sum: " << sum_val << std::endl;
    std::cout << "Average: " << avg_val << std::endl;

    return 0;
}

出力結果は次のとおりです:

Min: 1
Max: 9
Sum: 23
Average: 3.83333
その他の拡張