突破编程_C++_STL教程(数值算法(2))

1 std::partial_sum

1.1 基本概念

std::partial_sum 用于计算一个序列的部分和。具体地说,给定一个输入范围(比如一个数组或向量),std::partial_sum 会计算这个范围内每个位置及其之前所有元素的和,并将结果存储在一个输出范围内。

其函数原型如下:

template< class InputIt, class OutputIt >  
OutputIt partial_sum( InputIt first, InputIt last, OutputIt d_first );  
  
template< class InputIt, class OutputIt, class BinaryOperation >  
OutputIt partial_sum( InputIt first, InputIt last, OutputIt d_first, BinaryOperation binary_op );
  • first 和 last 是输入范围的迭代器。
  • d_first 是输出范围的开始迭代器。
  • binary_op 是一个二元操作,用于替代默认的加法操作

1.2 基本用法

假设有一个整数向量 v,现在需要计算它的部分和:

#include <iostream>  
#include <vector>  
#include <numeric> // for std::partial_sum  
#include <iterator> // for std::back_inserter  
  
int main() {  
    std::vector<int> v = {1, 2, 3, 4, 5};  
    std::vector<int> partialSums;  
  
    // 使用 back_inserter 来自动调整 partialSums 的大小  
    std::partial_sum(v.begin(), v.end(), std::back_inserter(partialSums));  
  
    for (int sum : partialSums) {  
        std::cout << sum << ' ';  
    }  
    // 输出: 1 3 6 10 15  
    return 0;  
}

上面代码的输出为:

1 3 6 10 15

在这个例子中,partialSums 会包含以下元素:[1, 1+2, 1+2+3, 1+2+3+4, 1+2+3+4+5]。

1.3 使用自定义二元操作

还可以提供一个自定义的二元操作来替代默认的加法。例如,如果需要计算部分乘积而不是部分和,则可以这样做:

#include <iostream>  
#include <vector>  
#include <numeric> // for std::partial_sum  
#include <iterator> // for std::back_inserter  
  
int main() {  
    std::vector<int> v = {1, 2, 3, 4, 5};  
    std::vector<int> partialSums;  
  
    // 使用 back_inserter 来自动调整 partialSums 的大小  
    std::partial_sum(v.begin(), v.end(), std::back_inserter(partialSums));  
  
    for (int sum : partialSums) {  
        std::cout << sum << ' ';  
    }  
    // 输出: 1 3 6 10 15  
    return 0;  
}

上面代码的输出为:

1 2 6 24 120

在这个例子中,partialProducts 会包含以下元素:[1, 12, 123, 1234, 12345]。

1.4 使用自定义数据结构

std::partial_sum 可以应用于任何可以定义加法操作的数据类型,包括自定义的数据结构。对于自定义的数据结构,只需要需要重载加法操作符(operator+)或提供一个自定义的二元操作函数对象或 lambda 表达式,这样 std::partial_sum 才能知道如何计算部分和。

下面是一个使用 std::partial_sum 对自定义数据结构进行部分求和的示例:

首先,定义一个自定义的数据结构,并重载加法操作符:

#include <iostream>  
#include <vector>  
#include <numeric> // for std::partial_sum  
#include <iterator> // for std::back_inserter  

// 自定义数据结构  
struct ComplexNumber {
	double real;
	double imag;

	// 重载加法操作符  
	ComplexNumber operator+(const ComplexNumber& other) const {
		return { real + other.real, imag + other.imag };
	}

	// 重载输出操作符,方便打印  
	friend std::ostream& operator<<(std::ostream& os, const ComplexNumber& cn) {
		os << "(" << cn.real << ", " << cn.imag << ")";
		return os;
	}
};

int main() {
	std::vector<ComplexNumber> complexNumbers = {
		{1.0, 2.0},
		{3.0, 4.0},
		{5.0, 6.0}
	};
	std::vector<ComplexNumber> partialSums;

	// 使用 std::partial_sum 计算部分和  
	std::partial_sum(complexNumbers.begin(), complexNumbers.end(), std::back_inserter(partialSums));

	// 打印结果  
	for (const auto& sum : partialSums) {
		std::cout << sum << ' ';
	}
	// 输出: (1, 2) (4, 6) (9, 12)  

	return 0;
}

上面代码的输出为:

(1, 2) (4, 6) (9, 12)

这个例子定义了一个 ComplexNumber 结构体来表示复数,并重载了加法操作符 operator+。这样就可以直接对 ComplexNumber 对象使用 std::partial_sum。程序会输出每个 ComplexNumber 对象及其之前所有对象的和。

如果不想或不能重载加法操作符,则可以提供一个自定义的二元操作函数对象或 lambda 表达式给 std::partial_sum 的第四个参数。例如:

#include <iostream>  
#include <vector>  
#include <numeric> // for std::partial_sum  
#include <iterator> // for std::back_inserter  
#include <functional> // for std::plus  
  
// 自定义数据结构(这次不重载加法操作符)  
struct ComplexNumber {  
    double real;  
    double imag;  
  
    // ... 其他成员函数和操作符 ...  
};  
  
// 自定义二元操作,用于计算两个 ComplexNumber 的和  
struct ComplexNumberAdd {  
    ComplexNumber operator()(const ComplexNumber& a, const ComplexNumber& b) const {  
        return {a.real + b.real, a.imag + b.imag};  
    }  
};  
  
int main() {  
    std::vector<ComplexNumber> complexNumbers = {  
        {1.0, 2.0},  
        {3.0, 4.0},  
        {5.0, 6.0}  
    };  
    std::vector<ComplexNumber> partialSums;  
  
    // 使用自定义的二元操作计算部分和  
    std::partial_sum(complexNumbers.begin(), complexNumbers.end(), std::back_inserter(partialSums), ComplexNumberAdd());  
  
    // 打印结果(同前例)  
    for (const auto& sum : partialSums) {  
        std::cout << "(" << sum.real << ", " << sum.imag << ") ";  
    }  
    // 输出: (1, 2) (4, 6) (9, 12)  
  
    return 0;  
}

上面代码的输出为:

(1, 2) (4, 6) (9, 12)

这个例子定义了一个名为 ComplexNumberAdd 的结构体,它重载了调用操作符 operator() 来计算两个 ComplexNumber 对象的和。然后将这个自定义的二元操作作为第四个参数传递给 std::partial_sum。这样,std::partial_sum 就可以使用这个自定义操作来计算 ComplexNumber 对象的部分和了。

2 std::adjacent_difference

2.1 基本概念

std::adjacent_difference 用于计算一个序列中相邻元素的差值(或更一般地说,是相邻元素之间通过某个二元操作得到的结果)。它通常用于计算序列中每个元素与其前一个元素之间的差值,并将结果存储在一个输出序列中。

其函数原型如下:

template< class InputIt, class OutputIt >  
OutputIt adjacent_difference( InputIt first, InputIt last, OutputIt d_first );  
  
template< class InputIt, class OutputIt, class BinaryOperation >  
OutputIt adjacent_difference( InputIt first, InputIt last, OutputIt d_first, BinaryOperation binary_op );
  • first 和 last 是输入范围的迭代器。
  • d_first 是输出范围的开始迭代器。
  • binary_op 是一个二元操作,用于替代默认的加法操作

2.2 基本用法

假设有一个整数向量 v,现在需要计算其中每个元素与其前一个元素之间的差值:

#include <iostream>  
#include <vector>  
#include <numeric> // for std::adjacent_difference  
#include <iterator> // for std::back_inserter  
  
int main() {  
    std::vector<int> v = {1, 3, 6, 10, 15};  
    std::vector<int> differences;  
  
    // 使用 back_inserter 来自动调整 differences 的大小  
    std::adjacent_difference(v.begin(), v.end(), std::back_inserter(differences));  
  
    // 输出结果  
    for (int diff : differences) {  
        std::cout << diff << ' ';  
    }  
  
    return 0;  
}

上面代码的输出为:

1 2 3 4 5

在这个例子中,differences 将会包含以下元素:[1, 3-1, 6-3, 10-6, 15-10],即 [1, 2, 3, 4, 5]。注意,第一个元素与它自己的差值就是它本身,因此 differences 的第一个元素是 v 的第一个元素。

2.3 使用自定义二元操作

也可以提供一个自定义的二元操作来替代默认的减法。例如,如果想计算相邻元素的乘积而不是差值,则可以这样做:

#include <iostream>  
#include <vector>  
#include <numeric> // for std::adjacent_difference  
#include <iterator> // for std::back_inserter  
#include <functional> // for std::multiplies  

int main() {
	std::vector<int> v = { 1, 2, 3, 4, 5 };
	std::vector<int> products;

	// 使用 std::multiplies 作为二元操作来计算相邻元素的乘积  
	std::adjacent_difference(v.begin(), v.end(), std::back_inserter(products), std::multiplies<int>());

	// 输出结果  
	for (int prod : products) {
		std::cout << prod << ' ';
	}

	return 0;
}

上面代码的输出为:

1 2 6 12 20

在这个例子中,products 将会包含以下元素:[1, 21, 32, 43, 54],即 [1, 2, 6, 12, 60]。

2.4 使用自定义数据结构

std::adjacent_difference 算法可以应用于任何支持二元操作的类型,包括自定义数据结构。对于自定义数据结构,只需要确保你提供了合适的二元操作,并且这个操作能够处理自定义数据结构类型的两个对象,并返回一个新的对象。

例如,假设有一个自定义的 Point 类,表示二维平面上的点,并且想要计算一系列点中相邻点之间的差(即向量)。则可以这样实现:

#include <iostream>  
#include <vector>  
#include <numeric> // for std::adjacent_difference  
#include <iterator> // for std::back_inserter  
  
// 自定义的 Point 类  
class Point {  
public:  
    int x, y;  
  
    Point(int x = 0, int y = 0) : x(x), y(y) {}  
  
    // 重载减法操作符,返回两个点之间的向量差  
    Point operator-(const Point& other) const {  
        return Point(x - other.x, y - other.y);  
    }  
  
    // 重载输出操作符,方便打印 Point 对象  
    friend std::ostream& operator<<(std::ostream& os, const Point& p) {  
        os << "(" << p.x << ", " << p.y << ")";  
        return os;  
    }  
};  
  
int main() {  
    // 创建一个包含多个点的向量  
    std::vector<Point> points = {  
        {1, 2},  
        {4, 6},  
        {7, 10},  
        {10, 14}  
    };  
  
    // 创建一个向量来存储相邻点之间的差(向量)  
    std::vector<Point> differences;  
  
    // 使用 std::adjacent_difference 计算相邻点之间的差,并将结果存储到 differences 中  
    std::adjacent_difference(points.begin(), points.end(), std::back_inserter(differences));  
  
    // 输出结果  
    for (const Point& diff : differences) {  
        std::cout << diff << ' ';  
    }  
  
    return 0;  
}

上面代码的输出为:

(1, 2) (3, 4) (3, 4) (3, 4)

在这个例子中,Point 类重载了减法操作符,以便 std::adjacent_difference 可以使用它来计算相邻点之间的向量差。std::adjacent_difference 默认使用减法操作符,但如果你的数据结构不支持减法或者你需要执行其他类型的二元操作,你可以提供一个自定义的二元操作函数对象或 lambda 表达式作为 std::adjacent_difference 的第四个参数。

3 std::iota

3.1 基本概念

std::iota 用于将一个序列的元素填充为连续递增的值。这个算法特别适用于初始化一个容器,使其包含一系列连续递增的整数或其他类型的值。

其函数原型如下:

template< class ForwardIt, class T >  
void iota( ForwardIt first, ForwardIt last, T value );
  • first 和 last 是表示序列范围的迭代器,first 指向序列中的第一个元素,last 指向序列“尾后”的位置(即不包含在填充范围内的位置)。
  • value 是序列中第一个元素的值,随后的元素将依次递增。

3.2 基本用法

std::iota 的用法非常简单。只需要提供序列的起始和结束迭代器,以及序列中第一个元素的值即可。下面是一个例子,展示了如何使用 std::iota 来初始化一个 std::vector<int>:

#include <iostream>  
#include <vector>  
#include <numeric> // for std::iota  

int main() {
	std::vector<int> v(10); // 创建一个包含10个元素的vector,所有元素初始化为0  

	// 使用std::iota将vector的元素初始化为从0开始的连续整数  
	std::iota(v.begin(), v.end(), 0);

	// 输出vector的内容  
	for (int i : v) {
		std::cout << i << ' ';
	}
	return 0;
}

上面代码的输出为:

0 1 2 3 4 5 6 7 8 9  

这个例子创建了一个包含 10 个元素的 std::vector<int>。然后,使用 std::iota 来初始化这个 vector,使其包含从 0 开始的连续整数。最后,遍历并打印出 vector 中的所有元素。

3.3 使用自定义数据结构

std::iota 算法通常用于生成连续递增的整数序列,但如果需要在一个容器中使用自定义数据结构的对象,并让这些对象之间以一种特定的方式“递增”,则要确保自定义数据结构支持递增操作,并且这个操作是符合期望的。

对于自定义数据结构,递增操作通常通过重载 ++ 操作符来实现。然而,std::iota 默认使用 ++ 操作符以递增的方式填充序列,这可能并不适用于所有类型的自定义数据结构。如果自定义数据结构不适合使用递增操作符,或者递增的含义不符合实际的需求,那么 std::iota 可能不是最佳选择。

但是,如果自定义数据结构确实可以通过递增来生成序列,并且需要使用 std::iota,则可以这样做:

  • 定义自定义数据结构,并重载 ++ 操作符。
  • 创建一个容器,用于存储自定义数据结构的对象。
  • 使用 std::iota 初始化这个容器,传入起始迭代器、结束迭代器和初始值。

下面是一个简单的例子,展示了如何为自定义数据结构使用 std::iota:

#include <iostream>  
#include <vector>  
#include <numeric> // for std::iota  

// 自定义数据结构  
class MyData {
public:
	int value;

	// 构造函数  
	MyData(int val = 0) : value(val) {}

	// 重载递增操作符  
	MyData& operator++() {
		++value; // 递增内部值  
		return *this;
	}

	// 重载输出操作符,方便打印对象  
	friend std::ostream& operator<<(std::ostream& os, const MyData& data) {
		os << data.value;
		return os;
	}
};

int main() {
	// 创建一个存储自定义数据结构的vector  
	std::vector<MyData> vec(5); // 创建一个包含5个MyData对象的vector  

	// 使用std::iota初始化vector,从MyData(1)开始递增  
	MyData initialValue(1); // 初始值  
	std::iota(vec.begin(), vec.end(), initialValue);

	// 输出vector的内容  
	for (const MyData& data : vec) {
		std::cout << data << ' ';
	} 

	return 0;
}

上面代码的输出为:

1 2 3 4 5

这个例子定义了一个 MyData 类,它包含一个 int 类型的 value 成员。然后重载了 ++ 操作符来递增 value 的值,并重载了输出操作符以便能够打印 MyData 对象。接下来,创建了一个 std::vector<MyData>,并使用 std::iota 来初始化它,从 MyData(1) 开始,并依次递增每个对象的 value 值。

注意:重载 ++ 操作符通常用于前置递增(如 ++x),但 std::iota 使用的是后置递增(如 x++)的语义。在 C++ 中,前置和后置递增可以有不同的行为,但在这个例子中,我们只关心递增操作本身,而不关心返回值,所以简单递增 value 并返回当前对象的引用是足够的。

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值