C++ 标准库的 <numeric>
头文件是进行数值计算的宝库。它不仅仅包含几个简单的函数,而是提供了一整套强大、高效且灵活的算法,用于处理序列数据。这些算法能够帮助我们写出更简洁、更具表现力且性能更高的代码,尤其是在现代 C++ (C++17 及以后) 中,它们中的许多都支持并行执行。
本文将全面介绍 <numeric>
头文件中的核心算法,分为三大类:
这类算法的目标是将一个序列中的所有元素通过某个二元操作(如加法、乘法)合并成一个单一的值。
std::accumulate
(C++98)这是最经典、最基础的累积算法。它严格按照从左到右的顺序进行计算。
[first, last)
中所有元素的总和,从一个初始值 init
开始。std::reduce
(C++17)std::reduce
是 std::accumulate
的现代化、可并行化版本。
accumulate
类似,但不保证计算顺序。std::execution::par
等执行策略进行并行计算,从而大幅提升性能。init
参数的版本,它会使用元素类型的默认构造函数(例如 int{}
即 0
)作为初始值。何时选择?
- 需要保证顺序(如减法),或在 C++17 之前的代码中,使用
std::accumulate
。- 追求性能,且操作允许乱序(如加法),现代 C++ 代码首选
std::reduce
。
std::inner_product
(C++98)std::transform_reduce
(C++17)这是最强大的归约算法,完美体现了 "map-reduce" 思想。
扫描算法不会将序列归约为单个值,而是生成一个包含所有中间累积结果的新序列。
std::partial_sum
(C++98)inclusive_scan
的早期版本。结果中的第 i
个元素是原序列前 i+1
个元素的和。std::inclusive_scan
&std::exclusive_scan
(C++17)这两个是现代化的、可并行的扫描算法。
inclusive_scan
:包含当前元素的扫描,行为与 partial_sum
相同。exclusive_scan
:不包含当前元素的扫描,结果中的第 i
个元素是原序列前 i
个元素的和。std::adjacent_difference
(C++98)partial_sum
的逆运算,计算序列中每两个相邻元素之间的差。std::iota
(C++11)std::gcd
&std::lcm
(C++17)<numeric>
头文件为 C++ 开发者提供了一套声明式、高效且功能强大的工具。通过使用这些算法,我们可以:
accumulate
, transform_reduce
)清晰地表达了代码的意图。下次当你需要对一个序列进行数值计算时,不妨先查阅一下 <numeric>
头文件,很可能已经有一个完美的算法在等着你了。