can someone come up with a clean (and fast) solution to the following problem:
I have a sequence of entries that hold basically a key and a value, say a
struct Value {
int index = 0;
int cost = 0;
}
I now want to merge entries such that each key is only contained once but the values should be combined - i.e. each index should be only contained once in the sequence, and the cost for each duplicate index should be accumulated.
The basic solution I came up with sorts the sequence, and when equal entries are detected in the BinaryPredicate passed to std::sort, the cost will be summed into the lhs. Then the cost of rhs will be set to 0. Then follows a remove_if which removes the 0-cost values. See here for an example:
#include <cstdlib>
#include <vector>
#include <algorithm>
#include <iostream>
struct Value
{
int index = 0;
int cost = 0;
};
// generate a bunch of random values in a vector
// values will have indices in range [0..10]
std::vector<Value> generator()
{
std::vector<Value> v(20);
std::generate(v.begin(), v.end(), []() { return Value{std::rand() % 10, std::rand() % 10}; });
return v;
}
void print(const std::vector<Value> &values)
{
for (auto v : values)
std::cout << "{i=" << v.index << ", c=" << v.cost << "}, ";
std::cout << "\n";
}
//
void merge(std::vector<Value> &values)
{
// sort values and merge costs
std::sort(values.begin(), values.end(), [](auto &lhs , auto &rhs) {
if (lhs.index == rhs.index) {
lhs.cost += rhs.cost;
rhs.cost = 0;
}
return lhs.index < rhs.index;
});
// remove entries with empty cost
auto it = std::remove_if(values.begin(), values.end(), [](const auto &v) { return v.cost == 0; });
values.erase(it, values.end());
}
int main()
{
auto v = generator();
std::cout << "generated values: ";
print(v);
merge(v);
std::cout << "merged values: ";
print(v);
}
Live on Compiler Explorer
Thing is: While the example above produces the correct results, it is from what I can tell not conforming to the C++ standard. A BinaryPredicate "shall not apply any non-constant function through the dereferenced iterators" http://eel.is/c++draft/algorithms.requirements#8.sentence-4 . Compare is a BinaryPredicate. http://eel.is/c++draft/alg.sorting#general-2.sentence-1 )
Does this mean that my only option is to roll a custom inplace_unique_reduce or similar, or is there maybe an alternative elegant approach to this problem? I would prefer not having to write my own non-trivial algorithm for this.
Thanks
Assuming you are ok with additional allocations, I would use std::map (or the std::unordered_map):
auto merge_entries(std::vector<Value>& original_values) {
auto values = std::map<int, int>();
for (const auto [index, cost] : original_values) {
values[index] += cost;
}
const auto end_of_merged_values = std::transform(
values.cbegin(), values.cend(), original_values.begin(),
[](const auto entry) {
return Value{entry.first, entry.second};
}
);
original_values.erase(end_of_merged_values, original_values.end());
}
Apart from one for() loop (which can be substituted with std::for_each, although such change would introduce unnecessary boilterplate resulting in harder to read code, in my opinion), this solution uses only the STL.
We first merge all the entries using the map and then we overwrite some elements so that our original std::vector holds the merged entries. What's super convenient is the fact that std::transform returns an iterator pointing to the end of the inserted range. Why is it beneficial for us? Because apart from the unlikely scenario where no merging occurs, we have fewer elements compared to what was originally passed in. Using that iterator we can erase the rest of the vector (nonoverwritten elements) keeping it clean, STL-like style.
Assuming you are not ok with additional allocations, but you are ok with streghtening your iterator requirements (to bidirectional), I would use std::partial_sum and std::unique:
template <class BiDirIt, class BinaryPredicateCompare, class BinaryOpReduce>
auto inplace_unique_reduce(
BiDirIt first, BiDirIt last,
BinaryPredicateCompare cmp,
BinaryOpReduce reduce
) {
std::partial_sum(
std::make_reverse_iterator(last), std::make_reverse_iterator(first),
std::make_reverse_iterator(last),
[cmp, reduce](auto acc, const auto& elem) {
if (cmp(acc, elem)) {
return reduce(acc, elem);
} else {
acc = elem;
}
return acc;
}
);
return std::unique(first, last, cmp);
}
used like so:
auto values = std::vector<Value>{
{1, 1}, {2, 2}, {2, 7}, {0, 5},
{3, 3}, {1, 2}, {3, 10}
};
auto comparator = [](const auto& lhs, const auto& rhs) {
return lhs.index == rhs.index;
};
auto reducer = [](const auto& lhs, const auto& rhs) {
return Value{lhs.index, lhs.cost + rhs.cost};
};
auto to_remove = inplace_unique_reduce(
values.begin(), values.end(),
comparator,
reducer
);
values.erase(to_remove, values.end());
for (const auto[index, cost] : values) {
std::cout << index << ' ' << cost << '\n';
}
Just like your original answer, this will not merge nonadjacent elements, but to do that you either have to sort them by index or use something like map, from the first part of my answer.
The std::make_reverse_iterator calls are necessary becauase std::partial_sum accumulates the merged element in the most right-hand side one of given group of consecutive, equivalent elements. std::unique, on the other hand, preserves only the first element from such groups. Because of this, you want to merge the elements in the reverse order compared to the one you will be std::unique-ing.
You raised some concerns about situations where copying or moving is expensive - in such cases, you are either left with your custom solutions that take into considerations your unique constraints, or you ease your constraints. Here we move-assign merged entries, but that's it for the potential bottlenecks. If your move assignment operator is expensive, I fear that no standard solution will work for you and you have to roll your own, like in your answer.
This is the best I can come up with so far, but I still wonder if there is a solution that does not require a custom algorithm but instead combines existing ones:
template <class ForwardIt, class BinaryPredicateCompare, class BinaryOpReduce>
ForwardIt inplace_unique_reduce(ForwardIt first, ForwardIt last, BinaryPredicateCompare cmp, BinaryOpReduce reduce)
{
if (first == last)
return last;
ForwardIt result = first;
while (++first != last) {
if (cmp(*result, *first)) {
reduce(*result, *first);
} else if (++result != first) {
*result = std::move(*first);
}
}
return ++result;
}
Personally I would be much happier to modify the objects inside the Predicate of remove_if, since it is a much simpler and more straight-forward algorithm than sort. It does require keeping track of the output index, though:
void merge(std::vector<Value> &values)
{
std::sort(values.begin(), values.end(), [](auto &lhs , auto &rhs) { return lhs.index < rhs.index; });
std::size_t o_i = 0;
auto it = std::remove_if(values.begin(), values.end(), [&](const auto &v) {
if(o_i > 0 && values[o_i-1].index == v.index)
{
values[o_i-1].cost += v.cost;
return true;
}
o_i++;
return false; });
values.erase(it, values.end());
}
Here's another version using the range-v3 library (std::ranges doesn't have group_by yet).
#include <range/v3/all.hpp>
void merge(std::vector<Value> &values)
{
std::sort(values.begin(), values.end(), [](auto &lhs, auto &rhs) { return lhs.index < rhs.index; });
auto merged = values
| ranges::v3::view::group_by([](Value &lhs, Value &rhs) { return lhs.index == rhs.index; })
| ranges::v3::view::transform([](auto &&vs){
int index, cost = 0;
for(auto& v : vs) { index=v.index; cost+=v.cost;}
return Value{index, cost};
});
auto it = ranges::v3::copy(merged.begin(), merged.end(), values.begin()).second;
values.erase(it, values.end());
}
Also, don't forget plain old for-loop:
void merge(std::vector<Value> &values)
{
std::sort(values.begin(), values.end(), [](auto &lhs , auto &rhs) { return lhs.index < rhs.index; });
std::size_t j = 0;
for(std::size_t i = 0; i < values.size(); i++)
{
if(j>0 && values[j-1].index == values[i].index)
{
values[j-1].cost += values[i].cost;
continue;
}
if(j!=i) values[j]=std::move(values[i]);
j++;
}
values.erase(values.begin()+j, values.end());
}
Related
I have an algorithm which generates combinations from entries of a container and I want to find the combination which minimizes a cost function:
struct Vec { double x; double y; };
double cost( Vec a, Vec b ) {
double dx = a.x - b.x;
double dy = a.y - b.y;
return dx*dx + dy*dy;
}
pair<Vec,Vec> get_pair_with_minimum_cost ( vector<Vec> inp, double (*cost_fun)(Vec,Vec) )
{
pair<Vec,Vec> result;
double min_cost = FLT_MAX;
size_t sz = inp.size();
for(size_t i=0; i<sz; i++) {
for (size_t j=i; j<sz; j++) {
double cost = cost_fun(inp[i], inp[j]);
if (cost < min_cost) {
min_cost = cost;
result = make_pair(inp[i], inp[j]);
}
}
}
return result;
}
vector <Vec> inp = {....};
auto best_pair = get_pair_with_minimum_cost ( inp, cost );
Unfortunately, get_pair_with_minimum_cost() does 2 jobs:
generates the combinations
gets the minimum element
I could break them in two functions, like:
the generator:
template <class Func>
void generate_all_combinations_of( vector<Vec> inp, Func fun )
{
size_t sz = inp.size();
for(size_t i=0; i<sz; i++) {
for (size_t j=i; j<sz; j++) {
fun(make_pair(inp[i], inp[j]));
}
}
}
and then use std::min_element on the output of the generator, i.e.
vector<Vec> inp = {....};
vector<pair<Vec,Vec>> all_combinations;
generate_all_combinations_of(inp, [&](vector<pair<Vec,Vec>> o){all_combinations.push_back(o); } );
auto best_pair = *min_element(all_combinations.begin(), all_combinations.end(), cost);
but I do not want the pay the cost of creating and extra container with temporary data (all_combinations).
Questions:
Can I rewrite the generate_all_combinations_of() such that it uses yield or the new std::ranges in such a way that I can combine it with STL algorithms such as find_if, any_of, min_element or even adjacent_pair ?
The great thing about this 'generator' function is that it is easy to read, so I would like to keep it as readable as possible.
NB: some of these algorithms need to break the loop.
What is the official name of combining entries this way?
It this the combinations used in 'bubble-sort'.
Here's how I would write the function in c++20, using range views and algorithms so that there isn't a separate container that stores the intermediate results:
double get_minimum_cost(auto const & inp)
{
namespace rs = std::ranges;
namespace rv = std::ranges::views;
// for each i compute the minimum cost for all j's
auto min_cost_from_i = [&](auto i)
{
auto costs_from_i = rv::iota(i + 1, inp.size())
| rv::transform([&](auto j)
{
return cost(inp[i], inp[j]);
});
return *rs::min_element(costs_from_i);
};
// compute min costs for all i's
auto all_costs = rv::iota(0u, inp.size())
| rv::transform(min_cost_from_i);
return *rs::min_element(all_costs);
}
Here's a demo.
Note that the solution doesn't compare the cost between same elements, since the cost function example you showed would have a trivial result of 0. For a cost function that doesn't return 0, you can adapt the solution to generate a range from i instead of i + 1. Also, if the cost function is not symmetric, make that range start from 0 instead of i.
Also, this function has UB if you call it with an empty range, so you should check for that as well.
There is http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p2168r0.pdf who's development I would follow
If you are using MSVC, and can use their experimental/generator (not sure if others support it yet), you can use
std::experimental::generator<std::size_t> Generate(std::size_t const end){
for(std::size_t i = 0; i < end; ++i)
co_yield i;
}
int main(){
auto vals = Generate(22);
auto const result = *std::min_element(std::begin(vals),std::end(vals));
std::cout <<'\n' << " " << result;
}
Here you would need to modify the Generate function to Yield a pair/or to yield cost
(My recommendation would be to Keep things simple and yield the cost)
Then use vals to find min_cost
Ranges
Based on what I can find about the Ranges Proposal, it works on the basis of std::begin and std::end both of which experimental::generator provides
So it should probably work
Here's how I would write the function in c++17, using algorithms' min_element function, with no need for a separate container that stores the intermediate results. I know you were looking for a c++20 solution, but this code does work fine under c++20, and perhaps it gives you some ideas about adapting functions to ranges when the range isn't just one of the ranges supplied by c++20's ranges library.
// TwoContainerRanger is an iterable container where the iterator consists
// of two indices that match the given filter, and whose iterators, when
// dereferenced, return the result of calling func with the
// elements of the two containers, at those two indices.
// filter can be nullptr.
template <typename Container1, typename Container2, typename Func>
struct TwoContainerRanger {
Container1 &c1;
Container2 &c2;
const Func &fun;
bool (*restriction)(size_t i1, size_t i2);
TwoContainerRanger(Container1 &container1, Container2 &container2,
bool (*filter)(size_t i1, size_t i2), const Func &func)
: c1(container1), c2(container2), fun(func), restriction(filter) {}
struct Iterator {
const TwoContainerRanger *gen;
size_t index1, index2;
auto &operator++() {
do {
if (++index1 == gen->c1.size()) {
if (++index2 == gen->c2.size()) {
// we leave both indices pointing to the end
// to indicate that we have reached the end.
return *this;
} else {
index1 = 0u;
}
}
} while (gen->restriction && gen->restriction(index1, index2) == false);
return *this;
}
bool operator==(const Iterator &other) const = default;
bool operator!=(const Iterator &other) const = default;
auto operator*() const {
return gen->fun(gen->c1[index1], gen->c2[index2]);
}
};
Iterator begin() {
Iterator b{this, size_t(0) - 1, 0u};
return ++b; // automatically applies the restriction
}
Iterator end() { return Iterator{this, c1.size(), c2.size()}; }
};
Calling it looks like this:
int main() {
std::array<Vec, 5> ar = {Vec{0, 0}, Vec{1, 1}, Vec{3, 3}, Vec{7, 7},
Vec{3.1, 3.1}};
TwoContainerRanger tcr{ar, ar, Triangle, cost};
auto result = std::min_element(tcr.begin(), tcr.end());
std::cout << "Min was at (" << result.index1 << "," << result.index2
<< "); cost was " << *result << '\n';
}
Here's a demo.
What is the best way to apply some operation (find a min for example) on std::adjacent_difference results without creating extra container?
Edit: manual loop is obvious way but not STL enough.
One way would be to implement output_iterator but looks a bit heavy:
template<typename T>
class find_min_oit {
T min = std::numeric_limits<T>::max();
public:
using iterator_category = std::output_iterator_tag;
find_min_oit& operator++() {
return *this;
}
find_min_oit& operator++(int) {
return *this;
}
find_min_oit& operator*() {
return *this;
}
find_min_oit& operator=(T const& value){
if (value < min){
min = value;
}
return *this;
}
T result() const {
return min;
}
};
void find_min(){
vector<int> arr{1,2,3,6,15};
auto res = std::adjacent_difference(arr.begin(), arr.end(), find_min_oit<int>());
std::cout << res.result() << std::endl;
}
You can use boost::function_output_iterator to define the iterator.
void find_min(){
vector<int> arr{1,2,3,6,15};
int result = std::numeric_limits<int>::max();
auto oit = boost::make_function_output_iterator([&result](int value) { result = std::min(value, result); });
std::adjacent_difference(arr.begin(), arr.end(), oit);
std::cout << result << std::endl;
}
You can use std::adjacent_find.
int find_min(){
vector<int> arr{1,2,3,6,15};
int min = std::numeric_limits<int>::max();
std::adjacent_find(arr.begin(), arr.end(), [&](auto a, auto b){
if ((a - b) < min)
min = a-b;
return false;
});
return min;
}
Just go for a manual loop, unfortunately, the standard algorithms are not made to be combined without a cost... You can look at the range v3 library that allows you to combine algorithms in a less-costly way.
template <class InputIt>
auto find_adjacent_min_v3(InputIt first, InputIt last) {
auto min = std::numeric_limits<typename InputIt::value_type>::max();
for (auto it = first++; first < last; ++first, ++it) {
if (*it - *first < min) {
min = *it - *first;
}
}
return min;
}
For the sake of the exercise, here is a version using the standard std::accumulate with a custom "accumulator":
template <class InputIt>
auto find_adjacent_min(InputIt first, InputIt last) {
auto max = std::numeric_limits<typename InputIt::value_type>::max();
return std::accumulate(
std::next(first), last, std::make_pair(*first, max),
[](auto p, auto x) {
auto diff = (x - p.first) < p.second ?
x - p.first : p.second;
return std::make_pair(x, diff);
}).second;
}
You're 80% of the way to answering your own question: just add another template parameter say typename On_Assignment, rename find_min_oit to something more generic and derived from On_Assignment so you can inject data members, then allow operator= to be supplied by On_Assignment. You then just need a few lines to write struct with an operator= to produce variations in handling of the values adjacent_difference will supply. Alternatively, pass the custom behaviour to the constructor and save it in a std::function for use from operator=: you can then pass in a lambda.
If it is acceptable that the input data is changed, you might try something like this:
std::min_element(x.begin() + 1, std::adjacent_difference(x.begin(), x.end(), x.begin());
CPP reference states explicitly that input and output begins are allowed to be equal.
I was wondering if there was a standard function that returns the minimum/maximum of the return values for a given range of elements. Something like this:
std::vector<int> list = { -2, -1, 6, 8, 10 };
auto it =
std::find_min_return_value(list.begin(), list.end(), std::abs, std::less<int>);
// it should be the iterator for -1
If there is no such, what is the best approach for a problem like this?
My list is long, I really don't want to copy it, and also don't want to call the function whose minimum return value I look for more than once per element. Thanks!
UPDATE:
Based on ForEveR's suggestion to use std::min_element, I made the following benchmarking tests:
std::vector<double> list = { -2, -1, 6, 8, 10 };
auto semi_expensive_test_function = [] (const double& a) { return asin(sin(a)); };
for(int i = 0; i < 10000000; ++i)
{
auto it = std::min_element(list.begin(), list.end(),
[&] (const double& a, const double& b) mutable
{
return(semi_expensive_test_function(a) < semi_expensive_test_function(b));
});
}
This worked just fine:
./a.out 11.52s user 0.00s system 99% cpu 11.521 total
After modifying the code to use a stateful lambda instead:
for(int i = 0; i < 10000000; ++i)
{
auto it = std::min_element(list.begin() + 1, list.end(),
[&, current_min_value = semi_expensive_test_function(*(list.begin()))] (const double& a, const double& b) mutable
{
double current_value = semi_expensive_test_function(b);
if(current_value < current_min_value)
{
current_min_value = std::move(current_value);
return true;
}
return false;
});
}
This resulted:
./a.out 6.34s user 0.00s system 99% cpu 6.337 total
Using stateful lambdas seems to be the way to go. The question is: is there a more code-compact way to achieve this?
With range-v3, it would be something like:
ranges::min(list, std::less<>{}, [](auto e) { return std::abs(e); });
Well, assuming Boost is like the standard library nowadays, you might use this:
#include <boost/range/adaptor/transformed.hpp>
#include <algorithm>
int main()
{
std::vector<int> list = { -2, -1, 6, 8, 10 };
auto abs_list = list | boost::adaptors::transformed(+[](int v) { return std::abs(v); });
// ^ - read http://stackoverflow.com/questions/11872558/using-boost-adaptors-with-c11-lambdas
auto it = std::min_element(abs_list.begin(), abs_list.end(), std::less<int>{});
std::cout << *it;
}
If it'll get reused, and to give you another option, you could write your own generic algorithm following std conventions.
template <typename T, typename ForwardIt, typename UnaryFunction, typename Comparator>
ForwardIt find_min_return_value(ForwardIt first, ForwardIt last, UnaryFunction op, Comparator compare)
{
if (first == last)
return last;
ForwardIt smallestItr = first;
T smallestValue = op(*first);
for (auto itr = first + 1; itr != last; ++itr)
{
T current = op(*itr);
if (compare(current, smallestValue))
{
smallestValue = current;
smallestItr = itr;
}
}
return smallestItr;
}
Usage is then quite code-compact, and the operation will only be performed once per element:
int main()
{
std::vector<int> list = { -2, -1, 6, 8, 10 };
auto it1 = find_min_return_value<int>(list.begin(), list.end(), [](int i){ return std::abs(i); }, std::less<int>());
std::vector<std::string> strings = { "Lorem", "ipsum", "dolor", "sit", "amet", "consectetur", "adipiscing", "elit" };
auto it3 = find_min_return_value<size_t>(strings.begin(), strings.end(), [](std::string s){ return s.length(); }, std::less<size_t>());
std::cout << *it1 << "\n"; // outputs -1
std::cout << *it3 << "\n"; // outputs sit
}
If you only suspect it may get reused one day it probably won't, and it's then overly complicated, and should then be just a simple function.
Can I get the positions of the max N elements (the equal max elements) using predefined function in STL?
A solution I thought of is:
vector<int> maxN(vector<int> original){
vector<int> result;
auto pt = max_element(original.begin(),original.end());
int max = *pt;
while(*pt == max){
result.push_back(distance(original.begin(),pt));
*pt = 0;//assumed that all the elements in original are greater than 0
pt = max_element(original.begin(),original.end());
}
return result;
}
There must be a more elegant way to do this.
It depends on your exact requirements:
std::max_element gives you the maximum element. std::copy_if can be used to copy all elements equal to the maximum (and limit the maximum number if required, e.g. using a lambda).
std::nth_element partially sorts a range (e.g. your vector) such the first n entries are equal or less to anything that follows. The first n elements are not sorted themselves. And it is not a stable partition.
std::partial_sort gives you the same, but the first n elements are sorted. Again, not a stable partition/sort.
Combine std::nth_element + std::stable_partition + std::stable_sort if you need a stable selection of the first n elements and you want them stably sorted.
Once you have found the max element, make another linear pass over the vector to find all the matching elements. Setting to 0 is not needed when doing it this way. The original vector is being passed by value, so setting to 0 was not being seen by the caller. This makes for a very clear implementation:
vector<int> maxN(vector<int> original){
vector<int> result;
if (original.empty()) return result;
const int max = *(max_element(original.begin(), original.end()));
for (int i = 0; i < original.size(); ++i) {
if (original[i] == max) result.push_back(i);
}
return result;
}
It is more importatnt to implement clear and maintainable code than to attempt to extract maximal reuse from the C++ library.
If your goal is to not use an explicit loop over the passed in original vector, but use some standard C++ template algorithm, I recommend creating a helper iterator to help you recover the index.
struct indexer {
int i_;
indexer (int i = 0) : i_(i) {}
indexer & operator ++ () { ++i_; return *this; }
indexer operator ++ (int) { ++i_; return i_ - 1; }
int operator * () const { return i_; }
bool operator != (indexer rhs) const { return i_ != rhs.i_; }
//... whatever else is required for copy_if
};
Then, you can invoke copy_if with a simple lambda and a back insert iterator:
copy_if(indexer(), indexer(original.size()), back_inserter(result),
[&](int i) -> bool { return original[i] == max; });
However, this is more obscure than the straightforward loop presented above.
As variant (can check on a cpp.sh)
#include <iostream>
#include <vector>
int main ()
{
std::vector<int>elems = {10, 20, 10, 30, 5, 30, 8, 30, 18, 12};
for(size_t i=0; i<elems.size()-1; i++)
{
if(elems[i+1] > elems[i]) { elems.erase(elems.begin()+i); i=-1;}
else if(elems[i+1] < elems[i]) { elems.erase(elems.begin()+i+1); i=-1;}
}
return 0;
}
You could decorate your original with indices, take the Nth element-approach, and strip off the indices again (test on cpp.sh):
template<typename T, typename less = std::greater<T>>
std::vector<int> max_indices(
int N,
const std::vector<T> &original,
less predicate = less())
{
auto decorated = with_indices(original);
// the gist of the problem
const auto nth = std::next(begin(decorated), N);
std::nth_element(begin(decorated), nth, end(decorated),
with_indices(predicate));
std::sort(begin(decorated), nth,
with_indices(predicate));
decorated.erase(nth, end(decorated));
return indices(decorated);
}
int main()
{
std::vector<int> values{ {1, 2, 3 , 4, 5, 6, 7, 8, 9, 10} };
auto m = max_indices(4, values);
assert(4u == m.size());
assert(9 == m[0]);
assert(8 == m[1]);
assert(7 == m[2]);
assert(6 == m[3]);
return 0;
}
Where these functions do the decorating/undecorating:
template<typename T>
std::vector<std::pair<T, int>> with_indices(const std::vector<T> &original)
{
std::vector< std::pair<T, int> > decorated;
std::transform(begin(original), end(original), std::back_inserter(decorated),
[index = 0](T t) mutable {
return std::make_pair(t, index++);
});
return decorated;
}
template<typename T>
std::vector<int> indices(const std::vector<std::pair<T, int>> &original)
{
std::vector<int> undecorated;
std::transform(begin(original), end(original), std::back_inserter(undecorated),
[](auto p) mutable {
return p.second;
});
return undecorated;
}
template<typename Function>
auto with_indices(Function f)
{
return [&](auto... args) {
return f(args.first...);
};
}
What is the most efficient and standard (C++11/14) way to find the max/min item of vector of vectors?
std::vector<std::vector<double>> some_values{{5,0,8},{3,1,9}};
the wanted max element is 9
the wanted min element is 0
Here's a multi-threaded solution that returns an iterator (or throws) to the maximum for general type T (assuming operator< is defined for T). Note the most important optimisation is to perform the inner max operations on the 'columns' to exploit C++'s column-major ordering.
#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 is not part of the standard library (yet), but here's an implementation you could use.
#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());
}
If you used a boost::multi_array<double, 2> instead of a std::vector<std::vector<double>> it would be as simple as:
auto minmax = std::minmax_element(values.data(), values.data() + values.num_elements());
Live demo.
The plain for loop way:
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);
}
}
You must at least look at every element, so, as Anony-mouse mentioned, complexity will be at least 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);
}
}
You can do it pretty easily with Eric Niebler's range-v3 library (which obviously isn't standard yet, but hopefully will be in the not-too-distant future):
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 is an iterator to the min element; p.second to the max.
(range-v3 does have an implementation of minmax_element, but unfortunately, it requires a ForwardRange and view::join only gives me an InputRange, so I can't use it.)
Any efficient way to calculate the maximum element in a 2-D array(or vector in your case) involves a complexity of O(n^2) irrespective of what you do, as the calculation involves a comparison between n*n elements.Best way in terms of ease of use is to use std::max_element on the vector of vectors.I will not delve into details.Here is the reference.
If you create a custom iterator to iterate over all double of your vector of vector, a simple std::minmax_element do the job
iterator is something like:
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;
};
And usage may be
// 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;
}
}
Live example
Using the accumulate function you could write:
#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;
}
but I'd prefer the good, old for-loop.
The example can be extended to find both the min and max values:
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));
});
(in real code you have to handle the empty-vector case)
Unfortunately a vector of vector isn't stored contiguously in memory, so you haven't a single block containing all the values (this is one of the reasons why a vector of vector isn't a good model for a matrix).
You can take advantage of a vector of vector if it contains a lot of elements.
Since each sub-vector is autonomous, you could use std::async to fill asynchronously a vector of futures containing the max value of each sub-vector.
The simplest method would be to first have a function to determine the max/min elements of one vector, say a function called:
double getMaxInVector(const vector<double>& someVec){}
Passing by reference (for reading purposes only) in this case will be a lot more time and space efficient (you don't want your function copying an entire vector). Thus in your function to determine max/min element of a vector of vectors, you would have a nested loop, such as:
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
The problem with the above solution is its inefficiency. From my knowledge, this algorithm will generally run on O(n^2log(n)) efficiency, which is quite unimpressive. But of course, it is still a solution. Although there might be standard algorithms that can find the max/min of a vector for you, it's always more accomplishing to write your own, and using the given will usually do nothing in terms of improving efficiency because the algorithm will generally be the same (for small functions that determine max/min). In fact, theoretically, standard functions would run marginally slower since those functions are templates which have to determine the type it is dealing with at run-time.
Lets say we have a vector named some_values, as shown below
7 4 2 0
4 8 10 8
3 6 7 6
3 9 19* 14
define a one-dimensional vector as shown below
vector<int> oneDimVector;
for(int i = 0; i < 4; i++){
for(int j = 0; j < 4; j++){
oneDimVector.push_back(some_values[i][j]);
}
}
Then find out a maximum/minimum element in that one-dimensional vector as shown below
vector<int>::iterator maxElement = max_element(oneDimVector.begin(),oneDimVector.end());
vector<int>::iterator minElement = min_element(oneDimVector.begin(),oneDimVector.end());
Now you get the max/min elements as below
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;
or optimazed variant:
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;
}