Get maximum element of std::vector with strides - c++

I have a std::vector<float> with the following layout of data
x1 | y1 | z1 | x2 | y2 | z2 | .... | xn | yn | zn
I'm trying to figure out an STL-ish way to get the maximum x element as well as y or z
The obvious
double xyzmax = *std::max_element(myvector.begin(),myvector.end() );
picks the absolute maximum and does not allow me to specify the stride.
Is there some trick with no for loops?

You could use the Boost.Iterator library and the boost::iterator_facade to create a strided iterator that can be initalized with a std::vector<float>::iterator and for which ++it does it += 3; on the underlying iterator.
Given such an iterator of type StrideIt, you could write
maxX = *std::max_element(StrideIt(v.begin() + 0), StrideIt(v.end() - 2));
maxY = *std::max_element(StrideIt(v.begin() + 1), StrideIt(v.end() - 1));
maxZ = *std::max_element(StrideIt(v.begin() + 2), StrideIt(v.end() - 0));
This is preferably to redefining the algorithms because there are many more algorithms than iterator types.
If you want maximum flexibility, you could make StrideIt a class template taking the type (float in your case) and a runtime construtor argument defining the stride (3 in your case).

Here is a reference implementation of std::max_element.
template<class ForwardIt>
ForwardIt max_element(ForwardIt first, ForwardIt last)
{
if (first == last) {
return last;
}
ForwardIt largest = first;
++first;
for (; first != last; ++first) {
if (*largest < *first) {
largest = first;
}
}
return largest;
}
You can create your own algorithm by modifying this in the following way:
template<class ForwardIt>
ForwardIt max_element_nth(ForwardIt first, ForwardIt last, int n)
{
if (first == last) {
return last;
}
ForwardIt largest = first;
first += n;
for (; first < last; first += n) {
if (*largest < *first) {
largest = first;
}
}
return largest;
}
Of course it has the limitation of working only with random access iterators, but it certainly works for vector.
double xmax = *max_element_nth(myvector.begin(),myvector.end(), 3);
double ymax = *max_element_nth(myvector.begin()+1,myvector.end(), 3);
double zmax = *max_element_nth(myvector.begin()+2,myvector.end(), 3);
But I'd rather do it by storing the (x, y, z) values in a structure, and take a vector of that. Then, you can use the standard max_element with a custom comparator.

Related

Computing truncated mean between two forward indicators

I have already computed the truncated mean of a vector via the function truncated_mean(std::vector& v, double trimming fraction). This function takes as inputs the vector v and the fraction that we want to remove to calculate the mean (e.g. 10% so we remove the highest and lowest 10% values and then we compute the mean), I created it using the Standard Library.
For example, v = [0,1,2....,9], then truncated_mean(v, 0.10) = 4.5.
Now, I want to reuse the same function but instead of having v as input, I want to have 2 forward iterators, v.begin() and v.end(). I am provided with the template of typename forward that I should use to check if its value_type (accessed via std::iterator_traits) meets a certain criteria. My understanding of the problem is that first I need to check if the inputs belong to a vector and from there I should access the vector in itself to compute the truncated mean.
How can I adapt my function to take as input the beginning and end of the vector rather than the vector itself?
Assuming sequence passed in is sorted you could simply use std::distance to figure out the length and skip the appropriate number of elements at the start and the end:
Edit: Extended code to use std::accumulate for random access iterators; Use concepts instead of distinguishing iterator types vis additional parameter, if you're allowed to use C++20 features.
template<typename RandomAccessIterator>
double truncated_mean_impl(RandomAccessIterator begin, RandomAccessIterator end, double trimming_fraction, std::random_access_iterator_tag)
{
if (trimming_fraction < 0)
{
throw std::range_error("trimming_fraction must not be negative");
}
if(trimming_fraction >= 0.5)
{
return std::numeric_limits<double>::quiet_NaN(); // no elements left after trimming
}
auto const count = std::distance(begin, end);
auto const skippedElementCountFront = static_cast<decltype(count)>(count * trimming_fraction);
auto const summandCount = count - 2 * skippedElementCountFront;
return std::accumulate<RandomAccessIterator, double>(begin + skippedElementCountFront, end - skippedElementCountFront, 0) / summandCount;
}
template<typename ForwardIterator>
double truncated_mean_impl(ForwardIterator begin, ForwardIterator end, double trimming_fraction, std::forward_iterator_tag)
{
if (trimming_fraction < 0)
{
throw std::range_error("trimming_fraction must not be negative");
}
if(trimming_fraction >= 0.5)
{
return std::numeric_limits<double>::quiet_NaN(); // no elements left after trimming
}
auto const count = std::distance(begin, end);
auto const skippedElementCountFront = static_cast<decltype(count)>(count * trimming_fraction);
// skip elements in the front
for (auto i = skippedElementCountFront; i != 0; --i, ++begin) {}
auto const summandCount = count - 2 * skippedElementCountFront;
double sum = 0;
for (auto i = summandCount; i != 0; --i, ++begin)
{
sum += *begin;
}
return sum / summandCount;
}
template<typename ForwardIterator>
double truncated_mean(ForwardIterator begin, ForwardIterator end, double trimming_fraction)
{
return truncated_mean_impl<ForwardIterator>(begin, end, trimming_fraction, typename std::iterator_traits<ForwardIterator>::iterator_category());
}
int main()
{
std::vector<int> const values { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
std::cout << truncated_mean(values.cbegin(), values.cend(), 0.1) << '\n';
}
If the input sequence is not sorted and you cannot or don't want to sort the input copying the elements to a new vector and applying your original algorithm to this vector would probably be best.

Averaging and decreasing the array (vector) C++

I've got an array (actually std::vector) size ~ 7k elements.
If you draw this data, there will be a diagram of the combustion of the fuel. But I want to minimize this vector from 7k elements to 721 (every 0.5 degree) elements or ~ 1200 (every 0.3 degree). Of course I want save diagram the same. How can I do it?
Now I am getting every 9 element from big vector to new and cutting other evenly from front and back of vector to get 721 size.
QVector <double> newVMTVector;
for(QVector <double>::iterator itv = oldVmtDataVector.begin(); itv < oldVmtDataVector.end() - 9; itv+=9){
newVMTVector.push_back(*itv);
}
auto useless = newVMTVector.size() - 721;
if(useless%2 == 0){
newVMTVector.erase(newVMTVector.begin(), newVMTVector.begin() + useless/2);
newVMTVector.erase(newVMTVector.end() - useless/2, newVMTVector.end());
}
else{
newVMTVector.erase(newVMTVector.begin(), newVMTVector.begin() + useless/2+1);
newVMTVector.erase(newVMTVector.end() - useless/2, newVMTVector.end());
}
newVMTVector.squeeze();
oldVmtDataVector.clear();
oldVmtDataVector = newVMTVector;
I can swear there is an algorithm that averages and reduces the array.
The way I understand it you want to pick the elements [0, k, 2k, 3k ... ] where n is 10 or n is 6.
Here's a simple take:
template <typename It>
It strided_inplace_reduce(It it, It const last, size_t stride) {
It out = it;
if (stride < 1) return last;
while (it < last)
{
*out++ = *it;
std::advance(it, stride);
}
return out;
}
Generalizing a bit for non-random-access iterators:
Live On Coliru
#include <iterator>
namespace detail {
// version for random access iterators
template <typename It>
It strided_inplace_reduce(It it, It const last, size_t stride, std::random_access_iterator_tag) {
It out = it;
if (stride < 1) return last;
while (it < last)
{
*out++ = *it;
std::advance(it, stride);
}
return out;
}
// other iterator categories
template <typename It>
It strided_inplace_reduce(It it, It const last, size_t stride, ...) {
It out = it;
if (stride < 1) return last;
while (it != last) {
*out++ = *it;
for (size_t n = stride; n && it != last; --n)
{
it = std::next(it);
}
}
return out;
}
}
template <typename Range>
auto strided_inplace_reduce(Range& range, size_t stride) {
using std::begin;
using std::end;
using It = decltype(begin(range));
It it = begin(range), last = end(range);
return detail::strided_inplace_reduce(it, last, stride, typename std::iterator_traits<It>::iterator_category{});
}
#include <vector>
#include <list>
#include <iostream>
int main() {
{
std::vector<int> v { 1,2,3,4,5,6,7,8,9 };
v.erase(strided_inplace_reduce(v, 2), v.end());
std::copy(v.begin(), v.end(), std::ostream_iterator<int>(std::cout << "\nv: ", " "));
}
{
std::list<int> l { 1,2,3,4,5,6,7,8,9 };
l.erase(strided_inplace_reduce(l, 4), l.end());
std::copy(l.begin(), l.end(), std::ostream_iterator<int>(std::cout << "\nl: ", " "));
}
}
Prints
v: 1 3 5 7 9
l: 1 5 9
What you need is an interpolation. There are many libraries providing many types of interpolation. This one is very lightweight and easy to setup and run:
http://kluge.in-chemnitz.de/opensource/spline/
All you need to do is create the second vector that contains the X values, pass both vectors to generate spline, and generate interpolated results every 0.5 degrees or whatever:
std::vector<double> Y; // Y is your current vector of fuel combustion values with ~7k elements
std::vector<double> X;
X.reserve(Y.size());
double step_x = 360 / (double)Y.size();
for (int i = 0; i < X.size(); ++i)
X[i] = i*step_x;
tk::spline s;
s.set_points(X, Y);
double interpolation_step = 0.5;
std::vector<double> interpolated_results;
interpolated_results.reserve(std::ceil(360/interpolation_step) + 1);
for (double i = 0.0, int j = 0; i <= 360; i += interpolation_step, ++j) // <= in order to obtain range <0;360>
interpolated_results[j] = s(i);
if (fmod(360, interpolation_step) != 0.0) // for steps that don't divide 360 evenly, e.g. 0.7 deg, we need to close the range
interpolated_results.back() = s(360);
// now interpolated_results contain values every 0.5 degrees
This should give you and idea how to use this kind of libraries. If you need some other interpolation type, just find the one that suits your needs. The usage should be similar.

Best way to to average duplicate properties in C++ vector

I have a std::vector<PLY> that holds a number of structs:
struct PLY {
int x;
int y;
int greyscale;
}
Some of the PLY's could be duplicates in terms of their position x and y but not necessarily in terms of their greyscale value. What is the best way to find those (position-) duplicates and replace them with a single PLY instace which has a greyscale value that represents the average greyscale of all duplicates?
E.g: PLY a{1,1,188} is a duplicate of PLY b{1,1,255}. Same (x,y) position possibly different greyscale.
Based on your description of Ply you need these operators:
auto operator==(const Ply& a, const Ply& b)
{
return a.x == b.x && a.y == b.y;
}
auto operator<(const Ply& a, const Ply& b)
{
// whenever you can be lazy!
return std::make_pair(a.x, a.y) < std::make_pair(b.x, b.y);
}
Very important: if the definition "Two Ply are identical if their x and y are identical" is not general valid, then defining comparator operators that ignore greyscale is a bad ideea. In that case you should define separate function objects or non-operator functions and pass them around to function.
There is a nice rule of thumb that a function should not have more than a loop. So instead of a nested 2 for loops, we define this helper function which computes the average of consecutive duplicates and also returns the end of the consecutive duplicates range:
// prereq: [begin, end) has at least one element
// i.e. begin != end
template <class It>
auto compute_average_duplicates(It begin, It end) -> std::pair<int, It>
// (sadly not C++17) concepts:
//requires requires(It i) { {*i} -> Ply; }
{
auto it = begin + 1;
int sum = begin->greyscale;
for (; it != end && *begin == *it; ++it) {
sum += it->greyscale;
}
// you might need rounding instead of truncation:
return std::make_pair(sum / std::distance(begin, it), it);
}
With this we can have our algorithm:
auto foo()
{
std::vector<Ply> v = {{1, 5, 10}, {2, 4, 6}, {1, 5, 2}};
std::sort(std::begin(v), std::end(v));
for (auto i = std::begin(v); i != std::end(v); ++i) {
decltype(i) j;
int average;
std::tie(average, j) = compute_average_duplicates(i, std::end(v));
// C++17 (coming soon in a compiler near you):
// auto [average, j] = compute_average_duplicates(i, std::end(v));
if (i + 1 == j)
continue;
i->greyscale = average;
v.erase(i + 1, j);
// std::vector::erase Invalidates iterators and references
// at or after the point of the erase
// which means i remains valid, and `++i` (from the for) is correct
}
}
You can apply lexicographical sorting first. During sorting you should take care of overflowing greyscale. With current approach you will have some roundoff error, but it will be small as i first sum and only then average.
In the second part you need to remove duplicates from the array. I used additional array of indices to copy every element not more than once. If you have some forbidden value for x, y or greyscale you can use it and thus get along without additional array.
struct PLY {
int x;
int y;
int greyscale;
};
int main()
{
struct comp
{
bool operator()(const PLY &a, const PLY &b) { return a.x != b.x ? a.x < b.x : a.y < b.y; }
};
vector<PLY> v{ {1,1,1}, {1,2,2}, {1,1,2}, {1,3,5}, {1,2,7} };
sort(begin(v), end(v), comp());
vector<bool> ind(v.size(), true);
int s = 0;
for (int i = 1; i < v.size(); ++i)
{
if (v[i].x == v[i - 1].x &&v[i].y == v[i - 1].y)
{
v[s].greyscale += v[i].greyscale;
ind[i] = false;
}
else
{
int d = i - s;
if (d != 1)
{
v[s].greyscale /= d;
}
s = i;
}
}
s = 0;
for (int i = 0; i < v.size(); ++i)
{
if (ind[i])
{
if (s != i)
{
v[s] = v[i];
}
++s;
}
}
v.resize(s);
}
So you need to check, is PLY a1 { 1,1,1 }; duplicates PLY a2 {2,2,1};
So simple method is to override operator == to check a1.x == a2.x and a1.y == a2.y. After you can write own function removeDuplicates(std::vector<PLU>& mPLY); which will use iterators of this vector, compare and remove. But better to use std::list if you want to remove from middle of array too frequently.

Reverse map a functional relation(c++)

I am using a simple function (y(x)), and I want to generate an x value from a certain y value. While typically reverse mapping does not give a single x value, I am using the maximum from my y values. This means that there will be a unique x value for the y value I input(the maximum). I don't understand how to code this in c++
If you don't need interpolation, only exact reverse lookup, then it's relatively straighforward:
std::map<YType, XType> lookup;
// (code to read the file goes here)
// for each x {
YType y = f(x);
if ((lookup.count(y) == 0) || (lookup[y] < x)) {
lookup[y] = x;
}
// }
Then your reverse lookup is just lookup[y], which will return 0 (or a default-constructed value where applicable) if y in fact was missing from the data.
Be aware that my code is a bit inefficient, it looks up y several times in the map, up to 3. You can optimize using iterators, but I'm concerned that obscures what's going on if you're not already familiar with them:
typedef std::map<YType, XType> maptype;
typedef std::pair<maptype::iterator, bool> resulttype;
resulttype result = lookup.insert(std::make_pair(y, x));
if (!result.second) {
// key already existed, so value was not inserted. Check for max.
maptype::iterator pos = result.first;
if ((*pos).second < x) {
(*pos).second = x;
}
}
If I understand correctly, you are given a finite range of values x, say x[0], x[1], ..., x[N], and a function f, and you want to find the index k for which f(x[k]) is the largest possible. In that case, a simple search will do:
size_t k = 0;
T m = f(x[k]);
T tmp;
for (size_t i = 1; i <= N; ++i)
{
if ((tmp = f(x[i])) > m)
{
k = i;
m = tmp;
}
}
// Maximum is (x[k], m)
Here T is the type such that f is T f(T);

Converting sets of integers into ranges

What's the most idiomatic way to convert a set of integers into a set of ranges?
E.g. given the set {0, 1, 2, 3, 4, 7, 8, 9, 11} I want to get { {0,4}, {7,9}, {11,11} }.
Let's say we are converting from std::set<int> into std::vector<std::pair<int, int>>.
I treat Ranges as inclusive on both sides, since it's more convenient in my case, but I can work with open-ended ranges too if necessary.
I've written the following function, but I feel like reinventing the wheel.
Please tell maybe there's something in STL or boost for this.
typedef std::pair<int, int> Range;
void setToRanges(const std::set<int>& indices, std::vector<Range>& ranges)
{
Range r = std::make_pair(-INT_MAX, -INT_MAX);
BOOST_FOREACH(int i, indices)
{
if (i != r.second + 1)
{
if (r.second >= 0) ranges.push_back(r);
r.first = i;
}
r.second = i;
}
ranges.push_back(r);
}
Now one can use interval_set from Boost.ICL (Boost > 1.46)
#include <set>
#include <iostream>
#include <algorithm>
#include <boost/icl/discrete_interval.hpp>
#include <boost/icl/closed_interval.hpp>
#include <boost/icl/interval_set.hpp>
typedef std::set<int> Set;
typedef boost::icl::interval_set<int> IntervalSet;
void setToInterval(const Set& indices, IntervalSet& intervals)
{
Set::const_iterator pos;
for(pos = indices.begin(); pos != indices.end(); ++pos)
{
intervals.insert(boost::icl::construct<boost::icl::discrete_interval<int> >(*pos, *pos, boost::icl::interval_bounds::closed()));
}
}
int main()
{
std::cout << ">>Interval Container Library Rocks! <<\n";
std::cout << "----------------------------------------------------\n";
Set indices = {0, 1, 2, 3, 4, 7, 8, 9, 11};
IntervalSet intervals;
setToInterval(indices, intervals);
std::cout << " intervals joined: " << intervals << "\n";
return 0;
}
Output:
intervals joined: {[0,4][7,9][11,11]}
I don't think there's anything in the STL or Boost that does this.
One thing you can do is to make your algorithm a little bit more general:
template<class InputIterator, class OutputIterator>
void setToRanges(InputIterator first, InputIterator last, OutputIterator dest)
{
typedef std::iterator_traits<InputIterator>::value_type item_type;
typedef typename std::pair<item_type, item_type> pair_type;
pair_type r(-std::numeric_limits<item_type>::max(),
-std::numeric_limits<item_type>::max());
for(; first != last; ++first)
{
item_type i = *first;
if (i != r.second + 1)
{
if (r.second >= 0)
*dest = r;
r.first = i;
}
r.second = i;
}
*dest = r;
}
Usage:
std::set<int> set;
// insert items
typedef std::pair<int, int> Range;
std::vector<Range> ranges;
setToRanges(set.begin(), set.end(), std::back_inserter(ranges));
You should also consider using the term interval instead of range, because the latter in STL parlance means "any sequence of objects that can be accessed through iterators or pointers" (source).
Finally, you should probably take at look at the Boost Interval Arithmetic Library, which is currently under review for Boost inclusion.
No shrinkwrapped solution I'm afraid, but an alternative algorithm.
Store your items in a bitvector - O(n) if you know the maximum item at the start and preallocate the vector.
Translate that vector into a vector of transition point flags - exclusive-or the bitvector with a bitshifted version of itself. Slightly fiddly at the word boundaries, but still O(n). Logically, you get a new key at the old max + 1 (the transition back to zeros after all your keys are exhausted), so it's a good idea to allow for that in the preallocation of the vector.
Then, iterate through the bitvector finding the set bits. The first set bit indicates the start of a range, the second the end, the third the start of the next range and so on. The following bit-fiddling function (assuming 32 bit int) may be useful...
int Low_Bit_No (unsigned int p)
{
if (p == 0) return -1; // No bits set
int l_Result = 31;
unsigned int l_Range = 0xffffffff;
unsigned int l_Mask = 0x0000ffff;
if (p & l_Mask) { l_Result -= 16; } else { l_Mask ^= l_Range; }
l_Range &= l_Mask;
l_Mask &= 0x00ff00ff;
if (p & l_Mask) { l_Result -= 8; } else { l_Mask ^= l_Range; }
l_Range &= l_Mask;
l_Mask &= 0x0f0f0f0f;
if (p & l_Mask) { l_Result -= 4; } else { l_Mask ^= l_Range; }
l_Range &= l_Mask;
l_Mask &= 0x33333333;
if (p & l_Mask) { l_Result -= 2; } else { l_Mask ^= l_Range; }
l_Mask &= 0x55555555;
if (p & l_Mask) { l_Result -= 1; }
return l_Result;
}
I'd use adjacent_find with a predicate that defines "adjacency" as two elements that are not sequential. This solution doesn't depend on INT_MAX. Still feels kinda clunky.
bool notSequential(int a, int b) { return (a + 1) != b; }
void setToRanges(const std::set<int>& indices, std::vector<Range>& ranges)
{
std::set<int>::iterator iter = indices.begin();
std::set<int>::iterator end = indices.end();
int first;
while (iter != end)
{
first = *iter;
iter = std::adjacent_find(iter, end, notSequential);
if (iter != end)
{
ranges.push_back(std::make_pair(first, *iter));
++iter;
}
}
ranges.push_back(std::make_pair(first, *--iter));
}
That tests against end more than necessary. adjacent_find can never return the last element of a list, so the incremented iterator will never be end and thus can still be dereferenced. It could be rewritten as:
void setToRanges(const std::set<int>& indices, std::vector<Range>& ranges)
{
std::set<int>::iterator iter = indices.begin();
std::set<int>::iterator end = indices.end();
if (iter == end) return; // empty set has no ranges
int first;
while (true)
{
first = *iter;
iter = std::adjacent_find(iter, end, notSequential);
if (iter == end) break;
ranges.push_back(std::make_pair(first, *iter++));
}
ranges.push_back(std::make_pair(first, *--iter));
}