一、摘要

C++的<numeric>头文件中包含了一系列可用于操作数值序列(sequences of numeric value)的函数,通过修改函数的参数类型也可以将这些函数应用到非数值序列中。熟练使用<numeric>中的函数可以方便地对例如vector<>,list<>等容器进行数值计算。
在C++11标准下,<numeric>中主要包括accumulate(),adjacent_difference(),inner_product(),partial_sum()iota()这五个函数,C++17又增加了gcd(),lcm()和midpoint()等函数。
本文将基于C++11标准主要讲解前五个函数的用法以及注意事项,囿于篇幅限制C++17标准中新增的函数本文不做过多介绍,更详尽的介绍读者可以参考Standard library header < numeric >。

本文第二部分是对accumulate(),adjacent_difference(),inner_product(),partial_sum()iota()五个函数的介绍和使用注意事项。第三部分简要提及了C++17中新增的几个常用的函数,本文第四部分列出了本文参考的文章链接。

二、C++11标准<numeric>头文件内函数简介

1. accumulate()函数介绍

accumulate()函数返回数组内元素的累积值。
例如,可以使用accumulate()函数计算vector<int> nums中所有元素的累加和,通过修改操作符(binary_op)可以计算累乘积或其他自定义累计结果。

(1).函数定义

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

first, last: 容器迭代器,计算范围;

init: 初始值,默认为1。若初始值init=2,那么accumulate(nums.begin(),nums.end(),2) 返回 2+nums[0]+nums[1]+…+nums[n-1];

binary_op: 元素间的操作符号,默认为plus<T>(求和)。

(2).用法示例

a. 基本用法,求vector<int> nums的元素累计和、累计减、累计乘、累计除

代码:

#include <iostream>
#include <vector>
#include <numeric>
using namespace std;
int main(int, char**) {vector<int> nums = { 6,3,2 };// 默认求和,输出为:0+6+3+2 = 11cout << accumulate(nums.begin(), nums.end(), 0) << endl;// 初始值为1求和,输出为:1+6+3+2 = 12cout << accumulate(nums.begin(), nums.end(), 1) << endl;// 求累减,输出为:0-6-3-2 = -11cout << accumulate(nums.begin(), nums.end(), 0, minus<int>()) << endl;// 求累乘,输出为:1*6*3*2 = 36cout << accumulate(nums.begin(), nums.end(), 1, multiplies<int>()) << endl;// 求累除,输出为:36/6/3/2 = 1cout << accumulate(nums.begin(), nums.end(), 36, divides<int>()) << endl;return 0;
}

输出:

11
12
-11
36
1

b. 自定义操作符binary_op

1.所示,可以设置二元操作符为plus,minus,multipliesdivides对容器内的元素进行加、减、乘和除的accumulate操作。

同样的,我们可以使用函数指针函数对象lambda表达式自定义一个二元运算符对元素进行accumulate操作。

接下来我们定义一个 求和后再模3 的二运运算符 add_mod_3_fun,即add_mod_3_fun(a,b) = (a+b)%3,然后使用该二元运算符对nums进行accumulate操作。

代码如下:

#include <iostream>
#include <vector>
#include <numeric>
using namespace std;int add_mod_3_fun(const int& a, const int& b) {return (a + b) % 3;
}
class ADD_MOD_3 {public:int operator()(const int& a, const int& b) {return (a + b) % 3;}
};int main(int, char**) {vector<int> nums = { 6,3,2 };// 使用自定义二元运算符// 使用函数指针,输出为 (0+6)%3=0 -> (0+3)%3=0 -> (0+2)%3 = 2cout << accumulate(nums.begin(), nums.end(), 0, add_mod_3_fun) << endl;// 使用函数对象cout << accumulate(nums.begin(), nums.end(), 0, ADD_MOD_3()) << endl;// 使用lambda表达式cout << accumulate(nums.begin(), nums.end(), 0, [](const int& a, const int& b) {return (a + b) % 3;}) << endl;return 0;
}

输出:

2
2
2

需要注意的是,在使用函数指针、函数对象或lambda表达式自定义运算符号时,参数列表应该为const int&类型以保证acumulate()函数不会对数组内原有元素做任何修改,尽管不为const int&类型依旧可以正常运行,但是为了安全考虑还是需要尽量使用const int&

2.adjacent_difference()函数介绍

从名字也可以看出adjacent_difference()是用来计算数组各元素与相邻元素(这里的相邻指的是前一个元素)之间差异的函数。
例如:若nums为待计算数组,result为存放差异结果的数组,那么:

result[0] = nums[0]
result[1] = nums[1] - nums[0]
result[2] = nums[2] - nums[1]
result[3] = nums[3] - nums[2]
result[4] = nums[4] - nums[3]
...

可以看到,由于数组首个元素没有前一个元素,函数规定数组首个元素前一个元素的差异为首个元素本身。另外在计算差异时,默认使用-减法运算,即differenc[i]=nums[i]-nums[i-1]

(1).函数定义

template <class InputIterator, class OutputIterator>OutputIterator adjacent_difference (InputIterator first, InputIterator last,OutputIterator result);template <class InputIterator, class OutputIterator, class BinaryOperation>OutputIterator adjacent_difference ( InputIterator first, InputIterator last,OutputIterator result, BinaryOperation binary_op );

first, last:容器迭代器,计算范围;
result:迭代器,存储差异结果数组 起始位置;
binary_op:计算差异的运算符,默认为减法运算;
函数返回值:迭代器,存储差异结果数组的最后一个差异元素的下一个位置;

(2).用法示例

a. 基本用法,求vector<int>的各元素与前一个元素的差、和、积和商

代码:

#include <iostream>
#include <vector>
#include <numeric>
using namespace std;
int main(int, char**) {vector<int> nums = { 1,2,3,5,9,11,12 };cout << "nums:";for (int i = 0; i < nums.size(); i++) {cout << nums[i] << " ";}cout << endl;vector<int> results(nums.size());// 计算各元素与前一个元素的 差cout << "minus:";vector<int>::iterator iter = adjacent_difference(nums.begin(), nums.end(), results.begin());for (int i = 0; i < results.size();i++) {cout << results[i] << " ";}cout << endl;// 计算各元素与前一个元素的 和cout << "plus:";iter = adjacent_difference(nums.begin(), nums.end(), results.begin(), plus<int>());for (int i = 0; i < results.size();i++) {cout << results[i] << " ";}cout << endl;// 计算各元素与前一个元素的 乘cout << "multiplies:";iter = adjacent_difference(nums.begin(), nums.end(), results.begin(), multiplies<int>());for (int i = 0; i < results.size();i++) {cout << results[i] << " ";}cout << endl;// 计算各元素与前一个元素的 除cout << "divides:";iter = adjacent_difference(nums.begin(), nums.end(), results.begin(), divides<int>());for (int i = 0; i < results.size();i++) {cout << results[i] << " ";}cout << endl;return 0;
}

结果:

nums:1 2 3 5 9 11 12
minus:1 1 1 2 4 2 1
plus:1 3 5 8 14 20 23
multiplies:1 2 6 15 45 99 132
divides:1 2 1 1 1 1 1

从输出结果中我们可以看出,无论我们使用什么运算符(减、加、乘还是除)计算差异,数组首个元素前一个元素差异都为首个元素本身

b. 自定义操作符binary_op

accumulate()类似,adjacent_difference()函数中也可以使用函数指针、函数对象或lambda表达式自定义差异运算法则,此处不在赘述。

c. 注意事项

  • 在使用数组result存储nums差异结果时,需要保证result.size()>=nums.size()
  • adjacent_difference()函数的返回值指向的是 最后一个差异结果的下一个元素位置,而不是result数组的最后一个位置。
    例如,若nums.size()等于3,result.size()等于5,那么

    iter=adjacent_difference(nums.begin(), nums.end(), result.begin());
    

    之后,iter指向result的第4个元素位置。

3.inner_product()函数介绍

inner product直译成中文为内积,的确该函数的功能与向量计算中的内积运算类似,inner_product()函数可以计算两个数组(向量)的内积结果。

例如:
两个数组分别为nums_1 = {1,2,3}nums_2 = {2,3,4}。那么两数组的内积

inner_product(nums_1.begin(), nums_1.end(), nums_2.begin(),0);

即为:0 + (1*2)+(2*3)+(3*4) = 20

(1).函数定义

template <class InputIterator1, class InputIterator2, class T>T inner_product (InputIterator1 first1, InputIterator1 last1,InputIterator2 first2, T init);template <class InputIterator1, class InputIterator2, class T,class BinaryOperation1, class BinaryOperation2>T inner_product (InputIterator1 first1, InputIterator1 last1,InputIterator2 first2, T init,BinaryOperation1 binary_op1,BinaryOperation2 binary_op2);

first1,last1,first2:容器迭代器,分别代表第一个数组的起始位置、终止位置和第二个数组的起始位置;
init:初始值,最后与两数组的内积向加;
binary_op1:二元运符,内积操作中外部的运算符号,默认为加法运算;
binary_op2:二元运符,内积操作中内的运算符号,默认为乘法运算;

(2).用法示例

a. 基本用法,计算两个数组的内积

将数组nums_1,nums_2视作两个相同维度的向量,inner_product()函数可以计算这两个向量(数组)的内积。
代码:

#include <iostream>
#include <vector>
#include <numeric>
using namespace std;
int main(int, char**) {vector<int> nums_1 = { 1,2,3 };vector<int> nums_2 = { 2,4,6 };// 28 = 0 + (1*2)+(2*4)+(3*6) = 2+8+18 = 28cout << inner_product(nums_1.begin(), nums_1.end(), nums_2.begin(), 0) << endl;// 33 = 5 + (1*2)+(2*4)+(3*6) = 2+8+18 = 33cout << inner_product(nums_1.begin(), nums_1.end(), nums_2.begin(), 5) << endl;return 0;
}

输出:

28
33

b. 自定义内积运算法则

默认两个数组的内积运算为:先各个元素相乘(内部运算符,binary_op2)然后再相加(binary_op1)。我们可以通过自定义binary_op1binary_op2实现自定义的内积运算。示例如下:
代码:

#include <iostream>
#include <vector>
#include <numeric>
using namespace std;
int main(int, char**) {vector<int> nums_1 = { 1,2,3 };vector<int> nums_2 = { 2,4,6 };// 自定义内积操作// 外部运算依旧为加法,内部运算为 减法// -5 = 1 + (1-2)+(2-4)+(3-6) = -5cout << inner_product(nums_1.begin(), nums_1.end(), nums_2.begin(), 1, plus<int>(), minus<int>()) << endl;// 外部运算依旧为乘法,内部运算为 加法// 162 = 1*(1+2)*(2+4)*(3+6) = 3*6*9 = 162cout << inner_product(nums_1.begin(), nums_1.end(), nums_2.begin(), 1, multiplies<int>(), plus<int>()) << endl;return 0;
}

输出:

-5
162

4.partial_sum()函数介绍

partial_sum()函数可以计算数组的部分和,所谓部分和即为数组从首个元素到第i个元素的和。
例如:若nums为待计算数组,result为存放部分和的结果数组,那么:

result[0] = nums[0]
result[1] = nums[0] + nums[1]
result[2] = nums[0] + nums[1] + nums[2]
result[3] = nums[0] + nums[1] + nums[2] + nums[3]
result[4] = nums[0] + nums[1] + nums[2] + nums[3] + nums[4]
...

其实从以上示例中也可以看出,所谓的部分和即为数组的前缀和

(1).函数定义

template <class InputIterator, class OutputIterator>OutputIterator partial_sum (InputIterator first, InputIterator last,OutputIterator result);template <class InputIterator, class OutputIterator, class BinaryOperation>OutputIterator partial_sum (InputIterator first, InputIterator last,OutputIterator result,BinaryOperation binary_op);

first,last:容器迭代器,待计算的数组起始、终止位置;
result:容器迭代器,存放部分和结果数组的起始位置;
binary_op:二元运算符,计算部分和的运算法则;
返回值:容器迭代器,结果数组最后一个 部分和元素 的下一个位置;

(2).用法示例

a. 基本用法,计算数组的部分和(前缀和)

代码:

#include <iostream>
#include <vector>
#include <numeric>
using namespace std;
int main(int, char**) {vector<int> nums = { 1,2,3,4,5 };vector<int> results(nums.size(), 0);vector<int>::iterator iter = partial_sum(nums.begin(), nums.end(), results.begin());for (int i = 0; i < results.size(); i++) {cout << results[i] << " ";}cout << endl;return 0;
}

输出:

1 3 6 10 15

b. 自定义操作符binary_op

与前面accumulate(), adjacent_difference()函数类似,可以使用函数指针,函数对象lambda表达式自定义求部分和的运算符号,此处不在赘述。
**需要注意的是:**结果容器results和原数组nums内的元素必须相同。
例如:假如nums内元素为pair<int, int>类型,我们需要计算nums[i].second的前缀和,那么results数组内的元素也必须为pair<int, int>类型,代码如下:

vector<pair<int, int>> nums;
for(int i=0; i<5; i++){nums.push_back(pair<int, int>(i, 2*i));
}
vector<pair<int, int>> results(nums.size());
partial_sum(nums.begin(), nums.end(), results.begin(), [](const pair<int, int>&a, const pair<int, int>&b){return pair<int, int>(a.second+b.second, 0);
});

5.iota()函数介绍

单从函数名字iota()并不能看出该函数的作用,因为该函数与前面其他函数的命名规则不同,该函数的名字iota并不是源自英文单词,而是来自希腊字母ι。该函数源于Ken Iverson发明的编程语言APL中的ι函数,其作用是生成一个以n为起始元素值,之后每个元素都增1,同时希腊字母ι也有极小,很小的一部分的意思。
因此,再C++中iota()函数的作用与APL中的ι函数类似,为使用以指定数值为起始,之后的元素依次增1的序列填充指定数组。

(1).函数定义

template <class ForwardIterator, class T>void iota (ForwardIterator first, ForwardIterator last, T val);

first,last:容器迭代器,待填充的数组;
val:初始元素值;

(2).用法示例

填充数组nums,令其第一个元素值为0,步长为1的递增数列。
代码:

#include <iostream>
#include <vector>
#include <numeric>
using namespace std;
int main(int, char**) {vector<int> nums(5, 0);iota(nums.begin(), nums.end(), 0);for (int i = 0; i < nums.size(); i++) {cout << nums[i] << " ";}return 0;
}

输出:

0 1 2 3 4

三、C++17新增的几个实用函数

1. gcd()函数

gcd()函数用来计算最大公约数(greatest common divisor)。
例如:gcd(12,18)返回6

2. lcm()函数

lcm()函数用来计算最小公倍数(least common multiple)。
例如:gcd(12,18)返回36(36是12和18的最小公倍数);

3. midpoint()函数

midpoint()函数用来计算整数、浮点数或指针的中点值。
例如:midpoint(2,5)返回3(2+5)/2取整等于3。

除了上面这三个函数之外,C++17中还新增了reduce(),transform_reduce(),inclusive_scan()等函数,详细信息读者可以查阅Standard library header 。

四、参考链接

[1]. cplusplus
[2]. cppreference Standard library header
[3]. Stack Overflow What does iota of std::iota stand for?
[4]. C++/C++11中头文件numeric的使用

[C++]<numeric>头文件介绍相关推荐

  1. linux非标准头文件,Linux学习:unix的标准化的实现(Linux中各种限制-数据类型-各种标准化头文件介绍)...

    作为Linux的前身,unix标准化是十分重要的.我在这里挑几个重要的点说明. 1:Linux中各种限制.Linux中限制有编译时限制和运行时限制,另外有一些限制是由于我们的实现不同而不同,因此我们需 ...

  2. NSIS 头文件介绍_FileFunc.nsh(1)

    文件函数,顾名思义就是处理文件用的函数.使用这些文件处理函数前,必须先包含头文件FileFunc.nsh.该头文件目前包含如下一些函数:Locate.GetSize.DriveSpace.GetDri ...

  3. NSIS 头文件介绍_WordFunc.nsh(3)

    文字函数,顾名思义就是处理字符串的函数.使用这些字符串函数前,必须先包含头文件WordFunc.nsh.该头文件目前包含如下一些函数:WordFind.WordFind2X.WordFind3X.Wo ...

  4. NSIS 头文件介绍_TextFunc.nsh(2)

    文本函数,顾名思义就是处理文本文件的函数.使用这些文本函数前,必须先包含头文件TextFunc.nsh.有些函数需要预声明,这个在帮助中有详细介绍,具体可查阅帮助.该头文件目前包含如下一些函数:Lin ...

  5. PIC单片机入门_PICC头文件介绍

    PICC支持下的C程序代码中一定要包含pic.h头文件,该文件安装在HT-PIC\include目录下.它是很多头文件的集合,C编译器在pic.h中根据用户选择的芯片自动载入相应的其它头文件,例如用户 ...

  6. std.h对应linux头文件,bits/stdc++.h头文件介绍(包含源代码)

    注:转自http://blog.csdn.net/charles_dong2/article/details/56909347,同为本人写的,有部分修改. 之前在一个小OJ上刷题时发现有人是这么写的: ...

  7. VScode找不到C++万能头文件<bits/stdc++.h>解决办法

    VScode找不到C++万能头文件<bits/stdc++.h>解决办法 一.万能头文件介绍 万能头文件<bits/stdc++> 中包含了 C++中大部分头文件,在大部分做题 ...

  8. AVRGCC常见库文件头文件介绍

    一.库函数头文件介绍 库函数按不同的类别声明在不同的头文件中,以字母为序分别介绍头文件: ctype.h:字符类型函数 eeprom.h:EEPROM 访问函数 errno.h:错误处理函数 ina9 ...

  9. C++11中头文件type_traits介绍

    C++11中的头文件type_traits定义了一系列模板类,在编译期获得某一参数.某一变量.某一个类等等类型信息,主要做静态检查. 此头文件包含三部分: (1).Helper类:帮助创建编译时常量的 ...

最新文章

  1. R语言max函数min函数计算各种数据对象最大值最小值实战
  2. Ubuntu 修改截屏快捷键
  3. Spring AOP编程-传统基于aspectJ切点AOP开发
  4. HDU - 5920 Ugly Problem(Java大数+贪心)
  5. Ubuntu下无法使用Secure_CRT连接服务器
  6. SDM For Face Alignment 流程介绍及Matlab代码实现之训练篇
  7. 职场中晋升最快的人具有什么特点?
  8. java堆的特点_java栈的特点是什么?java的堆和栈的优缺点介绍
  9. iOS应用程序安全风险及漏洞解析
  10. 转载-高仙机器人落地北京杭州深圳多个城市地铁
  11. 每日一问 --发信机和收信机对信号做了那些处理?
  12. Linux并行执行权限,如何在Linux中使用flock控制程序的异步执行
  13. 集成平台Ensemble环境搭建(HealthShare) 及MIRROR镜像(主备机)的配置
  14. html5知识点:超文本标记语言编程
  15. TIFS_2013_Empirical Evaluation and New Design for Fighting Evolving Twitter Spammers
  16. HDU - Robberies(01背包)
  17. ESP8266-Arduino编程实例-MAG3110磁力计驱动
  18. 安装CUDA11.2失败
  19. POST和GET有什么区别?
  20. 项目配置swagger(亲测能用)

热门文章

  1. pico的学习之路(四)——HC-SR501人体感应模块(树莓派pico实现)
  2. matlab中ode45用法,ode45(ode45用法举例)
  3. 圣迭戈与哥大电子计算机,加州大学圣迭戈分校
  4. 北京上地海淀IDC数据中心机房托管-永丰数据中心
  5. [转]程序员收集整理的PHP资源大全,包含各种类库及框架等
  6. 号码认证平台有哪些?号码认证平台费用?
  7. lopa分析_LOPA分析:使能条件和修正因子在场景识别方法中的应用
  8. Hive Load装载数据与HDFS的关系
  9. vue实现通过手机号发送短信验证码登录
  10. 基于DM6467的TVP7002 Linux驱动程序开发