查找 max/min 个向量向量
Find max/min of vector of vectors
找到向量的向量的 max/min 项的最有效和标准 (C++11/14) 方法是什么?
std::vector<std::vector<double>> some_values{{5,0,8},{3,1,9}};
想要的最大元素是9
想要的最小元素是 0
任何计算二维数组(或您的情况下的向量)中最大元素的有效方法都涉及 O(n^2)
的复杂性,无论您做什么,因为计算涉及 n*n
elements.Best 方式在易用性方面是使用 std::max_element
向量上 vectors.I 不会深究 details.Here 是 reference.
最简单的方法是首先有一个函数来确定一个向量的 max/min 个元素,比如说一个函数叫做:
double getMaxInVector(const vector<double>& someVec){}
在这种情况下通过引用传递(仅供阅读)将花费更多时间并且 space 高效(您不希望您的函数复制整个向量)。因此,在确定向量的向量的 max/min 元素的函数中,您将有一个嵌套循环,例如:
for(size_t x= 0; x < some_values.size(); x++){
for(size_t y = 0; y < x.size(); y++){
// y represents the vectors inside the vector of course
// current max/min = getMax(y)
// update max/min after inner loop finishes and x increments
// by comparing it with previous max/min
上述解决方案的问题是效率低下。据我所知,这个算法通常会 运行 O(n^2log(n)) 效率,这是相当不起眼的。但当然,它仍然是一个解决方案。虽然可能有标准算法可以为您找到向量的 max/min,但编写自己的总是更有成就感,并且使用给定的算法通常不会提高效率,因为算法通常是相同(对于确定 max/min 的小函数)。事实上,理论上,标准函数会 运行 稍微慢一些,因为这些函数是模板,必须在 运行 时间确定它正在处理的类型。
您必须至少查看每个元素,因此,正如 Anony-mouse 提到的,复杂度至少为 O(n^2).
#include <vector>
#include <limits>
#include <algorithm>
int main() {
std::vector<std::vector<double>> some_values;
double max = std::numeric_limits<double>::lowest();
for (const auto& v : some_values)
{
double current_max = *std::max_element(v.cbegin(), v.cend());
max = max < current_max ? current_max : max; // max = std::max(current_max, max);
}
}
使用 accumulate
函数你可以这样写:
#include <iostream>
#include <numeric>
#include <vector>
int main()
{
std::vector<std::vector<double>> m{ {5, 0, 8}, {3, 1, 9} };
double x = std::accumulate(m.begin(), m.end(), m[0][0],
[](double max, const std::vector<double> &v)
{
return std::max(max,
*std::max_element(v.begin(),
v.end()));
});
std::cout << x << '\n';
return 0;
}
但我更喜欢好的、旧的 for 循环。
可以扩展该示例以查找最小值和最大值:
std::accumulate(m.begin(), m.end(),
std::make_pair(m[0][0], m[0][0]),
[](std::pair<double, double> minmax, const std::vector<double> &v)
{
auto tmp(std::minmax_element(v.begin(), v.end()));
return std::make_pair(
std::min(minmax.first, *tmp.first),
std::max(minmax.second, *tmp.second));
});
(在真正的代码中你必须处理空向量的情况)
不幸的是,一个向量的向量没有连续存储在内存中,所以你没有一个包含所有值的块(这是向量的向量不是一个好的模型的原因之一矩阵)。
如果包含很多元素,您可以利用向量的向量。
由于每个子向量都是自治的,您可以使用 std::async 异步填充包含每个子向量最大值的期货向量。
这里有一个多线程的解决方案,returns一个迭代器(或抛出)最大为通用类型T
(假设operator<
定义为T
) .请注意,最重要的优化是对 'columns' 执行内部最大操作以利用 C++ 的列优先顺序。
#include <vector>
#include <algorithm>
template <typename T>
typename std::vector<T>::const_iterator max_element(const std::vector<std::vector<T>>& values)
{
if (values.empty()) throw std::runtime_error {"values cannot be empty"};
std::vector<std::pair<typename std::vector<T>::const_iterator, bool>> maxes(values.size());
threaded_transform(values.cbegin(), values.cend(), maxes.begin(),
[] (const auto& v) {
return std::make_pair(std::max_element(v.cbegin(), v.cend()), v.empty());
});
auto it = std::remove_if(maxes.begin(), maxes.end(), [] (auto p) { return p.second; });
if (it == maxes.begin()) throw std::runtime_error {"values cannot be empty"};
return std::max_element(maxes.begin(), it,
[] (auto lhs, auto rhs) {
return *lhs.first < *rhs.first;
})->first;
}
threaded_transform
还不是标准库的一部分,但这里有一个您可以使用的实现。
#include <vector>
#include <thread>
#include <algorithm>
#include <cstddef>
template <typename InputIterator, typename OutputIterator, typename UnaryOperation>
OutputIterator threaded_transform(InputIterator first, InputIterator last, OutputIterator result, UnaryOperation op, unsigned num_threads)
{
std::size_t num_values_per_threads = std::distance(first, last) / num_threads;
std::vector<std::thread> threads;
threads.reserve(num_threads);
for (int i = 1; i <= num_threads; ++i) {
if (i == num_threads) {
threads.push_back(std::thread(std::transform<InputIterator,
OutputIterator, UnaryOperation>,
first, last, result, op));
} else {
threads.push_back(std::thread(std::transform<InputIterator,
OutputIterator, UnaryOperation>,
first, first + num_values_per_threads,
result, op));
}
first += num_values_per_threads;
result += num_values_per_threads;
}
for (auto& thread : threads) thread.join();
return result;
}
template <typename InputIterator, typename OutputIterator, typename UnaryOperation>
OutputIterator threaded_transform(InputIterator first, InputIterator last, OutputIterator result, UnaryOperation op)
{
return threaded_transform<InputIterator, OutputIterator, UnaryOperation>(first, last, result, op, std::thread::hardware_concurrency());
}
如果您创建一个自定义迭代器来迭代 vector
的 double
或 vector
,一个简单的 std::minmax_element
就可以完成工作
迭代器类似于:
class MyIterator : public std::iterator<std::random_access_iterator_tag, double>
{
public:
MyIterator() : container(nullptr), i(0), j(0) {}
MyIterator(const std::vector<std::vector<double>>& container,
std::size_t i,
std::size_t j) : container(&container), i(i), j(j)
{
// Skip empty container
if (i < container.size() && container[i].empty())
{
j = 0;
++(*this);
}
}
MyIterator(const MyIterator& rhs) = default;
MyIterator& operator = (const MyIterator& rhs) = default;
MyIterator& operator ++() {
if (++j >= (*container)[i].size()) {
do {++i;} while (i < (*container).size() && (*container)[i].empty());
j = 0;
}
return *this;
}
MyIterator operator ++(int) { auto it = *this; ++(*this); return it; }
MyIterator& operator --() {
if (j-- == 0) {
do { --i; } while (i != 0 && (*container)[i].empty());
j = (*container)[i].size();
}
return *this;
}
MyIterator operator --(int) { auto it = *this; --(*this); return it; }
double operator *() const { return (*container)[i][j]; }
bool operator == (const MyIterator& rhs) const {
return container == rhs.container && i == rhs.i && j == rhs.j;
}
bool operator != (const MyIterator& rhs) const { return !(*this == rhs); }
private:
const std::vector<std::vector<double>>* container;
std::size_t i;
std::size_t j;
};
并且用法可能是
// Helper functions for begin/end
MyIterator MyIteratorBegin(const std::vector<std::vector<double>>& container)
{
return MyIterator(container, 0, 0);
}
MyIterator MyIteratorEnd(const std::vector<std::vector<double>>& container)
{
return MyIterator(container, container.size(), 0);
}
int main() {
std::vector<std::vector<double>> values = {{5,0,8}, {}, {3,1,9}};
auto b = MyIteratorBegin(values);
auto e = MyIteratorEnd(values);
auto p = std::minmax_element(b, e);
if (p.first != e) {
std::cout << "min is " << *p.first << " and max is " << *p.second << std::endl;
}
}
您可以使用 Eric Niebler 的 range-v3 库轻松地做到这一点(这显然还不是标准,但希望在不久的将来会成为标准):
vector<vector<double>> some_values{{5,0,8},{3,1,9}};
auto joined = some_values | ranges::view::join;
auto p = std::minmax_element(joined.begin(), joined.end());
p.first
是指向 min 元素的迭代器; p.second
到最大值
(range-v3 确实有 minmax_element 的实现,但不幸的是,它需要一个 ForwardRange,而 view::join 只给了我一个 InputRange,所以我不能使用它。)
如果您使用 boost::multi_array<double, 2>
而不是 std::vector<std::vector<double>>
,它会很简单:
auto minmax = std::minmax_element(values.data(), values.data() + values.num_elements());
普通for loop
方式:
T max_e = std::numeric_limits<T>::min();
for(const auto& v: vv) {
for(const auto& e: v) {
max_e = std::max(max_e, e);
}
}
假设我们有一个名为 some_values 的向量,如下所示
7 4 2 0
4 8 10 8
3 6 7 6
3 9 19* 14
定义一个一维向量如下图
vector<int> oneDimVector;
for(int i = 0; i < 4; i++){
for(int j = 0; j < 4; j++){
oneDimVector.push_back(some_values[i][j]);
}
}
然后在那个一维向量中找出一个maximum/minimum元素如下图
vector<int>::iterator maxElement = max_element(oneDimVector.begin(),oneDimVector.end());
vector<int>::iterator minElement = min_element(oneDimVector.begin(),oneDimVector.end());
现在你得到 max/min 个元素如下
cout << "Max element is " << *maxElement << endl;
cout << "Min element is " << *minElement << endl;
vector<vector<int>> vv = { vector<int>{10,12,43,58}, vector<int>{10,14,23,18}, vector<int>{28,47,12,90} };
vector<vector<int>> vv1 = { vector<int>{22,24,43,58}, vector<int>{56,17,23,18}, vector<int>{11,12,12,90} };
int matrix1_elem_sum=0;
int matrix2_elem_sum = 0;
for (size_t i = 0; i < vv.size(); i++)
{
matrix1_elem_sum += std::accumulate(vv[i].begin(), vv[i].end(), 0);
matrix2_elem_sum += std::accumulate(vv1[i].begin(), vv1[i].end(), 0);
}
cout << matrix1_elem_sum <<endl;
cout << matrix2_elem_sum << endl;
int summ = matrix1_elem_sum + matrix2_elem_sum;
cout << summ << endl;
或优化变体:
vector<vector<int>> vv = { vector<int>{10,12,43,58}, vector<int>{10,14,23,18}, vector<int>{28,47,12,90} };
vector<vector<int>> vv1 = { vector<int>{22,24,43,58}, vector<int>{56,17,23,18}, vector<int>{11,12,12,90} };
int summ=0;
int matrix2_elem_sum = 0;
for (size_t i = 0; i < vv.size(); i++)
{
summ += std::accumulate(vv[i].begin(), vv[i].end(), 0)+ std::accumulate(vv1[i].begin(), vv1[i].end(), 0);
}
cout << summ << endl;
}
找到向量的向量的 max/min 项的最有效和标准 (C++11/14) 方法是什么?
std::vector<std::vector<double>> some_values{{5,0,8},{3,1,9}};
想要的最大元素是9
想要的最小元素是 0
任何计算二维数组(或您的情况下的向量)中最大元素的有效方法都涉及 O(n^2)
的复杂性,无论您做什么,因为计算涉及 n*n
elements.Best 方式在易用性方面是使用 std::max_element
向量上 vectors.I 不会深究 details.Here 是 reference.
最简单的方法是首先有一个函数来确定一个向量的 max/min 个元素,比如说一个函数叫做:
double getMaxInVector(const vector<double>& someVec){}
在这种情况下通过引用传递(仅供阅读)将花费更多时间并且 space 高效(您不希望您的函数复制整个向量)。因此,在确定向量的向量的 max/min 元素的函数中,您将有一个嵌套循环,例如:
for(size_t x= 0; x < some_values.size(); x++){
for(size_t y = 0; y < x.size(); y++){
// y represents the vectors inside the vector of course
// current max/min = getMax(y)
// update max/min after inner loop finishes and x increments
// by comparing it with previous max/min
上述解决方案的问题是效率低下。据我所知,这个算法通常会 运行 O(n^2log(n)) 效率,这是相当不起眼的。但当然,它仍然是一个解决方案。虽然可能有标准算法可以为您找到向量的 max/min,但编写自己的总是更有成就感,并且使用给定的算法通常不会提高效率,因为算法通常是相同(对于确定 max/min 的小函数)。事实上,理论上,标准函数会 运行 稍微慢一些,因为这些函数是模板,必须在 运行 时间确定它正在处理的类型。
您必须至少查看每个元素,因此,正如 Anony-mouse 提到的,复杂度至少为 O(n^2).
#include <vector>
#include <limits>
#include <algorithm>
int main() {
std::vector<std::vector<double>> some_values;
double max = std::numeric_limits<double>::lowest();
for (const auto& v : some_values)
{
double current_max = *std::max_element(v.cbegin(), v.cend());
max = max < current_max ? current_max : max; // max = std::max(current_max, max);
}
}
使用 accumulate
函数你可以这样写:
#include <iostream>
#include <numeric>
#include <vector>
int main()
{
std::vector<std::vector<double>> m{ {5, 0, 8}, {3, 1, 9} };
double x = std::accumulate(m.begin(), m.end(), m[0][0],
[](double max, const std::vector<double> &v)
{
return std::max(max,
*std::max_element(v.begin(),
v.end()));
});
std::cout << x << '\n';
return 0;
}
但我更喜欢好的、旧的 for 循环。
可以扩展该示例以查找最小值和最大值:
std::accumulate(m.begin(), m.end(),
std::make_pair(m[0][0], m[0][0]),
[](std::pair<double, double> minmax, const std::vector<double> &v)
{
auto tmp(std::minmax_element(v.begin(), v.end()));
return std::make_pair(
std::min(minmax.first, *tmp.first),
std::max(minmax.second, *tmp.second));
});
(在真正的代码中你必须处理空向量的情况)
不幸的是,一个向量的向量没有连续存储在内存中,所以你没有一个包含所有值的块(这是向量的向量不是一个好的模型的原因之一矩阵)。
如果包含很多元素,您可以利用向量的向量。
由于每个子向量都是自治的,您可以使用 std::async 异步填充包含每个子向量最大值的期货向量。
这里有一个多线程的解决方案,returns一个迭代器(或抛出)最大为通用类型T
(假设operator<
定义为T
) .请注意,最重要的优化是对 'columns' 执行内部最大操作以利用 C++ 的列优先顺序。
#include <vector>
#include <algorithm>
template <typename T>
typename std::vector<T>::const_iterator max_element(const std::vector<std::vector<T>>& values)
{
if (values.empty()) throw std::runtime_error {"values cannot be empty"};
std::vector<std::pair<typename std::vector<T>::const_iterator, bool>> maxes(values.size());
threaded_transform(values.cbegin(), values.cend(), maxes.begin(),
[] (const auto& v) {
return std::make_pair(std::max_element(v.cbegin(), v.cend()), v.empty());
});
auto it = std::remove_if(maxes.begin(), maxes.end(), [] (auto p) { return p.second; });
if (it == maxes.begin()) throw std::runtime_error {"values cannot be empty"};
return std::max_element(maxes.begin(), it,
[] (auto lhs, auto rhs) {
return *lhs.first < *rhs.first;
})->first;
}
threaded_transform
还不是标准库的一部分,但这里有一个您可以使用的实现。
#include <vector>
#include <thread>
#include <algorithm>
#include <cstddef>
template <typename InputIterator, typename OutputIterator, typename UnaryOperation>
OutputIterator threaded_transform(InputIterator first, InputIterator last, OutputIterator result, UnaryOperation op, unsigned num_threads)
{
std::size_t num_values_per_threads = std::distance(first, last) / num_threads;
std::vector<std::thread> threads;
threads.reserve(num_threads);
for (int i = 1; i <= num_threads; ++i) {
if (i == num_threads) {
threads.push_back(std::thread(std::transform<InputIterator,
OutputIterator, UnaryOperation>,
first, last, result, op));
} else {
threads.push_back(std::thread(std::transform<InputIterator,
OutputIterator, UnaryOperation>,
first, first + num_values_per_threads,
result, op));
}
first += num_values_per_threads;
result += num_values_per_threads;
}
for (auto& thread : threads) thread.join();
return result;
}
template <typename InputIterator, typename OutputIterator, typename UnaryOperation>
OutputIterator threaded_transform(InputIterator first, InputIterator last, OutputIterator result, UnaryOperation op)
{
return threaded_transform<InputIterator, OutputIterator, UnaryOperation>(first, last, result, op, std::thread::hardware_concurrency());
}
如果您创建一个自定义迭代器来迭代 vector
的 double
或 vector
,一个简单的 std::minmax_element
就可以完成工作
迭代器类似于:
class MyIterator : public std::iterator<std::random_access_iterator_tag, double>
{
public:
MyIterator() : container(nullptr), i(0), j(0) {}
MyIterator(const std::vector<std::vector<double>>& container,
std::size_t i,
std::size_t j) : container(&container), i(i), j(j)
{
// Skip empty container
if (i < container.size() && container[i].empty())
{
j = 0;
++(*this);
}
}
MyIterator(const MyIterator& rhs) = default;
MyIterator& operator = (const MyIterator& rhs) = default;
MyIterator& operator ++() {
if (++j >= (*container)[i].size()) {
do {++i;} while (i < (*container).size() && (*container)[i].empty());
j = 0;
}
return *this;
}
MyIterator operator ++(int) { auto it = *this; ++(*this); return it; }
MyIterator& operator --() {
if (j-- == 0) {
do { --i; } while (i != 0 && (*container)[i].empty());
j = (*container)[i].size();
}
return *this;
}
MyIterator operator --(int) { auto it = *this; --(*this); return it; }
double operator *() const { return (*container)[i][j]; }
bool operator == (const MyIterator& rhs) const {
return container == rhs.container && i == rhs.i && j == rhs.j;
}
bool operator != (const MyIterator& rhs) const { return !(*this == rhs); }
private:
const std::vector<std::vector<double>>* container;
std::size_t i;
std::size_t j;
};
并且用法可能是
// Helper functions for begin/end
MyIterator MyIteratorBegin(const std::vector<std::vector<double>>& container)
{
return MyIterator(container, 0, 0);
}
MyIterator MyIteratorEnd(const std::vector<std::vector<double>>& container)
{
return MyIterator(container, container.size(), 0);
}
int main() {
std::vector<std::vector<double>> values = {{5,0,8}, {}, {3,1,9}};
auto b = MyIteratorBegin(values);
auto e = MyIteratorEnd(values);
auto p = std::minmax_element(b, e);
if (p.first != e) {
std::cout << "min is " << *p.first << " and max is " << *p.second << std::endl;
}
}
您可以使用 Eric Niebler 的 range-v3 库轻松地做到这一点(这显然还不是标准,但希望在不久的将来会成为标准):
vector<vector<double>> some_values{{5,0,8},{3,1,9}};
auto joined = some_values | ranges::view::join;
auto p = std::minmax_element(joined.begin(), joined.end());
p.first
是指向 min 元素的迭代器; p.second
到最大值
(range-v3 确实有 minmax_element 的实现,但不幸的是,它需要一个 ForwardRange,而 view::join 只给了我一个 InputRange,所以我不能使用它。)
如果您使用 boost::multi_array<double, 2>
而不是 std::vector<std::vector<double>>
,它会很简单:
auto minmax = std::minmax_element(values.data(), values.data() + values.num_elements());
普通for loop
方式:
T max_e = std::numeric_limits<T>::min();
for(const auto& v: vv) {
for(const auto& e: v) {
max_e = std::max(max_e, e);
}
}
假设我们有一个名为 some_values 的向量,如下所示
7 4 2 0
4 8 10 8
3 6 7 6
3 9 19* 14
定义一个一维向量如下图
vector<int> oneDimVector;
for(int i = 0; i < 4; i++){
for(int j = 0; j < 4; j++){
oneDimVector.push_back(some_values[i][j]);
}
}
然后在那个一维向量中找出一个maximum/minimum元素如下图
vector<int>::iterator maxElement = max_element(oneDimVector.begin(),oneDimVector.end());
vector<int>::iterator minElement = min_element(oneDimVector.begin(),oneDimVector.end());
现在你得到 max/min 个元素如下
cout << "Max element is " << *maxElement << endl;
cout << "Min element is " << *minElement << endl;
vector<vector<int>> vv = { vector<int>{10,12,43,58}, vector<int>{10,14,23,18}, vector<int>{28,47,12,90} };
vector<vector<int>> vv1 = { vector<int>{22,24,43,58}, vector<int>{56,17,23,18}, vector<int>{11,12,12,90} };
int matrix1_elem_sum=0;
int matrix2_elem_sum = 0;
for (size_t i = 0; i < vv.size(); i++)
{
matrix1_elem_sum += std::accumulate(vv[i].begin(), vv[i].end(), 0);
matrix2_elem_sum += std::accumulate(vv1[i].begin(), vv1[i].end(), 0);
}
cout << matrix1_elem_sum <<endl;
cout << matrix2_elem_sum << endl;
int summ = matrix1_elem_sum + matrix2_elem_sum;
cout << summ << endl;
或优化变体:
vector<vector<int>> vv = { vector<int>{10,12,43,58}, vector<int>{10,14,23,18}, vector<int>{28,47,12,90} };
vector<vector<int>> vv1 = { vector<int>{22,24,43,58}, vector<int>{56,17,23,18}, vector<int>{11,12,12,90} };
int summ=0;
int matrix2_elem_sum = 0;
for (size_t i = 0; i < vv.size(); i++)
{
summ += std::accumulate(vv[i].begin(), vv[i].end(), 0)+ std::accumulate(vv1[i].begin(), vv1[i].end(), 0);
}
cout << summ << endl;
}