目 录CONTENT

文章目录

C++ STL:算法-数值算法

TalentQ
2025-09-05 / 0 评论 / 0 点赞 / 0 阅读 / 0 字

C++标准模板库(STL)中的数值算法提供了一套强大而高效的工具,用于处理各种数值计算任务。这些算法定义在<numeric>头文件中,不仅能够简化代码,提高可读性,还能通过编译器的优化获得更好的性能。本文将深入探讨五个核心数值算法,从函数原型到实际应用,全面解析其工作原理和使用技巧。

1 std::accumulate - 累积计算算法

算法概述

std::accumulate是STL中最常用的数值算法之一,用于计算给定范围内元素的累积值。它通过遍历范围中的每个元素,并将其与当前累积值结合,最终返回累积结果。

函数原型

// 版本1:使用默认加法操作
template< class InputIt, class T >
T accumulate( InputIt first, InputIt last, T init );

// 版本2:使用自定义二元操作
template< class InputIt, class T, class BinaryOperation >
T accumulate( InputIt first, InputIt last, T init, BinaryOperation op );

参数详解

  • first, last:输入范围的起始和结束迭代器(前闭后开区间[first, last))

  • init:累积操作的初始值,其类型决定了返回值的类型

  • op:自定义二元操作函数,接受当前累积值和当前元素值,返回新的累积值

工作原理

算法从初始值init开始,依次对范围内的每个元素应用操作(默认为加法),不断更新累积值:

result = init
for each element in [first, last):
    result = op(result, *it)  // 或者 result + *it(默认情况)
return result

代码示例

#include <iostream>
#include <vector>
#include <numeric>  // For std::accumulate
#include <functional>  // For std::multiplies
#include <string>

int main() {
    // 示例1:基本用法 - 计算整数和
    std::vector<int> numbers = {1, 2, 3, 4, 5};
    
    // 计算所有元素的和,初始值为0
    // 计算过程: 0+1=1 → 1+2=3 → 3+3=6 → 6+4=10 → 10+5=15
    int sum = std::accumulate(numbers.begin(), numbers.end(), 0);
    std::cout << "Sum of numbers: " << sum << std::endl;  // 输出: 15

    // 示例2:使用自定义操作 - 计算乘积
    // 计算过程: 1*1=1 → 1*2=2 → 2*3=6 → 6*4=24 → 24*5=120
    int product = std::accumulate(numbers.begin(), numbers.end(), 1, 
                                 std::multiplies<int>());
    std::cout << "Product of numbers: " << product << std::endl;  // 输出: 120

    // 示例3:处理浮点数 - 计算平均值
    std::vector<double> values = {1.5, 2.5, 3.5, 4.5};
    double total = std::accumulate(values.begin(), values.end(), 0.0);
    double average = total / values.size();
    std::cout << "Average: " << average << std::endl;  // 输出: 3.0

    // 示例4:字符串连接
    std::vector<std::string> words = {"Hello", " ", "World", "!"};
    std::string sentence = std::accumulate(words.begin(), words.end(), 
                                         std::string(""));
    std::cout << "Concatenated: " << sentence << std::endl;  // 输出: Hello World!

    return 0;
}

应用场景

  • 计算数组或容器的总和、乘积等聚合值

  • 实现复杂的归约操作(如求最大值、最小值)

  • 字符串拼接或其他自定义累积操作

  • 统计分析和数据处理

注意事项

  • 初始值的类型很重要,它决定了返回值的类型和计算精度

  • 对于浮点数计算,要注意累积误差问题

  • 自定义操作函数应该是可结合且可交换的,以获得确定性的结果

2 std::iota - 序列生成算法

算法概述

std::iota算法用于生成一个连续的数值序列,从指定的初始值开始,依次递增填充到容器中。算法名称来源于APL语言中的⍳字符,该字符用于生成整数序列。

函数原型

template< class ForwardIt, class T >
void iota( ForwardIt first, ForwardIt last, T value );

参数详解

  • first, last:输出范围的起始和结束迭代器

  • value:序列的起始值,每次赋值后递增

工作原理

算法从value开始,依次为范围内的每个元素赋值,每次赋值后value递增:

for each element in [first, last):
    *it = value
    value = value + 1  // 使用前置递增

代码示例

#include <iostream>
#include <vector>
#include <numeric>  // For std::iota
#include <algorithm> // For std::for_each

int main() {
    // 示例1:生成整数序列
    std::vector<int> numbers(10);  // 创建包含10个元素的向量
    
    // 生成从0开始的序列: 0, 1, 2, ..., 9
    std::iota(numbers.begin(), numbers.end(), 0);
    
    std::cout << "Generated sequence: ";
    for (int num : numbers) {
        std::cout << num << " ";
    }
    std::cout << std::endl;

    // 示例2:生成指定范围的序列
    std::vector<int> range(5);
    std::iota(range.begin(), range.end(), 10);  // 从10开始: 10, 11, 12, 13, 14
    
    std::cout << "Range starting from 10: ";
    for (int num : range) {
        std::cout << num << " ";
    }
    std::cout << std::endl;

    // 示例3:与算法结合使用 - 生成索引序列
    std::vector<std::string> fruits = {"apple", "banana", "cherry", "date"};
    std::vector<size_t> indices(fruits.size());
    
    std::iota(indices.begin(), indices.end(), 0);  // 生成索引: 0, 1, 2, 3
    
    std::cout << "Fruits with indices:" << std::endl;
    for (size_t idx : indices) {
        std::cout << idx << ": " << fruits[idx] << std::endl;
    }

    return 0;
}

应用场景

  • 生成测试数据或序列

  • 创建索引数组

  • 初始化需要连续值的容器

  • 与算法结合实现复杂操作

注意事项

  • 要求容器元素类型支持前置递增操作

  • 生成的序列是连续的整数值

  • 通常用于初始化阶段,填充容器数据

3 std::inner_product - 内积计算算法

算法概述

std::inner_product计算两个序列的内积(点积),即对应元素相乘后的累加和。算法支持自定义加法和乘法操作,使其具有更广泛的适用性。

函数原型

// 版本1:使用默认加法和乘法操作
template< class InputIt1, class InputIt2, class T >
T inner_product( InputIt1 first1, InputIt1 last1, InputIt2 first2, T init );

// 版本2:使用自定义加法和乘法操作
template< class InputIt1, class InputIt2, class T,
          class BinaryOperation1, class BinaryOperation2 >
T inner_product( InputIt1 first1, InputIt1 last1, InputIt2 first2, T init,
                 BinaryOperation1 op1, BinaryOperation2 op2 );

参数详解

  • first1, last1:第一个序列的起始和结束迭代器

  • first2:第二个序列的起始迭代器(长度应与第一个序列相同)

  • init:初始值

  • op1:替代加法的二元操作

  • op2:替代乘法的二元操作

工作原理

算法计算两个序列的对应元素乘积之和:

result = init
for each corresponding pair (a, b) from [first1, last1) and [first2, ...):
    result = op1(result, op2(a, b))
return result

代码示例

#include <iostream>
#include <vector>
#include <numeric>  // For std::inner_product
#include <functional> // For std::plus, std::multiplies

int main() {
    // 示例1:基本用法 - 计算向量点积
    std::vector<int> vec1 = {1, 2, 3};
    std::vector<int> vec2 = {4, 5, 6};
    
    // 计算点积: 1*4 + 2*5 + 3*6 = 4 + 10 + 18 = 32
    int dot_product = std::inner_product(vec1.begin(), vec1.end(), 
                                       vec2.begin(), 0);
    std::cout << "Dot product: " << dot_product << std::endl;  // 输出: 32

    // 示例2:使用自定义操作 - 计算曼哈顿距离
    std::vector<int> point1 = {1, 2, 3};
    std::vector<int> point2 = {4, 6, 8};
    
    // 计算 |1-4| + |2-6| + |3-8| = 3 + 4 + 5 = 12
    int manhattan_distance = std::inner_product(
        point1.begin(), point1.end(), point2.begin(), 0,
        std::plus<int>(),  // 加法操作
        [](int a, int b) { return std::abs(a - b); }  // 绝对值差
    );
    std::cout << "Manhattan distance: " << manhattan_distance << std::endl;

    // 示例3:统计相同位置元素相等的次数
    std::vector<int> arr1 = {1, 2, 3, 4, 5};
    std::vector<int> arr2 = {1, 2, 4, 4, 6};
    
    int match_count = std::inner_product(
        arr1.begin(), arr1.end(), arr2.begin(), 0,
        std::plus<int>(),  // 累加匹配次数
        [](int a, int b) { return a == b ? 1 : 0; }  // 相等检查
    );
    std::cout << "Number of matching elements: " << match_count << std::endl;

    return 0;
}

应用场景

  • 向量和矩阵运算

  • 距离计算(欧几里得距离、曼哈顿距离)

  • 统计分析和相关性计算

  • 模式匹配和相似度计算

注意事项

  • 两个序列的长度应该相同,否则行为未定义

  • 自定义操作应该满足数学上的结合律和交换律

  • 对于大型数据集,考虑数值精度和溢出问题

4 std::adjacent_difference - 相邻差值计算算法

算法概述

std::adjacent_difference计算序列中相邻元素的差值,并将结果存储到另一个序列中。第一个元素保持不变或根据实现方式处理。

函数原型

// 版本1:使用默认减法操作
template< class InputIt, class OutputIt >
OutputIt adjacent_difference( InputIt first, InputIt last, OutputIt d_first );

// 版本2:使用自定义二元操作
template< class InputIt, class OutputIt, class BinaryOperation >
OutputIt adjacent_difference( InputIt first, InputIt last, 
                             OutputIt d_first, BinaryOperation op );

参数详解

  • first, last:输入范围的起始和结束迭代器

  • d_first:输出范围的起始迭代器

  • op:自定义二元操作,用于计算相邻元素的"差值"

工作原理

算法计算相邻元素的差值:

*d_first = *first  // 第一个元素直接复制
for each subsequent element:
    *(d_first + i) = op(*(first + i), *(first + i - 1))

代码示例

#include <iostream>
#include <vector>
#include <numeric>  // For std::adjacent_difference
#include <cmath>    // For std::pow

int main() {
    // 示例1:基本用法 - 计算相邻差值
    std::vector<int> numbers = {2, 4, 6, 8, 10};
    std::vector<int> differences(numbers.size());
    
    // 计算相邻元素的差值
    // 结果: [2, 4-2=2, 6-4=2, 8-6=2, 10-8=2]
    std::adjacent_difference(numbers.begin(), numbers.end(), 
                            differences.begin());
    
    std::cout << "Original numbers: ";
    for (int num : numbers) std::cout << num << " ";
    std::cout << "\nDifferences: ";
    for (int diff : differences) std::cout << diff << " ";
    std::cout << std::endl;

    // 示例2:使用自定义操作 - 计算相邻比值
    std::vector<double> values = {1.0, 2.0, 4.0, 8.0, 16.0};
    std::vector<double> ratios(values.size());
    
    // 计算相邻元素的比值
    std::adjacent_difference(values.begin(), values.end(), 
                            ratios.begin(),
                            std::divides<double>());
    
    std::cout << "Values: ";
    for (double val : values) std::cout << val << " ";
    std::cout << "\nRatios: ";
    for (double ratio : ratios) std::cout << ratio << " ";
    std::cout << std::endl;

    // 示例3:计算二阶差分
    std::vector<int> data = {1, 4, 9, 16, 25};
    std::vector<int> first_diff(data.size());
    std::vector<int> second_diff(data.size());
    
    // 计算一阶差分
    std::adjacent_difference(data.begin(), data.end(), first_diff.begin());
    
    // 计算二阶差分(一阶差分的差分)
    std::adjacent_difference(first_diff.begin(), first_diff.end(), 
                            second_diff.begin());
    
    std::cout << "Original data: ";
    for (int d : data) std::cout << d << " ";
    std::cout << "\nFirst differences: ";
    for (int d : first_diff) std::cout << d << " ";
    std::cout << "\nSecond differences: ";
    for (int d : second_diff) std::cout << d << " ";
    std::cout << std::endl;

    return 0;
}

应用场景

  • 时间序列分析(计算变化率)

  • 数值微分近似

  • 信号处理和边缘检测

  • 数据压缩和编码

注意事项

  • 输出序列应该有足够的空间存储结果

  • 第一个元素的处理方式:直接复制或根据实现定义

  • 自定义操作应该满足数学上的合理性

5 std::partial_sum - 部分和计算算法

算法概述

std::partial_sum计算序列的部分和(前缀和),即每个位置存储从开始到该位置的所有元素之和。

函数原型

// 版本1:使用默认加法操作
template< class InputIt, class OutputIt >
OutputIt partial_sum( InputIt first, InputIt last, OutputIt d_first );

// 版本2:使用自定义二元操作
template< class InputIt, class OutputIt, class BinaryOperation >
OutputIt partial_sum( InputIt first, InputIt last, 
                     OutputIt d_first, BinaryOperation op );

参数详解

  • first, last:输入范围的起始和结束迭代器

  • d_first:输出范围的起始迭代器

  • op:自定义二元操作,用于累积计算

工作原理

算法计算部分和:

*d_first = *first
for each subsequent element:
    *(d_first + i) = op(*(d_first + i - 1), *(first + i))

代码示例

#include <iostream>
#include <vector>
#include <numeric>  // For std::partial_sum
#include <functional> // For std::multiplies

int main() {
    // 示例1:基本用法 - 计算前缀和
    std::vector<int> numbers = {1, 2, 3, 4, 5};
    std::vector<int> prefix_sums(numbers.size());
    
    // 计算部分和: [1, 1+2=3, 1+2+3=6, 1+2+3+4=10, 1+2+3+4+5=15]
    std::partial_sum(numbers.begin(), numbers.end(), 
                    prefix_sums.begin());
    
    std::cout << "Numbers: ";
    for (int num : numbers) std::cout << num << " ";
    std::cout << "\nPrefix sums: ";
    for (int sum : prefix_sums) std::cout << sum << " ";
    std::cout << std::endl;

    // 示例2:使用自定义操作 - 计算前缀乘积
    std::vector<int> values = {1, 2, 3, 4, 5};
    std::vector<int> prefix_products(values.size());
    
    // 计算部分乘积: [1, 1*2=2, 1*2*3=6, 1*2*3*4=24, 1*2*3*4*5=120]
    std::partial_sum(values.begin(), values.end(), 
                    prefix_products.begin(), 
                    std::multiplies<int>());
    
    std::cout << "Values: ";
    for (int val : values) std::cout << val << " ";
    std::cout << "\nPrefix products: ";
    for (int product : prefix_products) std::cout << product << " ";
    std::cout << std::endl;

    // 示例3:应用场景 - 范围求和查询
    std::vector<int> data = {10, 20, 30, 40, 50};
    std::vector<int> sums(data.size());
    
    // 预处理:计算前缀和数组
    std::partial_sum(data.begin(), data.end(), sums.begin());
    
    // 快速查询区间[i, j]的和:sums[j] - sums[i-1]
    int i = 1, j = 3;  // 查询data[1]到data[3]的和: 20+30+40=90
    int range_sum = sums[j] - (i > 0 ? sums[i-1] : 0);
    
    std::cout << "Sum of elements from index " << i << " to " << j 
              << ": " << range_sum << std::endl;

    return 0;
}

应用场景

  • 范围求和查询的预处理

  • 累积分布函数计算

  • 数值积分近似

  • 数据分析和统计计算

注意事项

  • 输出序列应该有足够的空间存储结果

  • 自定义操作应该满足结合律

  • 注意数值溢出问题,特别是对于大数据集

性能分析与优化建议

时间复杂度对比

算法

时间复杂度

空间复杂度

适用场景

accumulate

O(n)

O(1)

单值聚合计算

iota

O(n)

O(1)

序列生成

inner_product

O(n)

O(1)

双序列运算

adjacent_difference

O(n)

O(n)

相邻元素操作

partial_sum

O(n)

O(n)

前缀计算

优化建议

  1. 预分配内存:对于需要输出序列的算法,预先分配足够空间

  2. 选择合适算法:根据具体需求选择最合适的算法

  3. 并行化处理:C++17起支持并行执行策略

  4. 数值精度:注意浮点数计算的精度问题

  5. 避免不必要的拷贝:使用移动语义和引用减少拷贝开销

0

评论区