Spec-Zone.ru › C++

std::reduce

Определено в заголовке <numeric>
(1)
template< class InputIt >
typename std::iterator_traits<InputIt>::value_type
    reduce( InputIt first, InputIt last );
(с C++17)
(до C++20)
template< class InputIt >
constexpr typename std::iterator_traits<InputIt>::value_type
    reduce( InputIt first, InputIt last );
(с C++20)
template< class ExecutionPolicy, class ForwardIt >
typename std::iterator_traits<ForwardIt>::value_type
    reduce( ExecutionPolicy&& policy,
            ForwardIt first, ForwardIt last );
(2) (с C++17)
(3)
template< class InputIt, class T >
T reduce( InputIt first, InputIt last, T init );
(с C++17)
(до C++20)
template< class InputIt, class T >
constexpr T reduce( InputIt first, InputIt last, T init );
(с C++20)
template< class ExecutionPolicy, class ForwardIt, class T >
T reduce( ExecutionPolicy&& policy,
          ForwardIt first, ForwardIt last, T init );
(4) (с C++17)
(5)
template< class InputIt, class T, class BinaryOp >
T reduce( InputIt first, InputIt last, T init, BinaryOp binary_op );
(с C++17)
(до C++20)
template< class InputIt, class T, class BinaryOp >
constexpr T reduce( InputIt first, InputIt last, T init, BinaryOp binary_op );
(с C++20)
template< class ExecutionPolicy, class ForwardIt, class T, class BinaryOp >
T reduce( ExecutionPolicy&& policy,
          ForwardIt first, ForwardIt last, T init, BinaryOp binary_op );
(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 по binary_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 изменяет любой элемент или делает недействительным любой итератор в [first, last), включая конечный итератор.

Параметры

first, last - диапазон элементов, к которым будет применено алгоритм
init - начальное значение обобщённой суммы
policy - стратегия выполнения. Подробнее см. стратегия выполнения.
binary_op - бинарный Функциональный объект, который будет применяться в неопределённом порядке к результату обращения по ссылке к входным итераторам, результатам других binary_op и init.
Требования к типу
-InputIt должно соответствовать требованиям ЛегасиInputIterator.
-ForwardIt должно соответствовать требованиям ЛегасиForwardIterator.
-T должно соответствовать требованиям MoveConstructible. и binary_op(init, *first), binary_op(*first, init), binary_op(init, init), и binary_op(*first, *first) должны быть преобразуемы к T.

Возвращаемое значение

Обобщённая сумма init и *first, *(first + 1), ... *(last - 1) по binary_op,

где обобщённая сумма GSUM(op, a1, ..., aN) определяется следующим образом:

  • если N = 1, a1
  • если N > 1, op(GSUM(op, b1, ..., bK), GSUM(op, bM, ..., bN)) где
    • b1, ..., bN могут быть любой перестановкой a1, ..., aN и
    • 1 < K + 1 = M ≤ N

другими словами, reduce ведет себя как std::accumulate за исключением того, что элементы диапазона могут быть сгруппированы и переупорядочены произвольным образом

Сложность

O(last - first) применений binary_op.

Исключение

Перегрузки с параметром шаблона ExecutionPolicy сообщают об ошибках следующим образом:

  • Если выполнение функции, вызванной как часть алгоритма, выбрасывает исключение, и ExecutionPolicy является одной из стандартных стратегий, std::terminate вызывается. Для любой другой ExecutionPolicy, поведение определяется реализацией.
  • Если алгоритм не может выделить память, std::bad_alloc выбрасывается.

Примечания

Если диапазон пуст, возвращается init, не изменённая.

Пример

сравнение 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 <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

См. также

accumulate
суммирует или сворачивает диапазон элементов
(шаблон функции)
transform
применяет функцию к диапазону элементов, сохраняя результаты в целевом диапазоне
(шаблон функции)
transform_reduce
(C++17)
применяет вызываемый объект, затем сворачивает вне очереди
(шаблон функции)
ranges::fold_left
(C++23)
сворачивает слева диапазон элементов
(niebloid)

© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/w/cpp/algorithm/reduce

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API