1.说明:
数值算法属于泛型算法,要求输入迭代器,输出数据则使用输出迭代器表示目的位置
除iota外,数值算法都提供了重载版本(即用谓词代替默认运算符的自定义版本)
1.说明:
数值算法属于泛型算法,要求输入迭代器,输出数据则使用输出迭代器表示目的位置
除iota外,数值算法都提供了重载版本(即用谓词代替默认运算符的自定义版本)
2.Functions
iota 用起始值的连续增量填充单容器
accumulate 单容器求和
reduce 单容器求和或(应用可调用)与accumulate相似但顺序混乱
transform_reduce 容器1元素乘以容器2元素后求总和(或应用可调用)
inner_product 计算两容器内积(即对应元素的积的和)(或应用可调用)
adjacent_difference 单容器差分=后一个元素-前一个元素
partial_sum 单容器累计和(或应用可调用)a1=v1,a2=v1+v2,a3= v1+v2+v3,...
inclusive_scan 相似partial_sum求累积和(包括当前元素)(或应用可调用)v0=init+e0,v1=v0+e1,v2=v1+e2,v3=v2+e3,...
exclusive_scan 相似partial_sum求累积和(不含当前元素)(或应用可调用)v[0]=init;v[i]=v[i-1]+e[i-1];
transform_inclusive_scan unary_op函数作用于每个元素后,然后再用binary_op函数累积操作新元素(包含当前值)
transform_exclusive_scan unary_op函数作用于每个元素后,然后再用binary_op函数累积操作新元素(不含当前值)
gcd 返回两个整数的最大公约数
lcm 返回两个整数的最小公倍数
midpoint 两个数字或指针之间的中点 (C++20)
1.accumulate-单容器求和
函数:T accumulate( InputIt first, InputIt last, T init ); //使用+运算符
T accumulate( InputIt first, InputIt last, T init,BinaryOperation op );//使用指定二元操作
用途:求容器中[first,last)元素的总和
返回:
1)给定值与给定范围内的元素之和
2)给定范围的左折结果op
原理:result=*first+*(first+1)+...;
注意:执行左折为正确折叠必将参数顺序颠倒到二进制运算符,并使用反向迭代器
2.实例:accumulate
#include <iostream>
#include <vector>
#include <numeric>
#include <string>
#include<cassert>
using namespace std;
int main(){
vector<int> v{ 1, 2, 3, 4, 5};
int sum = accumulate(v.begin(), v.end(), 100);
assert(sum == 115);//=100+1+2+3+4+5
int product = accumulate(v.begin(), v.end(), 100, multiplies<int>());
assert(product == 100*1*2*3*4*5);
auto dash_fold = [](string a, int b) {return move(a) + '-' + to_string(b);};
// 从第一个元素开始
string s = accumulate(next(v.begin()), v.end(),to_string(v[0]), dash_fold);
assert(s == string("1-2-3-4-5"));
// 右折使用反向迭代器:从最后一个元素开始
string rs = accumulate(next(v.rbegin()), v.rend(),to_string(v.back()), dash_fold);
assert(rs == string("5-4-3-2-1"));
}
2.adjacent_difference差分=后一个元素-前一个元素
函数:
OutputIt adjacent_difference( InputIt first, InputIt last,OutputIt d_first );//版本1用元素类型的-运算符
ForwardIt2 adjacent_difference( policy, ForwardIt1 first, ForwardIt1 last,ForwardIt2 d_first );
OutputIt adjacent_difference( InputIt first, InputIt last, OutputIt d_first, BinaryOperation op );//版本2用指定二元操作
ForwardIt2 adjacent_difference(policy, ForwardIt1 first, ForwardIt1 last, ForwardIt2 d_first, BinaryOperation op );
用途:差分rst[0]=*first,rst[1]=*(first+2)-*(first+1),rst[2]=*(first+3)-*(first+2),...
笔记:如果first == last,此函数无效,仅返回d_first
实例6:adjacent_difference差分
#include <numeric>
#include <vector>
#include <array>
#include <iostream>
int main(){
std::vector<int> vec{ 2, 4, 6, 8, 10,12 };
std::adjacent_difference(vec.begin(), vec.end(), vec.begin());
for (auto v : vec)std::cout << v << ' ';
std::cout << '\n';//2 2 2 2 2 2==(2,6-4,8-6,10-8,12-10)
std::array<int, 6> arr{ 1 };
adjacent_difference(begin(arr), std::prev(end(arr)), std::next(begin(arr)), std::plus<> {});
copy(begin(arr), end(arr), std::ostream_iterator<int> {std::cout, " "});//1 1 2 3 5 8
//==(1,1,1+1,1+2,2+3,3+5)
}
3.std::exclusive_scan 累积和但不包括当前元素(或应用可调用)
1.1.函数:
OutputIt exclusive_scan( InputIt first, InputIt last,OutputIt d_first, T init, [BinaryOperation binary_op ]);
ForwardIt2 exclusive_scan( policy, ForwardIt1 first, ForwardIt1 last,ForwardIt2 d_first, T init, [BinaryOperation binary_op] );
1.2.用途:累积和但不包括当前元素(或应用可调用)
1.3.原理:v[0]=init;v[i]=v[i-1]+e[i-1];
1.4.参数:
first, last - 求和元素的范围
d_first - 目标范围的开始;可能等于firs
policy - 执行策略
init - 初始值 必须要有
binary_op- 二元函数
实例:
#include <functional>
#include <iostream>
#include <iterator>
#include <numeric>
#include <vector>
using namespace std;
void test() {
std::vector<long long> data{ 2, 10, 5, 2, 1, 2, 10, 5 };
std::cout << "1.1.exclusive sum: ";
std::exclusive_scan(data.begin(), data.end(),
std::ostream_iterator<long long>(std::cout, " "),0, std::plus<long long>{});
std::cout << "\n\n1.2.exclusive sum: ";
long longinit=0,v=0;
for (auto i = 0; i < data.size(); ++i)
{
if (i == 0)
v = init;
else
v = v + data[i-1];
cout << v << " ";
}
cout << endl;
std::cout << "\n\n2.1.exclusive product: ";
std::exclusive_scan(data.begin(), data.end(),
std::ostream_iterator<long long>(std::cout, " "), 1, std::multiplies<long long>{});
std::cout << "\n2.2.inclusive product: ";
init = 1, v = 0;
for (auto i = 0; i < data.size(); ++i)
{
if (i == 0)
v = init;
else
v = v * data[i - 1];
cout << v << " ";
}
cout << endl;
std::cout << "\n\n3.1.exclusive divides: ";
std::exclusive_scan(data.begin(), data.end(),
std::ostream_iterator<long long>(std::cout, " "), 100000, std::divides<long long>{});
std::cout << "\n3.2.inclusive divides: ";
init = 100000, v = 0;
for (auto i = 0; i < data.size(); ++i)
{
if (i == 0)
v = init;
else
v = v / data[i - 1];
cout << v << " ";
}
cout << endl;
}
int main() {
test();
}
输出:
1.1.exclusive sum: 0 2 12 17 19 20 22 32
1.2.exclusive sum: 0 2 12 17 19 20 22 32
2.1.exclusive product: 1 2 20 100 200 200 400 4000
2.2.inclusive product: 1 2 20 100 200 200 400 4000
3.1.exclusive divides: 100000 50000 5000 1000 500 500 250 25
3.2.inclusive divides: 100000 50000 5000 1000 500 500 250 25
4.gcd 最大公约数
函数:constexpr std::common_type_t<M, N> gcd(M m, N n);
用途:计算整数m和n的最大公约数
返回:如果m和n均为零,则返回零。否则,返回|m|和的最大公约数|n|
注意:如M或N不是整数,或一个[cv]bool则程序格式错误
如m或n不能表示为类型std :: common_type_t < M,N >的值则行为未定义
实例12:gcd最大公约数
#include <iostream>
#include <type_traits>
#include <numeric>
#include<cassert>
template <class T>struct Number { T v; };
template <class T, class U>
Number<typename std::common_type<T, U>::type> operator+(
const Number<T>& lhs,const Number<U>& rhs){ return { lhs.v + rhs.v };}
using namespace std;
int main(){
//测试x1,y1的共同的类型
Number<int> x1 = { 1 }, x2 = { 2 };
Number<double> y1 = { 2.3 }, y2 = { 3.5 };
auto v = x1.v + y2.v;
assert(string(typeid(v).name()) == string("double"));
assert(x1.v + x2.v == 3);
assert(x1.v + y1.v == 3.3);
//测试最大公约数
constexpr int p{ 2 * 2 * 3 };constexpr int q{ 2 * 3 * 3 };
static_assert(2 * 3 == std::gcd(p, q));
}
5.inclusive_scan求累积和 (或应用可调用)
1.1.函数:
OutputIt inclusive_scan( InputIt first,InputIt last, OutputIt d_first ); //std::plus<>()
OutputIt inclusive_scan( InputIt first, InputIt last, OutputIt d_first,BinaryOperation binary_op, [T init] );
ForwardtIt2 inclusive_scan(policy, ForwardIt1 first, ForwardIt1 last,ForwardIt2 d_first );//std::plus<>()
ForwardIt2 inclusive_scan(policy, ForwardIt1 first, ForwardIt1 last, ForwardIt2 d_first, BinaryOperation binary_op,[T init] );
1.2.用途:求累积和 (或应用可调用)
1.3.参数:first, last - 求和元素的范围
d_first - 目标范围开始可等于first
policy - 执行策略
init - 初始值(可选)
binary_op- 二元函数
1.4.原理:v0=init+e0,v1=v0+e1,v2=v1+e2,v3=v2+e3,...
1.5.注意:不应修改任何迭代元素,否则行为未知
实例:
#include <functional>
#include <iostream>
#include <iterator>
#include <numeric>
#include <vector>
using namespace std;
void test1() {
std::vector<long long> data{ 10000, 10, 5, 2, 1, 2, 10, 5 };
std::cout << "\n1.11.inclusive sum: ";
std::inclusive_scan(data.begin(), data.end(),std::ostream_iterator<long long>(std::cout, " "));
cout << "\n1.12.inclusive sum: ";
long long tmp = 0;
for(auto i = 0; i < 8; ++i)
{
if (i == 0)tmp=data[i];
else
tmp=tmp+ data[i];
cout << tmp << " ";
}
cout << endl;
std::cout << "\n1.21.inclusive product: ";
std::inclusive_scan(data.begin(), data.end(), std::ostream_iterator<long long>(std::cout, " "),std::multiplies<>{});
cout << "\n1.22.inclusive product: ";
tmp = 0;
for(auto i = 0; i < 8; ++i)
{
if (i == 0)tmp = data[i];
else
tmp = tmp * data[i];
cout << tmp << " ";
}
cout << endl;
std::cout << "\n1.31.inclusive divides: ";
std::inclusive_scan(data.begin(), data.end(),std::ostream_iterator<long long>(std::cout, " "),std::divides<>{});
cout << "\n1.32.inclusive divides: ";
tmp = 0;
for(auto i = 0; i < 8; ++i)
{
if (i == 0)tmp = data[i];
else
tmp = tmp /data[i];
cout << tmp << " ";
}
cout << endl;
}
void test2() {
std::vector<long long> data{ 10, 10, 5, 2, 1, 2, 10, 5 };
std::cout << "\n2.11.inclusive sum: ";
std::inclusive_scan(data.begin(), data.end(),std::ostream_iterator<long long>(std::cout, " "), std::plus<long long>(), 10);
cout << "\n2.12.inclusive sum: ";
long long tmp = 10;
for (auto i = 0; i < 8; ++i)
{
tmp += data[i];
cout << tmp << " ";
}
cout << endl;
std::cout << "\n2.21.inclusive product: ";
std::inclusive_scan(data.begin(), data.end(),std::ostream_iterator<long long>(std::cout, " "),std::multiplies<>{}, 10);
cout << "\n2.22.inclusive product: ";
tmp = 10;
for (auto i = 0; i < 8; ++i)
{
tmp = tmp * data[i];
cout << tmp << " ";
}
cout << endl;
std::cout << "\n2.31.inclusive divides: ";
std::inclusive_scan(data.begin(), data.end(), std::ostream_iterator<long long>(std::cout, " "), std::divides<>{}, 10000);
cout << "\n2.32.inclusive divides: ";
tmp = 10000;
for (auto i = 0; i < 8; ++i)
{
tmp = tmp / data[i];
cout << tmp << " ";
}
cout << endl;
}
int main() {
test1();
test2();
}
输出:
1.11.inclusive sum: 10000 10010 10015 10017 10018 10020 10030 10035
1.12.inclusive sum: 10000 10010 10015 10017 10018 10020 10030 10035
1.21.inclusive product: 10000 100000 500000 1000000 1000000 2000000 20000000 100000000
1.22.inclusive product: 10000 100000 500000 1000000 1000000 2000000 20000000 100000000
1.31.inclusive divides: 10000 1000 200 100 100 50 5 1
1.32.inclusive divides: 10000 1000 200 100 100 50 5 1
2.11.inclusive sum: 20 30 35 37 38 40 50 55
2.12.inclusive sum: 20 30 35 37 38 40 50 55
2.21.inclusive product: 100 1000 5000 10000 10000 20000 200000 1000000
2.22.inclusive product: 100 1000 5000 10000 10000 20000 200000 1000000
2.31.inclusive divides: 1000 100 20 10 10 5 0 0
2.32.inclusive divides: 1000 100 20 10 10 5 0 0
6.inner_product计算两个范围元素的内积
函数:
T inner_product( InputIt1 first1, InputIt1 last1, InputIt2 first2, T init );//版本1使用元素类型的*和+
T inner_product( InputIt1 first1, InputIt1 last1, InputIt2 first2, T init,BinaryOperation1 op1,BinaryOperation2 op2 );
用途:返回两序列内积(即对应元素的积的和),和的初值由init指定的值开始
类型要求:
--InputIt1, InputIt2必须满足LegacyInputIterator的要求
--ForwardIt1, ForwardIt2必须满足LegacyForwardIterator的要求
--T必须满足CopyAssignable和CopyConstructible的要求
实例:std::inner_product
#include <iostream>
#include <numeric>
#include <vector>
using namespace std;
int main(){
std::vector<int> v1{ 0, 1, 2, 3, 4 };
std::vector<int> v2{ 5, 4, 2, 3, 1 };
int r1 = std::inner_product(v1.begin(), v1.end(), v2.begin(), 0);
std::cout << "Inner product of v1 and v2: " << r1 << '\n';//0+0*5+1*4+2*2+3*3+4*1=21
int r2 = std::inner_product(v1.begin(), v1.end(), v2.begin(), 0,
std::plus<>(), std::equal_to<>());
std::cout << "Number of pairwise matches between v1 and v2: " << r2 << '\n';//2
}
7.iota-单容器插入递增数据
实例1:iota
#include <iostream>
#include <list>
#include <numeric>
int main() {
std::list<int> lst(7);
std::iota(lst.begin(), lst.end(), -3);//lst=-3 -2 -1 0 1 2 3
for (auto n : lst) std::cout << n << ' ';
std::cout << '\n';
}
8.lcm 两个整数的最小公倍数
函数:constexpr std::common_type_t<M, N> lcm(M m, N n);
用途:计算整数m和的最小公倍数n
参数:m, n -整数值
返回:如果m或n为零,则返回零。否则,返回|m|和的最小公倍数|n|
备注:如M或N不是整数类型,或一个(cv)bool,则程序格式错误
如|m|和|n|不能std :: common_type_t < M,N >行为是未定义
2.实例:
#include <numeric>
int main() {
constexpr int p {2 * 2 * 3};constexpr int q {2 * 3 * 3};
static_assert(2 * 2 * 3 * 3 == std::lcm(p, q));
}
9.midpoint 两个数字或指针之间的中点 (C++20)
函数:
constexpr T midpoint(T a, T b) noexcept;(C++20) 返回(a+b)/2 如为奇数则四舍五入
constexpr T* midpoint(T* a, T* b); (C++20) 返回x[i+(j-i)/2]
用途:计算整数,浮点数或指针a和的中点b
参数:a, b-整数浮点数或指针值
注意:如果a并且b不指向同一数组对象的元素,则行为是不确定的
原理:可以简单地实现为return a + (b - a) / 2; 这种实现不能保证是可移植
实例14:std::midpoint
#include <cstdint>
#include <limits>
#include <numeric>
#include <iostream>
int main() {
std::uint32_t a = std::numeric_limits<std::uint32_t>::max();
std::uint32_t b = std::numeric_limits<std::uint32_t>::max() - 2;
std::cout << "a: " << a << '\n' //4294967295
<< "b: " << b << '\n' // 4294967293
<< "错误溢出=" << (a + b) / 2 << '\n' //2147483646
<< "正确= " << std::midpoint(a, b) << "\n\n"; //4294967294
auto on_pointers = [](int i, int j) {
char const* text = "0123456789";
char const* p = text + i;
char const* q = text + j;
std::cout << "std::midpoint('" << *p << "', '" << *q << "'): '"
<< *std::midpoint(p, q) << "'\n";
};
on_pointers(2, 4); //'3'
on_pointers(2, 5); //'3'
on_pointers(5, 2); //'4'
on_pointers(2, 6); //'4'
}
10.partial_sum 计算一系列元素的部分和
函数:
OutputIt partial_sum( InputIt first, InputIt last, OutputIt d_first );//版本用元素类型的+运算符
OutputIt partial_sum( InputIt first, InputIt last, OutputIt d_first,BinaryOperation op );//版本2用指定二元操作
用途:新序列每个新元素值都等于输入范围中当前位置和之前位置上所有元素之和
原理:
*(d_first) = *first;
*(d_first+1) = *first + *(first+1);
*(d_first+2) = *first + *(first+1) + *(first+2);
*(d_first+3) = *first + *(first+1) + *(first+2) + *(first+3);
...
实例7:std::partial_sum
#include <numeric>
#include <vector>
#include <iostream>
#include <iterator>
int main(){
std::vector<int> v = { 2, 2, 2, 2, 2 };
std::partial_sum(v.begin(), v.end(),std::ostream_iterator<int>(std::cout, " "));
std::cout << '\n';//2 4 6 8 10==(2,2+2,2+4,2+6,2+8)
v.assign({ 1,2,3,4,5,6 });
std::partial_sum(v.begin(), v.end(), v.begin(), std::multiplies<int>());
for (auto n : v)std::cout << n << " ";
std::cout << '\n';//1 2 6 24 120 720==(1,1*2,1*2*3,1*2*3*4,1*2*3*4*5,1*2*3*4*5*6)
}
11.reduce-单容器求和 与std :: accumulate相似,但顺序混乱
函数:
typename std::iterator_traits<InputIt>::value_type
reduce([policy,] InputIt first, InputIt last); //相当init=0,binary_op=plus<>()
T reduce([policy,] InputIt first, InputIt last, T init); //相当binary_op=plus<>()
T reduce([policy,] InputIt first, InputIt last, T init, BinaryOp binary_op);
用途:其行为类似于std :: accumulate,只是范围的元素可以按任意顺序分组和重新排列
参数:- 广义和的初始值
原理:op(GSUM(op,b 1,...,bk)
注意:如果行为binary_op不是关联性或可交换性,则行为是不确定的。
如binary_op修改[first,last)任何元素或使迭代器无效,则该行为未定义包括结束迭代器
2.实例:reduce 和上例accumulate完全相同
#include <iostream>
#include <numeric>
#include <vector>
#include<string>
#include<cassert>
using namespace std;
int main(){
vector<int> v{ 1, 2, 3, 4, 5 };
int sum = reduce(v.begin(), v.end(), 100);
cout << "sum=" << sum << endl;
//assert(sum == 115);//=100+1+2+3+4+5
int product = reduce(v.begin(), v.end(), 100, multiplies<int>());
assert(product == 100 * 1 * 2 * 3 * 4 * 5);
auto dash_fold = [](string a, int b) {return move(a) + '-' + to_string(b); };
string s = reduce(next(v.begin()), v.end(), to_string(v[0]), dash_fold);
assert(s == string("1-2-3-4-5"));
string rs = reduce(next(v.rbegin()), v.rend(), to_string(v.back()), dash_fold);
assert(rs == string("5-4-3-2-1"));
}
12.transform_exclusive_scan
1.1.说明:同std::transform_exclusive_scan类似
1.2.用途:unary_op函数作用于每个元素后,然后再用binary_op函数累积操作新元素(不含当前值)
2.实例:参考transform_exclusive_scan
13.std::transform_inclusive_scan
1.1.函数:
OutputIt transform_inclusive_scan( InputIt first, InputIt last, OutputIt d_first,BinaryOperation binary_op, UnaryOperation unary_op,[T init] );
ForwardIt2 transform_inclusive_scan(policy, ForwardIt1 first, ForwardIt1 last, ForwardIt2 d_first,BinaryOperation binary_op, UnaryOperation unary_op, [T init] );
1.2.用途:unary_op函数作用于每个元素后,然后再用binary_op函数累积操作新元素(包含当前值)
2.实例:
#include <functional>
#include <iostream>
#include <iterator>
#include <numeric>
#include <vector>
int main(){
std::vector data{ 3, 1, 4, 1, 5, 9, 2, 6 };
auto times_10 = [](int x) { return x * 10; };
std::cout << "10 times exclusive sum: ";
std::transform_exclusive_scan(data.begin(), data.end(),
std::ostream_iterator<int>(std::cout, " "),
0, std::plus<int>{}, times_10);
std::cout << "\n10 times inclusive sum: ";
std::transform_inclusive_scan(data.begin(), data.end(),
std::ostream_iterator<int>(std::cout, " "),
std::plus<int>{}, times_10);
}
输出:
//10 times exclusive sum : 0 30 40 80 90 140 230 250
//10 times inclusive sum : 30 40 80 90 140 230 250 310
14.transform_reduce-容器1元素乘以容器2元素后求总和 应用可调用的,然后减少混乱
函数:
T transform_reduce(InputIt1 first1, InputIt1 last1, InputIt2 first2, T init);// std::plus<>(), std::multiplies<>()默认std :: inner_product并行化
T transform_reduce(InputIt1 first1, InputIt1 last1, InputIt2 first2,T init, binary_op1, binary_op2);
T transform_reduce(InputIt first, InputIt last,T init, BinaryOp binop, UnaryOp unary_op);
T transform_reduce(policy, ForwardIt1 first1, ForwardIt1 last1, ForwardIt2 first2, T init);
T transform_reduce(policy,ForwardIt1 first1, ForwardIt1 last1, ForwardIt2 first2,T init, BinaryOp1 binary_op1, BinaryOp2 binary_op2);
T transform_reduce(policy,ForwardIt first, ForwardIt last,T init, BinaryOp binary_op, UnaryOp unary_op);
参数:binary_op二元函数;unary_op元函数
说明:4-6功能同1-3只是多个并行化参数选择
注意:任何函数修改迭代器元素(包括end)行为是未定义的
2.实例:transform_reduce
#include <iostream>
#include <numeric>
#include <vector>
using namespace std;
int main(){
vector<int> v{ 1, 2, 3, 4, 5 };
std::vector<int> v1 = { 10, 20, 30 };
std::vector<int> v2 = { 1, 2, 3};
auto product = transform_reduce(v1.begin(), v1.end(),v2.begin(), 1000);
std::cout <<"product="<< product << endl;//1000+10*1+2*20+30*3=1140
product = transform_reduce(v1.begin(), v1.end(), v2.begin(), 1000,std::plus<>(), std::multiplies<>());
std::cout << "product=" << product << endl;//1000+10*1+2*20+30*3=1140
auto less_three = [](int x) {return x+10; };
product = transform_reduce(v2.begin(), v2.end(), 1000, std::plus<>(), less_three);
std::cout << "product=" << product << endl;//1000+(1+10)+(2+10)+(3+10)=1036
}