C++(20/23)标准模板库编程 - 19 数值处理 - 第二部分

本章涵盖更多数值处理类与算法,主要内容包括:

  • 使用 std::valarray
  • 使用 std::slice
  • 内积
  • 归约运算

与前一章类似,本章部分讨论假定读者已具备某些数学学科的基础知识。您可以根据编程兴趣选择略读或跳过无关章节。

类 std::valarray


模板类 std::valarray 是专为数值运算设计的一维类数组结构。与 std::array 和 std::vector 等类似容器不同,std::valarray 类排除了某些形式的别名使用,这使得编译器能够进行更激进的优化。通过结合 std::valarray 类和 std::slice 类,可以构建多维数组模型。后续章节将对此进行更详细的讲解。

算术运算函数


代码清单 19-1-1 展示了示例函数 Ch19_01_ex1() 的源代码。该示例演示了使用 std::valarray 类实例进行基础运算的几种方法。

//-------------------------------------------------------------------------
// Ch19_01_ex.cpp
//-------------------------------------------------------------------------
#include <algorithm>
#include <numeric>
#include <valarray>
#include "Ch19_01.h"
#include "MT.h"
#include "RN.h"
void Ch19_01_ex1()
{
    const char* fmt1 = "{:7d}";
    const char* fmt2 = "{:7.1f}";
    constexpr size_t epl_max {10};
    constexpr size_t n {25};
    constexpr int rng_min {1};
    constexpr int rng_max {500};
    constexpr unsigned int rng_seed {19011};
    // using std::valarray<int>
    std::valarray va1 = RN::get_valarray<int>(n, rng_min, rng_max, rng_seed);
    MT::print_ctr("\nva1:\n", va1, fmt1, epl_max);
    std::println("\nva1 - sum: {:d}, min: {:d}, max: {:d}",
        va1.sum(), va1.min(), va1.max());
    // using std::valarray<> operator+=
    va1 += 5;
    MT::print_ctr("\nva1 (after operator+=):\n", va1, fmt1, epl_max);
    // using std::valarray<double>
    std::valarray va2 = RN::get_valarray<double>(n, rng_min, rng_max, rng_seed + 1);
    MT::print_ctr("\nva2:\n", va2, fmt2, epl_max);
    std::println("\nva2 - sum: {:.1f}, min: {:.1f}, max: {:.1f}",
        va2.sum(), va2.min(), va2.max());
    // using std::valarray<> operator-=
    va2 -= 0.5;
    MT::print_ctr("\nva2 (after operator-=):\n", va2, fmt2, epl_max);
    // using operator[]
    for (size_t i = 0; i < n; ++i)
        va2[i] = static_cast<double>(va1[i] % 3);
    MT::print_ctr("\nva2 (after operator[] calculations):\n",
        va2, fmt2, epl_max);
}

在 Ch19_01_ex1() 开头的代码块中,执行 std::valarray va1 = RN::get_valarray<int>(n, rng_min, rng_max, rng_seed) 会初始化 va1,使其包含 n 个介于[rng_min, rng_max]之间的随机整数。函数 RN::get_valarray() 是 std::valarray 版本的 RN::get_vector()(参见代码清单 18-5-1-1 和文件 Common/RN.h)。执行 va1.sum() 会计算 va1 中所有元素的总和,而 va1.min() 和 v1.max() 则分别计算最小值和最大值。在接下来的代码块中,执行 va1 += 5 会给 va1 中的每个元素加 5。

清单 19-1-1 中的下一个 std::valarray 示例使用 std::valarray va2 = RN::get_valarray<double>(n, rng_min, rng_max, rng_seed) 来初始化 va2 的随机值 。与之前示例类似,执行 va2.sum() 将对 va2 的元素求和,而 va2.min() 和 va2.max() 则分别确定 va2 的最小值与最大值。语句 va2 -= 0.5 会从 va2 的每个元素中减去 0.5。其他常见算术运算符同样可用于调整 std::valarray 中的元素。

Ch19_01_ex1() 中的最后一个代码块展示了如何使用 operator[] 访问 std::valarray 中的单个元素。与 std::array 和 std::vector 类似,std::valarray 的 operator[] 不会检查无效索引;若使用无效索引,其执行行为是未定义的。std::valarray 类也没有定义边界检查成员函数 at()。

示例函数 Ch19_01_ex2()(如代码清单 19-1-2 所示)重点展示了使用 std::valarray 类实例的更多操作。Ch19_01_ex2() 的执行始于测试用 std::valarray 对象 va1、va2 和 va3 的初始化。请注意前两个包含 int 类型元素,而后者包含 long long 类型元素。
void Ch19_01_ex2()
{
    const char* fmt = "{:7d}";
    constexpr size_t epl_max {10};
    // create test valarray<> objects
    std::valarray<int> va1 {10, 20, 30, 40, 50};
    std::valarray<int> va2 {100, 200, 300, 400, 500, 600, 700, 800};
    std::valarray<long long> va3 {1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000};
    MT::print_ctr("\nva1:\n", va1, fmt, epl_max);
    MT::print_ctr("\nva2:\n", va2, fmt, epl_max);
    MT::print_ctr("\nva3:\n", va3, fmt, epl_max);
    // using valarray<>::operator=
    va2 = va1;
    MT::print_ctr("\nva2 (after operator=):\n", va1, fmt, epl_max);
//  va3 = va1;    // illegal - different element types
    // using valarray<>::apply
    auto apply_op = [](int x) { return x * x - 1; };
    std::valarray<int> va4 = va1.apply(apply_op);
    MT::print_ctr("\nva4:\n", va4, fmt, epl_max);
}

在调用 print_ctr() 之后,执行 va2 = va1 将 va1 赋值给 va2。类 std::valarray 的赋值运算符仅支持使用持有相同元素类型的对象进行赋值。语句 va3 = va1 被注释掉,因为它是无效的;对象 va3 持有 long long 类型,而 va1 包含 int 类型。Ch19_01_ex2() 的最后代码块演示了 std::valarray::apply() 的使用。执行 std::valarray<int> va4 = va1.apply(apply_op) 会返回一个新的 std::valarray,其值对应于 va4[i] = apply_op(va1[i])。

在代码清单 19-1-3 中,示例函数 Ch19_01_ex3() 使用了 std::iota(std::begin(va1), std::end(va1), fp_t {1}) 来初始化 std::valarray<double> va1(n)。与大多数其他 STL 容器不同,类 std::valarray 没有定义迭代器成员函数 begin() 和 end(),必须使用全局函数 std::begin() 和 std::end() 来获取 std::valarray 实例的迭代器。

void Ch19_01_ex3()
{
    const char* fmt = "{:12.4f}";
    constexpr size_t epl_max {5};
    // create test valarray<>
    // must use std::begin() and std::end() for std::valarray iterators
    constexpr size_t n {20};
    std::valarray<double> va1(n);
    std::iota(std::begin(va1), std::end(va1), 1.0);
    MT::print_ctr("\nva1:\n", va1, fmt, epl_max);
    // using std::valarray<> math overloads
    std::valarray<double> va2 = std::sqrt(va1);
    MT::print_ctr("\nva2 (after sqrt):\n", va2, fmt, epl_max);
    va2 = std::pow(va2, 3.0);
    MT::print_ctr("\nva2 (after pow):\n", va2, fmt, epl_max);
    va2 = std::log10(va1);
    MT::print_ctr("\nva2 (after log10):\n", va2, fmt, epl_max);
}

Ch19_01_ex3() 中的剩余代码展示了 std::valarray 非成员函数 std::sqrt()、std::pow() 和 std::log10() 的用法。这些函数都会对指定 std::valarray 中的每个元素执行数学运算。STL 还为常见的三角函数和双曲函数运算定义了 std::valarray 重载。

在代码清单 19-1-4 中,示例函数 Ch19_01_ex4() 演示了 operator<、operator== 和 operator> 的使用。这些运算符返回类型为 std::valarray<bool> 的对象。例如,执行 std::valarray<bool> va_lt = va1 < va2 会返回一个 std::valarray<bool>,其第 i 个元素等于 va1[i] < va2[i]。使用 std::valarray 进行比较时,两个数组必须包含相同数量的元素。

void Ch19_01_ex4()
{
    const char* fmt = "{:7.1f}";
    constexpr size_t epl_max {11};
    constexpr size_t n {10};
    // create test valarray<> objects
    std::valarray<float> va1(n);
    std::iota(std::begin(va1), std::end(va1), 1.0f);
    std::valarray<float> va2(va1);
    va2[0] += 1.0f;
    va2[n / 4] -= 2.0f;
    va2[n / 2] *= 3.0f;
    va2[n - 1] /= 4.0f;
    MT::print_ctr("\nva1:\n", va1, fmt, epl_max);
    MT::print_ctr("\nva2:\n", va2, fmt, epl_max);
    // using operator< (returns std::valarray<bool>)
    std::valarray<bool> va_lt = va1 < va2;
    MT::print_ctr("\nva_lt:\n", va_lt, "{:>7s}", epl_max);
    // using operator== (returns std::valarray<bool>)
    std::valarray<bool> va_eq = va1 == va2;
    MT::print_ctr("\nva_cmp:\n", va_eq, "{:>7s}", epl_max);
    // using operator> (returns std::valarray<bool>)
    std::valarray<bool> va_gt = va1 > va2;
    MT::print_ctr("\nva_gt:\n", va_gt, "{:>7s}", epl_max);
}

以下是示例 Ch19_01 的运行结果:

----- Results for example Ch19_01 -----
----- Ch19_01_ex1() -----
va1:
     16    454    341    466    394    276    327    115    203    373
    450    260    219    491     18    487    187     84    166     64
     22    131    222    210    129
va1 - sum: 6105, min: 16, max: 491
va1 (after operator+=):
     21    459    346    471    399    281    332    120    208    378
    455    265    224    496     23    492    192     89    171     69
     27    136    227    215    134
va2:
   70.0  238.0  206.0   77.0  330.0  210.0  473.0  453.0  210.0  135.0
   82.0  222.0  211.0  217.0   86.0   72.0  330.0  425.0  241.0  408.0
  124.0   60.0  461.0  335.0  259.0
va2 - sum: 5935.0, min: 60.0, max: 473.0
va2 (after operator-=):
   69.5  237.5  205.5   76.5  329.5  209.5  472.5  452.5  209.5  134.5
   81.5  221.5  210.5  216.5   85.5   71.5  329.5  424.5  240.5  407.5
  123.5   59.5  460.5  334.5  258.5
va2 (after operator[] calculations):
    0.0    0.0    1.0    0.0    0.0    2.0    2.0    0.0    1.0    0.0
    2.0    1.0    2.0    1.0    2.0    0.0    0.0    2.0    0.0    0.0
    0.0    1.0    2.0    2.0    2.0
----- Ch19_01_ex2() -----
va1:
     10     20     30     40     50
va2:
    100    200    300    400    500    600    700    800
va3:
   1000   2000   3000   4000   5000   6000   7000   8000
va2 (after operator=):
     10     20     30     40     50
va4:
     99    399    899   1599   2499
----- Ch19_01_ex3() -----
va1:
      1.0000      2.0000      3.0000      4.0000      5.0000
      6.0000      7.0000      8.0000      9.0000     10.0000
     11.0000     12.0000     13.0000     14.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

akluse

失业老程序员求打赏,求买包子钱

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值