当前位置: 首页 > news >正文

<numeric>

简介

这是一个“小而精”的算法库,主要用于对数值序列进行数值累计和计算

基础累加与乘积

accumulate()(C++03起)

版本一

template <class InputIterator, class T> T accumulate (InputIterator first, InputIterator last, T init); //first,last:定义了要计算的范围(左闭右开区间 [first, last)) //init: 累加的初始值 //函数返回值:最终的累加结果,类型与 init 相同

意思是从 init 开始,顺序地把区间里的每一个元素加进去

版本二

template <class InputIterator, class T, class BinaryOperation> T accumulate (InputIterator first, InputIterator last, T init, BinaryOperation binary_op); //first,last:定义了要计算的范围(左闭右开区间 [first, last)) //init:累加的初始值 //binary_op:二元操作,可以是一个 Lambda 表达式(最常用的) // 也可以是一个 函数指针 // 也可以是一个 函数对象

注:

Lambda 表达式:

是 C++11 引入的一种 匿名函数

它允许你在代码中直接定义一个“临时函数”,而不必专门去写一个完整的 function 或 class

格式:[capture](parameters { return_type{body} }

[capture]:决定了 Lambda 能不能看到并使用它外部定义的变量

[]:空捕获,不能访问外部变量

[x]:捕获变量 x 的值(拷贝一份)

[&x]:捕获变量 x 的引用(直接操作原变量)

[=]:捕获外部所有变量的值

[&]:捕获外部所有变量的引用

(parameters):参数列表,跟普通函数一样,定义要传入的参数

{ return_type{body} }:函数体,具体的逻辑代码以及函数返回值

EG:

//传统写法 bool isbigfive(int n) { return n > 5; } // 然后在 main 里调用 find_if(v.begin(), v.end(), isbigfive); //Lambda 写法 find_if(v.begin(),v.end(),[](int) { return n > 5; }); //find_if():在指定范围内,寻找第一个满足特定条件的元素

注:

详细了解 find_if()

注意

如果编写这样编写 accumulate(v.begin(), v.end(), 0),即使 v 里面存的是浮点数,结果也会被强转成 int(因为初始值 0 是 int);如果要保留小数,初始值应写成 0.0

EG:

#include <iostream> #include <vector> #include <numeric> // accumulate 所在的头文件 #include <string> using namespace std; int main() { vector<int> v = { 1, 2, 3, 4, 5 }; int sum = accumulate(v.begin(), v.end(), 0); cout << "版本一 (默认累加): " << sum << endl; int sum_of_squares = accumulate(v.begin(), v.end(), 0, [](int acc, int x) { return acc + (x * x); }); cout << "版本二 (平方和): " << sum_of_squares << endl; vector<string> words = { "C++", "Standard", "Library" }; string result = accumulate(words.begin(), words.end(), string("Learning"), [](string a, string b) { return a + " -> " + b; }); cout << "版本二 (字符串连接): " << result << endl; return 0; }

reduce()(C++17起)

理解了 acumulate() 之后,reduce() 就很好理解

简单理解就是:accumulate 是 严格顺序的,而reduce是 为了并行 而生的

这个可以先了解一下:

std::accumulate的定义决定了它必须从左到右依次累加。这在单核 CPU 上没问题,但在多核时代,如果我们想把一个巨大的数组分给 8 个 CPU 核心同时计算,accumulate的顺序协议就成了瓶颈

版本一:基础规约

template<class ExecutionPolicy, class ForwardIt> T reduce(ExecutionPolicy&& policy, ForwardIt first, ForwardIt last); //ExecutionPolicy&& policy (执行策略):这是 C++17 引入的核心参数, //决定了算法如何运行,告诉编译器,你是想让这段代码在单线程跑,还是想让它利用多核 CPU 并行跑 //常见取值(定义在 <execution> 中):(这个大家可以先做了解) //execution::seq:顺序执行 // 和 accumulate 类似,但在内部依然不保证操作顺序 //execution::par:并行执行 // 算法会将数据拆分成多个块,利用多线程同时计算 //execution::par_unseq:并行且矢量化 // 最高级的优化,利用多线程的同时还利用 CPU 的 SIMD 指令集 //ForwardIt first, ForwardIt last (迭代器范围):定义了一个左闭右开区间 [first, last),即要参与计算的数据范围

注意:

accumulate只需要InputIterator(最弱的迭代器),而reduce需要ForwardIt(正向迭代器)这是因为reduce可能需要多次扫描或拆分区间来支持并行化

版本二:带初始值和自定义操作

template<class ExecutionPolicy, class ForwardIt, class T, class BinaryOp> T reduce(ExecutionPolicy&& policy, ForwardIt first, ForwardIt last, T init, BinaryOp binary_op); //T init (初始值):计算的起点 //注意:它的类型 T 非常重要 // reduce 的结果类型完全取决于 init 的类型 // 如果是 reduce(..., 0),结果是 int // 如果是 reduce(..., 0.0),结果是 double // 如果是 reduce(..., 0LL),结果是 long long //BinaryOp binary_op (二元操作):定义如何合并两个元素 // 它是一个可调用对象(函数、Lambda、std::plus<> 等)

EG:

#include <iostream> #include <vector> #include <numeric> #include <execution> // 并行策略需要这个头文件 (C++17) using namespace std; int main() { vector<int> v(1000000, 1); // 100万个1 //基础用法(类似于 accumulate) int sum = reduce(v.begin(), v.end()); cout << "基础总和: " << sum << endl; //并行用法 (std::execution::par) // 这告诉编译器:请尽量利用多核并行计算 int par_sum = reduce(execution::par, v.begin(), v.end(), 0); cout << "并行总和: " << par_sum << endl; //自定义操作 (计算所有数字乘积的 2 倍起始值) int product = reduce(execution::par, v.begin(), v.end(), 1, [](int a, int b) { return a * b; }); return 0; }

inner_product()(C++03起)

对两个等长的序列进行以下操作:

将两个序列中对应位置的元素相乘(或执行自定义操作 A)

将所有乘积相加(或执行自定义操作 B)到初始值上

简单的例子就是:数学中的向量的内积(点积)

版本一

template<class InputIt1, class InputIt2, class T> T inner_product(InputIt1 first1, InputIt1 last1, InputIt2 first2, T init); //InputIt1 first1, InputIt1 last1:第一个序列的起始和结束位置(左闭右开区间 [first1, last1)) // 决定因素:这个区间的长度决定了计算会进行多少轮 //InputIt2 first2:第二个序列的起始位置 // 注意:算法会自动假设第二个序列和第一个序列一样长 // 如果第二个序列太短,程序会崩溃(越界访问) //T init:累加的初始值 // 重要性:它决定了返回值的数据类型。例如传 0 返回 int,传 0.0 返回 double

版本二

template<class InputIt1, class InputIt2, class T, class BinaryOp1, class BinaryOp2> T inner_product(InputIt1 first1, InputIt1 last1, InputIt2 first2, T init, BinaryOp1 op1, BinaryOp2 op2); //BinaryOp1 op1(外部操作 - 相当于“加法”):负责把“当前轮次的计算结果”合并到“之前的累计总和”里 //BinaryOp2 op2(内部操作 - 相当于“乘法”):负责处理“两个序列中对应位置的一对元素”

EG:

#include <iostream> #include <vector> #include <numeric> using namespace std; int main() { // 准备两个向量 vector<int> a = { 1, 3, 5 }; vector<int> b = { 2, 4, 6 }; //版本一:默认点积 int dot_product = inner_product(a.begin(), a.end(), b.begin(), 0); cout << "默认内积: " << dot_product << endl; //版本二:自定义操作 bool is_equal = inner_product(a.begin(), a.end(), b.begin(), true, [](bool acc, bool res) { return acc && res; }, // 对应累计操作 [](int x, int y) { return x == y; } // 对应配对操作 ); cout << "两个数组是否完全相等: " << (is_equal ? "是" : "否") << endl; //版本二:复杂一点的逻辑 int total_diff = inner_product(a.begin(), a.end(), b.begin(), 0, plus<int>(), [](int x, int y) { return abs(x - y); }); cout << "对应元素差的绝对值之和: " << total_diff << endl; return 0; }

transform_reduce()(C++17起)(简述)

inner_product() + reduce()

这个函数重载版本比较多,并且算法竞赛中使用频率较低,这里我就不过阐述,如有需要了解,可以评论留言 + 私信,看到了我都会回复的,在这里我就写一个例子,便于大家感知和理解

#include <iostream> #include <vector> #include <numeric> #include <execution> // 并行策略 #include <functional> using namespace std; int main() { vector<int> v1 = { 1, 2, 3, 4, 5 }; vector<int> v2 = { 1, 2, 3, 4, 5 }; auto result = transform_reduce( execution::par, // 1. 执行策略:并行 v1.begin(), v1.end(), // 2. 序列1范围 v2.begin(), // 3. 序列2起点 0, // 4. 初始值 plus<>(), // 5. 归约操作 (Reduce): 如何把结果加起来 [](int a, int b) { // 6. 变换操作 (Transform): 对应元素怎么处理 return (a * a) - (b * b); } ); cout << "变换归约结果: " << result << endl; return 0; }

差值与前缀和

partial_sum()

前缀和:前缀和是指一个数组中,从第 1 个元素到第 i 个元素的累加和

比如:

假设原数组为 a,前缀和数组为 S:S[0] = a[0]

S[1] = a[0] + a[1]

S[i] = a[0] + a[1] + ... + a[i]

template< class InputIt, class OutputIt > OutputIt partial_sum( InputIt first, InputIt last, OutputIt d_first ); //first:指向待处理序列的第一个元素 //last:指向待处理元素的最后一个元素的下一个位置 (标准 STL 的左闭右开区间 [first, last)) //d_first:指明计算结果从哪里开始写入 // 它可以是另一个容器的开头(如 res.begin()) // 它也可以就是 first 本身,这样会直接在原数组上更新,覆盖旧值

EG:

#include <iostream> #include <vector> #include <numeric> using namespace std; int main() { vector<int> v = { 1, 2, 3, 4, 5 }; vector<int> res(5); partial_sum(v.begin(), v.end(), res.begin()); for (int x : res) cout << x << " "; return 0; }

adjacent_difference()

差分:差分数组是指当前元素与前一个元素的差值

比如:

假设原数组为 a ,差分数组为 d:d[0] = a[0]

d[1] = a[1] - a[0]

d[i] = a[i] - a[i-1]

template< class InputIt, class OutputIt > OutputIt adjacent_difference( InputIt first, InputIt last, OutputIt d_first ); //参数所表达意义和 前缀和 函数相同

EG:

#include <iostream> #include <vector> #include <numeric> using namespace std; int main() { vector<int> v = { 1, 3, 6, 10, 15 }; vector<int> diff(5); std::adjacent_difference(v.begin(), v.end(), diff.begin()); for (int x : diff) std::cout << x << " "; return 0; }

差分与前缀和的关系:

对原数组求差分,再求前缀和,会变回原数组

对原数组求前缀和,再求差分,也会变回原数组

序列操作与内积(待更)

高级数学运算 (C++17/20)(待更)

http://www.jsqmd.com/news/675158/

相关文章:

  • Z-Image开源大模型调试工具:LM系列权重一键注入+生成结果自动标注
  • 告别混乱!在uni-app中优雅管理推送消息与角标:一个封装好的Push工具类详解
  • BitNet b1.58-2B-4T-GGUF快速上手:WebUI界面操作+System Prompt调优指南
  • 别再死磕90%!手把手教你用STL软件测试库搞定ISO 26262 ASIL B认证
  • 计算机毕业设计:Python农产品电商数据挖掘与推荐系统 Flask框架 矩阵分解 数据分析 可视化 协同过滤推荐算法 深度学习(建议收藏)✅
  • 郑州城市职业学院:作息安排与住宿生活全知道
  • nli-MiniLM2-L6-H768惊艳效果:支持‘跨语言标签’(English label + 中文文本)混合推理
  • Beaver Notes(海狸笔记)v4.4.0 中文版 ,开源免费、本地存储、零追踪的笔记软件
  • 冰雪传奇三职业深度解析!官方认证下支持安卓、iOS、PC 三端互通
  • Dify快速集成Slack通知、企微审批、AWS Lambda:3步自动化上线,附可运行YAML模板
  • Asian Beauty Z-Image Turbo 技术解析:透过LSTM理解序列生成在扩散模型中的角色
  • Django 表单实战:从 0 到 1 实现用户注册表单(Form/ModelForm 详解)
  • 别再傻傻用typeid判断类型了!C++运行时类型识别(RTTI)的完整指南与实战避坑
  • Typora Markdown写作助手:集成GLM-4.7-Flash实现智能排版
  • Phi-3.5-mini-instruct多场景落地:教育机构AI助教系统建设实践
  • 预算为0也能上系统?揭秘私藏的5个“零成本”数字化神器
  • real-anime-z企业应用:品牌IP延展——从LOGO生成配套动漫风格VI素材
  • 《不花一分钱,让你的QClaw在Mac上跑得比云端还快》
  • Qianfan-OCR惊艳效果:手写体混合印刷体合同中签名区域+条款文本分离展示
  • 【限时首发|Loom安全迁移黄金72小时】:20年JVM专家手把手带你完成存量Spring Boot项目响应式重构+全链路安全加固(含自动化检测脚本)
  • 别再拍脑袋估工时了!用FPA功能点分析法,像东北电网那样精准评估软件开发工作量
  • real-anime-z新手误区纠正:不是步数越高越好,12步+LoRA强度1.0更高效
  • 易基因: Nat Plants:南科大朱健康/华中农大赵伦团队aChIP-seq+WGBS表观多组学揭示ROS1调控DNA去甲基化新机制
  • Phi-3.5-mini-instruct开源可部署:提供完整Dockerfile与K8s Helm Chart模板
  • [特殊字符] EagleEye效果实测:20ms内完成1080P图像检测的完整性能报告
  • NaViL-9B效果对比评测:vs Qwen-VL、InternVL在中文图文任务表现
  • 2026直埋保温管厂家推荐排行榜产能与专利双优企业领衔 - 爱采购寻源宝典
  • unity_vuforia_ar—-识别地面
  • AI人体骨骼检测保姆级教程:3步完成部署,轻松绘制骨骼连线图
  • Phi-3.5-mini-instruct部署案例:4090单卡同时运行3个实例实测