std::reduce详解

基础介绍

reuduce的含义是规约,即将一系列元素通过某种操作组合成单个结果的过程。c++17引入了std::reduce函数。std::reduce与std::accumulate类似,不同的是std::reduce采用的并行方式进行运算,所谓并行就是在多个线程同时进行运算得到的结果的过程;std::accumulate是串行方式进行得到一个累积和。所以从这个角度来看,当数据量比较小时优先采用std::accumulate完成求和运算。当数据量比较大时,推荐采用std::reduce来完成计算。

std::reduce函数特点

  1. 默认是求和运算,初始值为0
  2. 可以指定初始值
  3. 可以指定运算方法
  4. 可以指定计算策略

依赖的头文件:#include <numeric>

函数原型

最简单的形式

template<class InputIt>
typename iterator_traits<InputIt>::value_type
reduce(InputIt first, InputIt last);

特征:

  • 该重载函数仅有2个参数,这两个参数一般为容器迭代器
  • 该重载函数的初始值为0
  • 该重载函数的操作符为加法操作符(+)

示例:

#include <numeric>
#include <iostream>
#include <vector>
using namespace std;

void example()
{
    vector<int> vec{1,2,3};
    int sum = std::reduce(vec.begin(), vec.end());
    cout <<"sum = " <<sum<<endl;  //输出为6 1+2+3=6
}

带初始值版本

template<class InputIt, class T>
T reduce(InputIt first, InputIt last, T init);

特征:

  • 在前面简单版本的基础上多了一个参数初始值,其余与简单版本相同:初始值的含义就是提供一个起始值,简单版本的初始值是0,请看下面的例子

示例:

#include <numeric>
#include <iostream>
#include <vector>
using namespace std;

void example()
{
    vector<int> vec{1,2,3};
    int sum = std::reduce(vec.begin(), vec.end(), 5);
    cout <<"sum = " <<sum<<endl;  //输出为11,5+1+2+3=11
}

完整版本

template<class InputIt, class T, class BinaryOp>
T reduce(InputIt first, InputIt last, T init, BinaryOp binary_op);

特征:

  • 在带初始值版本的基础上,允许指定二元操作符,前面的版本默认都是加法操作符,而这个八本可以指定操作符,但是不能随意指定二元操作符,指定的二元操作符必须符合交换律和结合律。减法不满足交换律(a-b != b-a)

示例:

#include <numeric>
#include <iostream>
#include <vector>
using namespace std;

void example()
{
    vector<int> vec{1,2,3};
    int result = std::reduce(vec.begin(), vec.end(), 5, std::multiplies<>());
    cout <<"result = " <<result <<endl;  //输出为30,5x1x2x3=30
}

执行策略重载版本

除上面外,还存在指定执行策略的重载版本,与前面类似,仅仅是在第一个参数指定执行策略。请看下面的函数原型:

//基本执行策略,除可以指定执行策略外,其余功能与简单版本相同
template<class ExecutionPolicy, class ForwardIt>
typename iterator_traits<ForwardIt>::value_type
reduce(ExecutionPolicy&& policy, ForwardIt first, ForwardIt last);

//带初始值的执行策略版本,其余功能与带初始值版本相同
template<class ExecutionPolicy, class ForwardIt, class T>
T reduce(ExecutionPolicy&& policy, 
         ForwardIt first, ForwardIt last, T init);

//完整执行策略版本,相对于完整版本多了一个参数,该参数用于指定执行策略,其余相同
template<class ExecutionPolicy, 
         class ForwardIt, class T, class BinaryOp>
T reduce(ExecutionPolicy&& policy,
         ForwardIt first, ForwardIt last,
         T init, BinaryOp binary_op);

执行策略种类如下:

// 可用的执行策略
auto sum1 = std::reduce(std::execution::seq,      // 顺序执行
                       v.begin(), v.end());
auto sum2 = std::reduce(std::execution::par,      // 并行执行
                       v.begin(), v.end());
auto sum3 = std::reduce(std::execution::par_unseq,// 并行+向量化
                       v.begin(), v.end()); 

高级用法

前一个章节的示例代码都是采用基本数据类型int,std::reduce也支持自定义类型的进行运算。请看下面的示例:

struct Number {
    int value;
    
    // 必须定义加法操作符
    Number operator+(const Number& other) const {
        return Number{value + other.value};
    }
};

std::vector<Number> numbers = {{1}, {2}, {3}};
Number result = std::reduce(numbers.begin(), numbers.end(),
                          Number{0});

从上面的例子可以看出,自定义类型Number,容器中的元素都是Number类型,初始值也是Nmber{0},通过重载操作符+实现数据折叠。

应用场景分析

  • 数值计算场景:大数据量的数值运算推荐使用std::reduce,例如向量点积运算
  • 并行运算场景
  • 科学计算运用:矩阵计算

总而言之,reduce函数的应用场景一般都是在大数据量的情况下的使用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值