I have an app based on qt (qcustomplot) that prints two different graphs. They have one point of intersection. How to find x and y coordinates of this point?
This doesn't have much to do with plotting, since you'd be investigating the underlying data. Let's say that we can interpolate between data points using lines, and the data sets are single-valued (i.e. for any x or key coordinate, there's only one value).
Online demo of the code below
Let's sketch a solution. First, some preliminaries, and we detect whether QCustomPlot was included so that the code can be tested without it - the necessary classes are mocked:
#define _USE_MATH_DEFINES
#include <algorithm>
#include <cassert>
#include <cmath>
#include <iostream>
#include <optional>
#include <type_traits>
#include <vector>
//#include "qcustomplot.h"
constexpr bool debugOutput = false;
#ifndef QCP_PLOTTABLE_GRAPH_H
struct QCPGraphData {
double key, value;
QCPGraphData() = default;
QCPGraphData(double x, double y) : key(x), value(y) {}
};
#endif
auto keyLess(const QCPGraphData &l, const QCPGraphData &r) { return l.key < r.key; }
#ifndef QCP_PLOTTABLE_GRAPH_H
template <typename T> struct QCPDataContainer : public std::vector<T> {
using std::vector<T>::vector;
void sort() { std::sort(this->begin(), this->end(), keyLess); }
};
using QCPGraphDataContainer = QCPDataContainer<QCPGraphData>;
#endif
using Point = QCPGraphData;
using Container = QCPGraphDataContainer;
static_assert(std::is_copy_constructible_v<Point>, "Point must be copy-constructible");
Some helper functions:
std::ostream &operator<<(std::ostream &os, const Point &p) {
return os << "(" << p.key << ", " << p.value << ")";
}
template <class T> bool has_unique_keys(const T &v) {
constexpr auto keyEqual = [](const Point &l, const Point &r) { return l.key == r.key; };
return std::adjacent_find(std::begin(v), std::end(v), keyEqual) == std::end(v);
}
template <class T> bool has_valid_points(const T& v) {
constexpr auto isValid = [](const Point &p) { return std::isfinite(p.key) && std::isfinite(p.value); };
return std::all_of(std::begin(v), std::end(v), isValid);
}
The line segment intersection finder:
// intersection of two line segments
std::optional<Point> intersection(const Point &a1, const Point &a2, const Point &b1, const Point &b2)
{
auto p1 = a1, p2 = a2, p3 = b1, p4 = b2;
assert(p1.key <= p2.key);
assert(p3.key <= p4.key);
if (debugOutput) std::cout << p1 << "-" << p2 << ", " << p3 << "-" << p4;
auto const denom = (p1.key - p2.key)*(p3.value - p4.value)
- (p1.value - p2.value)*(p3.key - p4.key);
if (fabs(denom) > 1e-6*(p2.key - p1.key)) {
// the lines are not parallel
auto const scale = 1.0/denom;
auto const q = p1.key*p2.value - p1.value*p2.key;
auto const r = p3.key*p4.value - p3.value*p4.key;
auto const x = (q*(p3.key-p4.key) - (p1.key-p2.key)*r) * scale;
if (debugOutput) std::cout << " x=" << x << "\n";
if (p1.key <= x && x <= p2.key && p3.key <= x && x <= p4.key) {
auto const y = (q*(p3.value-p4.value) - (p1.value-p2.value)*r) * scale;
return std::optional<Point>(std::in_place, x, y);
}
}
else if (debugOutput) std::cout << "\n";
return std::nullopt;
}
An algorithm that walks down two lists of points sorted in ascending key (x) order, and finds all intersections of line segments spanning consecutive point pairs from these two lists:
std::vector<Point> findIntersections(const Container &a_, const Container &b_)
{
if (a_.size() < 2 || b_.size() < 2) return {};
static constexpr auto check = [](const auto &c){
assert(has_valid_points(c));
assert(std::is_sorted(c.begin(), c.end(), keyLess));
assert(has_unique_keys(c));
};
check(a_);
check(b_);
bool aFirst = a_.front().key <= b_.front().key;
const auto &a = aFirst ? a_ : b_, &b = aFirst ? b_ : a_;
assert(a.front().key <= b.front().key);
if (a.back().key < b.front().key) return {}; // the key spans don't overlap
std::vector<Point> intersections;
auto ia = a.begin(), ib = b.begin();
Point a1 = *ia++, b1 = *ib++;
while (ia->key < b1.key) a1=*ia++; // advance a until the key spans overlap
for (Point a2 = *ia, b2 = *ib;;) {
auto const ipt = intersection(a1, a2, b1, b2);
if (ipt)
intersections.push_back(*ipt);
bool advanceA = a2.key <= b2.key, advanceB = b2.key <= a2.key;
if (advanceA) {
if (++ia == a.end()) break;
a1 = a2, a2 = *ia;
}
if (advanceB) {
if (++ib == b.end()) break;
b1 = b2, b2 = *ib;
}
}
return intersections;
}
And a more generic version that can also sort the points in ascending key order:
auto findIntersections(Container &d1, Container &d2, bool presorted)
{
if (!presorted) {
d1.sort();
d2.sort();
}
return findIntersections(d1, d2);
}
And now some simple demonstration:
template <typename Fun>
Container makeGraph(double start, double step, double end, Fun &&fun) {
Container result;
int i = 0;
for (auto x = start; x <= end; x = ++i * step)
result.emplace_back(x, fun(x));
return result;
}
int main()
{
for (auto step2: {0.1, 0.1151484584}) {
auto sinPlot = makeGraph(-2*M_PI, 0.1, 3*M_PI, sin);
auto cosPlot = makeGraph(0., step2, 2*M_PI, cos);
auto intersections = findIntersections(sinPlot, cosPlot);
std::cout << "Intersections:\n";
for (auto &ip : intersections)
std::cout << " at " << ip << "\n";
}
}
Demo output:
Intersections:
at (0.785613, 0.706509)
at (3.92674, -0.706604)
Intersections:
at (0.785431, 0.706378)
at (3.92693, -0.706732)
Related
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 10 months ago.
Improve this question
Problem Description
We have a vector<A> v containing for 4 objects. Each object has the following members x, y, z and w. Two objects are considered equal if the have the same x and y. In that case we merge the objects: we merge the vector w and we change the value of z if and only if the value of object that we want to check if it exists is different from zero. Else, we consider that it's a new object.
In the following source code, I was able to implement the algorithm, but the main issue that it is O(n²) (because I am looping over each object of the vector then using find_if to check if we have a similar object or not in the merged vector).
Question
Is it possible to make it simpler (that is, less time complexity)? I can't find a way.
Source Code
#include <iostream>
#include <vector>
#include <algorithm>
class A{
public:
int x, y, z;
std::vector<int> w;
};
int main() {
A a1, a2, a3, a4;
a1.x = 1; a1.y =2; a1.z = 3; a1.w = {1,2,3,4,5};
a2.x = 4; a2.y =5; a2.z = 6; a2.w = {6,7,8,9};
a3.x = 13;a3.y =14; a3.z = 14; a3.w = {10,11,12};
a4.x = 1; a4.y =2; a4.z = 0;a4.w = {44,45,46,47,48};
std::vector<A> v = {a1, a2, a3, a4};
std::vector<A> merged;
/* If 2 objects have the same x and y then merge objects */
for(const A&a:v){
auto it = std::find_if(merged.begin(),merged.end(),[&](const A&ma){
/*2 objects are the same if they have the same x and y*/
return a.x == ma.x and a.y == ma.y;
});
/* if 2 objects have the same x and y then merge*/
if(it != merged.end()){
/* Replace z in the merged vector only if a.z is different from 0*/
if(a.z != 0){
it->z = a.z;
}
/* Merge vectors*/
std::vector<int> mergedws;
std::set_union(a.w.begin(),a.w.end(),it->w.begin(),it->w.end(), std::back_inserter(mergedws));
it->w = mergedws;
} else {
/*We consider that a is a new object, since we couldn't find a similar object in the merged vector*/
merged.push_back(a);
}
}
/* merged vector should have 3 objects because a1 and a4 the same*/
std::cout <<"Number of Objects is: "<< merged.size() << std::endl;
for(const auto&m:merged){
std::cout <<"Element "<< m.x <<", "<< m.y <<","<<m.z << std::endl;
}
return 0;
}
You can do it in O(NlogN * MlogM) if you sort the input, and then do a linear pass to merge.
N is the length of v, and M is the length of the A::ws.
bool compare(const A & lhs, const A & rhs) {
return std::tie(lhs.x, lhs.y) < std::tie(rhs.x, rhs.y);
}
std::sort(v.begin(), v.end(), compare);
for (auto first = v.begin(), last = {}; it != v.end(); it = last) {
A result = *first;
last = std::upper_bound(first++, v.end(), result, compare);
for (; first != last; ++first) {
if (first->z) {
result.z = first->z;
}
// this is an in-place set_union
std::merge(result.w.begin(), result.w.end(), first->w.begin(), first->w.end());
auto unique_end = std::unique(result.w.begin(), result.w.end());
result.w.erase(unique_end, result.w.end());
}
merged.push_back(result);
}
The best way is to use std::unordered_map this will allow to find matching item in constant time, so final time complexity will be O(n):
class A {
public:
int x, y, z;
std::vector<int> w;
};
struct Point2d {
int x, y;
Point2d(int x, int y)
: x { x }
, y { y }
{
}
Point2d(const A& a)
: x { a.x }
, y { a.y }
{
}
};
bool operator==(const Point2d& l, const Point2d& r)
{
return l.x == r.x && l.y == r.y;
}
template <>
struct std::hash<Point2d> {
size_t operator()(const Point2d& p) const
{
std::hash<int> sub_hash {};
return (sub_hash(p.x) * 16777619) ^ sub_hash(p.y);
}
};
template <typename T, typename F>
std::unordered_map<Point2d, A> merge_collection(const T& collection, F f)
{
std::unordered_map<Point2d, A> r;
for (const auto& item : collection) {
f(r[item], item);
}
return r;
}
void merge_a(A& dest, const A& toMerge)
{
std::vector<int> w;
w.reserve(dest.w.size() + toMerge.w.size());
std::merge(dest.w.begin(), dest.w.end(), toMerge.w.begin(), toMerge.w.end(), std::back_inserter(w));
dest = {dest.x, dest.y, dest.z, std::move(w)};
}
template <typename T>
std::unordered_map<Point2d, A> merge_collection(const T& collection)
{
return merge_collection(collection, merge_a);
}
https://godbolt.org/z/Wz8vbz4h1
I have a 6D vector and I need to check neighborhood of each element (2 elements in each direction). Of course when I am on boundary of the vector, check leads in the Segmentation fault. All I can do is having switch with ton of cases. Is there any better way to solve this? I also thought of something like try-catch.
Still too bulky but it works:
#include <iostream>
#include <array>
#include <vector>
typedef std::vector<int> Vector1D;
typedef std::vector<Vector1D> Vector2D;
typedef std::vector<Vector2D> Vector3D;
typedef std::vector<Vector3D> Vector4D;
typedef std::vector<Vector4D> Vector5D;
typedef std::vector<Vector5D> Vector6D;
typedef std::array<size_t, 6> Path;
bool GetVectorPathElement(Vector6D const &vector6D, Path const &path, int &val)
{
size_t i = 0, k = path[i];
if (vector6D.size() > k)
{
Vector5D const &vector5D = vector6D[k];
k = path[++i];
if (vector5D.size() > k)
{
Vector4D const &vector4D = vector5D[k];
k = path[++i];
if (vector4D.size() > k)
{
Vector3D const &vector3D = vector4D[k];
k = path[++i];
if (vector3D.size() > k)
{
Vector2D const &vector2D = vector3D[k];
k = path[++i];
if (vector2D.size() > k)
{
Vector1D const &vector1D = vector2D[k];
k = path[++i];
if (vector1D.size() > k)
{
val = vector1D[k];
return true;
}
}
}
}
}
}
std::cout << "Invalid path " << k << " at index " << i << std::endl;
return false;
}
int main()
{
Vector1D vector1D = { 1,2,3,4,5,6 };
Vector2D vector2D = { vector1D, vector1D, vector1D, vector1D, vector1D };
Vector3D vector3D = { vector2D, vector2D, vector2D, vector2D };
Vector4D vector4D = { vector3D, vector3D, vector3D };
Vector5D vector5D = { vector4D, vector4D };
Vector6D vector6D = { vector5D };
Path path = { 0,0,2,1,4,5 };
int element;
if (GetVectorPathElement(vector6D, path, element))
{
std::cout << "Path: ";
for (auto i : path)
std::cout << i << " ";
std::cout << "\nElement value at destination: " << element << std::endl;
}
return 0;
}
https://ideone.com/nL1zo2
I would like to translate the following traditional for loop into a C++11 for-each loop without extra looping constructs:
int a[] = { 5, 6, 7, 8, 9, 10 };
int b[] = { 50, 60, 70, 80, 90, 100 };
// Swap a and b array elements
for (int i = 0; i < sizeof(a)/sizeof(a[0]); i++)
{
a[i] ^= b[i]; b[i] ^= a[i]; a[i] ^= b[i];
}
Does there exist any way by which it is possible to provide more than one variable in the C++11 for-each loop like:
for (int i, int j : ...)
There is no built-in way to do this. If you can use Boost, boost::combine will work for iterating two (or more) ranges simultaneously (Does boost offer make_zip_range?, How can I iterate over two vectors simultaneously using BOOST_FOREACH?):
for (boost::tuple<int&, int&> ij : boost::combine(a, b)) {
int& i = boost::get<0>(ij);
int& j = boost::get<1>(ij);
// ...
}
Unfortunately accessing the elements within the tuple elements of the zipped range is highly verbose. C++17 will make this much more readable using structured binding:
for (auto [&i, &j] : boost::combine(a, b)) {
// ...
}
Since you don't need to break out of the loop or return from the enclosing function, you could use boost::range::for_each with the body of your loop as a lambda:
boost::range::for_each(a, b, [](int& i, int& j)
{
// ...
});
zip or combine ranges are common in many range libraries.
Writing one strong enough for a for(:) loop isn't hard however.
First we write a basic range type:
template<class It>
struct range_t {
It b,e;
It begin() const{ return b; }
It end() const{ return e; }
range_t without_front( std::size_t count = 1 ) const {
return {std::next(begin()), end()};
}
bool empty() const { return begin()==end(); }
};
template<class It>
range_t<It> range( It b, It e ) { return {b,e}; }
template<class C>
auto range( C& c ) {
using std::begin; using std::end;
return range( begin(c), end(c) );
};
Then we write an iterator that works with ranges (easier than with iterators):
template<class R1, class R2>
struct double_foreach_iterator {
R1 r1;
R2 r2;
void operator++() { r1 = r1.without_front(); r2 = r2.without_front(); }
bool is_end() const { return r1.empty() || r2.empty(); }
auto operator*()const {
return std::tie( *r1.begin(), *r2.begin() );
}
using self=double_foreach_iterator;
auto cur() const {
return std::make_tuple( r1.begin(), r2.begin() );
}
friend bool operator==( self const& lhs, self const& rhs ) {
if (lhs.is_end() || rhs.is_end())
return lhs.is_end() == rhs.is_end();
return lhs.cur() == rhs.cur();
}
friend bool operator!=( self const& lhs, self const& rhs ) {
return !(lhs==rhs);
}
};
now we double iterate:
template<class A, class B>
auto zip_iterate(
A& a, B& b
) {
auto r1 = range(a);
auto r2 = range(b);
auto r1end = range(r1.end(), r1.end());
auto r2end = range(r2.end(), r2.end());
using it = double_foreach_iterator<decltype(r1), decltype(r2)>;
return range( it{r1, r2}, it{r1end, r2end} );
}
which gives us:
for (auto tup : zip_iterate(a, b)) {
int& i = std::get<0>(tup);
int& j = std::get<1>(tup);
// ...
}
or in C++17:
for (auto&& [i, j] : zip_iterate(a, b)) {
// ...
}
My zip iterate does not assume the two containers are of the same length, and will iterate to the length of the shorter one.
live example.
Just for fun.
The following isn't intended to be a serious answer to the question but just an exercise to try to understand the potentiality of C++11 (so, please, be patient).
The following is an example of a class (a draft of a class) that receive a couple of container (with size() method), with the same size (exception otherwise), and of a custom iterator that return a std::pair of std::reference_wrapper to n-position elements.
With a simple use example that show that it's possible to change the value in the starting containers.
Doesn't work with old C-style arrays but works with std::array. We're talking about C++11 so I suppose we could impose the use of std::array.
#include <array>
#include <vector>
#include <iostream>
#include <functional>
template <typename T1, typename T2>
class pairWrapper
{
public:
using V1 = typename std::remove_reference<decltype((T1().at(0)))>::type;
using V2 = typename std::remove_reference<decltype((T2().at(0)))>::type;
using RW1 = std::reference_wrapper<V1>;
using RW2 = std::reference_wrapper<V2>;
class it
{
public:
it (pairWrapper & pw0, std::size_t p0): pos{p0}, pw{pw0}
{ }
it & operator++ ()
{ ++pos; return *this; }
bool operator!= (const it & it0)
{ return pos != it0.pos; }
std::pair<RW1, RW2> & operator* ()
{
static std::pair<RW1, RW2>
p{std::ref(pw.t1[0]), std::ref(pw.t2[0])};
p.first = std::ref(pw.t1[pos]);
p.second = std::ref(pw.t2[pos]);
return p;
}
private:
std::size_t pos;
pairWrapper & pw;
};
it begin()
{ return it(*this, 0U); }
it end()
{ return it(*this, len); }
pairWrapper (T1 & t10, T2 & t20) : len{t10.size()}, t1{t10}, t2{t20}
{ if ( t20.size() != len ) throw std::logic_error("no same len"); }
private:
const std::size_t len;
T1 & t1;
T2 & t2;
};
template <typename T1, typename T2>
pairWrapper<T1, T2> makePairWrapper (T1 & t1, T2 & t2)
{ return pairWrapper<T1, T2>(t1, t2); }
int main()
{
std::vector<int> v1 { 1, 2, 3, 4 };
std::array<long, 4> v2 { { 11L, 22L, 33L, 44L } };
for ( auto & p : makePairWrapper(v1, v2) )
{
std::cout << '{' << p.first << ", " << p.second << '}' << std::endl;
p.first += 3;
p.second += 55;
}
for ( const auto & i : v1 )
std::cout << '[' << i << ']' << std::endl;
for ( const auto & l : v2 )
std::cout << '[' << l << ']' << std::endl;
return 0;
}
p.s.: sorry for my bad English
Let's say I have a vector declared like this:
struct MYSTRUCT
{
float a;
float b;
};
std::vector<MYSTRUCT> v;
Now, I want to find all elements of v that share the same a, and average their b, i.e.
Say v contains these five elements {a, b}: {1, 1}, {1, 2}, {2, 1}, {1, 3}, {2, 2}
I want to get v[0], v[1], v[3] (where a is 1) and average b: (1 + 2 + 3)/3 = 2, and v[2] and v[4] (where a is 2) and average b: (1+2)/2 = 1.5
Afterwards v will look like this: {1, 2}, {1, 2}, {2, 1.5}, {1, 2}, {2, 1.5}
I'm not really familiar with STL or Boost so I can only figure out how to do this the "bruteforce" way in C++, but I'm guessing that the STL (for_each?) and Boost (lambda?) libraries can solve this more elegantly.
EDIT Just for reference, here's my (working) brute force way to do it:
for(int j = 0; j < tempV.size(); j++)
{
MYSTRUCT v = tempV.at(j);
int matchesFound = 0;
for(int k = 0; k < tempV.size(); k++)
{
if(k != j && v.a == tempV.at(k).a)
{
v.b += tempV.at(k).b;
matchesFound++;
}
}
if(matchesFound > 0)
{
v.b = v.b/matchesFound;
}
finalV.push_back(v);
}
Just thinking aloud, this may end up fairly silly:
struct Average {
Average() : total(0), count(0) {}
operator float() const { return total / count; }
Average &operator+=(float f) {
total += f;
++count;
}
float total;
int count;
};
struct Counter {
Counter (std::map<int, Average> &m) : averages(&m) {}
Counter operator+(const MYSTRUCT &s) {
(*averages)[s.a] += s.b;
return *this;
}
std::map<int, Average> *averages;
};
std::map<int, Average> averages;
std::accumulate(v.begin(), v.end(), Counter(averages));
BOOST_FOREACH(MYSTRUCT &s, v) {
s.b = averages[s.a];
}
Hmm. Not completely silly, but perhaps not compelling either...
Sketch of a solution:
sort(v.begin(), v.end());
vector<MYSTRUCT>::iterator b = v.begin(), e = v.end();
while (b != e) {
vector<MYSTRUCT>::iterator m = find_if(b, e, bind(&MYSTRUCT::a, _1) != b->a);
float x = accumulate(b, m, 0.f, _1 + bind(&MYSTRUCT::b,_2)) / (m-b);
for_each(b, m, bind(&MYSTRUCT::a, _1) = x);
b = m;
}
It's not a great one, though, since it's not exactly what was asked for (thanks to the sort), and still doesn't really feel clean to me. I think that some filter_iterators and transform_iterators or something could possibly give a much more functional-style answer.
Another approach, this one not in-place, though I think it's time-complexity-wise asymptotically the same.
typedef map<float, vector<float>> map_type;
map_type m;
BOOST_FOREACH(MYSTRUCT const &s, v) {
m[s.a].push_back(s.b);
}
BOOST_FOREACH(map_type::reference p, m) {
float x = accumulate(p.second.begin(), p.second.end(), 0.0f) / p.second.size();
p.second.assign(1, x);
}
BOOST_FOREACH(MYSTRUCT &s, v) {
s.b = m[s.a].front();
}
Again, though, it's just a slightly elegant way to code the brute-force solution, not a nice functional-style way.
Perhaps a brute force approach?...
struct MYAVG
{
int count;
float avg;
};
// first pass - calculate averages
for ( vector < MYSTRUCT >::iterator first = v.begin();
first != v.end(); ++first )
{
MYAVG myAvg;
myAvg.count = 1;
myAvg.avg = first->b;
if ( mapAvg.find( first->a ) == mapAvg.end() )
mapAvg[ first->a ] = myAvg;
else
{
mapAvg[ first->a ].count++;
mapAvg[ first->a ].avg =
( ( mapAvg[ first->a ].avg * ( mapAvg[ first->a ].count - 1 ) )
+ myAvg.avg ) / mapAvg[ first->a ].count;
}
}
// second pass - update average values
for ( vector < MYSTRUCT >::iterator second = v.begin();
second != v.end(); ++second )
second->b = mapAvg[ second->a ].avg;
I've tested this with the values you've supplied and get the required vector - It's not exactly optimal, but I think it's quite easy to follow (might be more preferable to a complex algorithm).
Avoid C-style! It's not what C++ is designed for. I'd like to emphasize clarity and readability.
#include <algorithm>
#include <iostream>
#include <map>
#include <numeric>
#include <vector>
#include <boost/assign/list_of.hpp>
using namespace std;
using namespace boost::assign;
struct mystruct
{
mystruct(float a, float b)
: a(a), b(b)
{ }
float a;
float b;
};
vector <mystruct> v =
list_of ( mystruct(1, 1) ) (1, 2) (2, 1) (1, 3) (2, 2);
ostream& operator<<(
ostream& out, mystruct const& data)
{
out << "{" << data.a << ", " << data.b << "}";
return out;
}
ostream& operator<<(
ostream& out, vector <mystruct> const& v)
{
copy(v.begin(), v.end(),
ostream_iterator <mystruct> (out, " "));
return out;
}
struct average_b
{
map <float, float> sum;
map <float, int> count;
float operator[] (float a) const
{
return sum.find(a)->second / count.find(a)->second;
}
};
average_b operator+ (
average_b const& average,
mystruct const& s)
{
average_b result( average );
result.sum[s.a] += s.b;
++result.count[s.a];
return result;
}
struct set_b_to_average
{
set_b_to_average(average_b const& average)
: average(average)
{ }
mystruct operator()(mystruct const& s) const
{
return mystruct(s.a, average[s.a]);
}
average_b const& average;
};
int main()
{
cout << "before:" << endl << v << endl << endl;
transform(v.begin(), v.end(),
v.begin(), set_b_to_average(
accumulate(v.begin(), v.end(), average_b())
));
cout << "after:" << endl << v << endl << endl;
}
You can use the "partition" algorithm along with "accumulate."
Example
#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
struct test
{
float a;
float b;
test(const float one, const float two)
: a(one), b(two)
{
}
};
struct get_test_a {
float interesting;
get_test_a(const float i)
: interesting(i)
{
}
bool operator()(const test &value) const
{
static const float epi = 1e-6;
return value.a < interesting + epi &&
value.a > interesting - epi;
}
};
struct add_test_b {
float operator()(const float init, const test &value) const
{
return init + value.b;
}
};
int main(int argc, char **argv)
{
using std::partition;
using std::accumulate;
using std::distance;
typedef std::vector<test> container;
container myContainer;
// Say 'myVector' contains these five elements {a, b}:
// {1, 1}, {1, 2}, {2, 1}, {1, 3}, {2, 2}
myContainer.push_back(test(1, 1));
myContainer.push_back(test(1, 2));
myContainer.push_back(test(2, 1));
myContainer.push_back(test(1, 3));
myContainer.push_back(test(2, 2));
// I want to get v[0], v[1], v[3] (where a is 1) and
// average b: (1 + 2 + 3)/3 = 2,
// and v[2] and v[4] (where a is 2) and average b: (1+2)/2 = 1.5
const container::iterator split =
partition(myContainer.begin(), myContainer.end(),
get_test_a(1));
const float avg_of_one =
accumulate(myContainer.begin(), split, 0.0f, add_test_b())
/ distance(myContainer.begin(), split);
const float avg_of_others =
accumulate(split, myContainer.end(), 0.0f, add_test_b())
/ distance(split, myContainer.end());
std::cout << "The 'b' average of test values where a = 1 is "
<< avg_of_one << std::endl;
std::cout << "The 'b' average of the remaining test values is "
<< avg_of_others << std::endl;
return 0;
}
Documentation from the gcc headers
/**
* #brief Move elements for which a predicate is true to the beginning
* of a sequence.
* #ingroup mutating_algorithms
* #param first A forward iterator.
* #param last A forward iterator.
* #param pred A predicate functor.
* #return An iterator #p middle such that #p pred(i) is true for each
* iterator #p i in the range #p [first,middle) and false for each #p i
* in the range #p [middle,last).
*
* #p pred must not modify its operand. #p partition() does not preserve
* the relative ordering of elements in each group, use
* #p stable_partition() if this is needed.
*/
template<typename _ForwardIterator, typename _Predicate>
inline _ForwardIterator
partition(_ForwardIterator __first, _ForwardIterator __last,
_Predicate __pred)
/**
* #brief Accumulate values in a range with operation.
*
* Accumulates the values in the range [first,last) using the function
* object #a binary_op. The initial value is #a init. The values are
* processed in order.
*
* #param first Start of range.
* #param last End of range.
* #param init Starting value to add other values to.
* #param binary_op Function object to accumulate with.
* #return The final sum.
*/
template<typename _InputIterator, typename _Tp, typename _BinaryOperation>
inline _Tp
accumulate(_InputIterator __first, _InputIterator __last, _Tp __init,
_BinaryOperation __binary_op)
It seems the easiest way is to run a moderately complex functor over the colelction:
struct CountAllAverages {
typedef std::pair<float, unsigned> average_t;
std::map<float, average_t> averages;
void operator()(mystruct& ms) {
average_t& average = averages[ms.a];
average.second++;
average.first += ms.b;
}
float getAverage(float a) { return averages[a].first/averages[a].second; }
};
Writing C++, you should maintain balance between reusability (e.g. reuse existing algorithms and data structures) and readability. onebyone was close, but his solution can be further improved:
template<class T>
struct average {
T total;
int count;
mutable bool calculated;
mutable T average_value;
average & operator+=(T const & value) {
total += value;
++count;
calculated = false;
}
T value() const {
if(!calculated) {
calculated = true;
average_value = total / count;
}
return average_value;
}
};
std::map< float, average<float> > averages;
BOOST_FOREACH(MYSTRUCT &element, v) {
averages[element.a] += element.b;
}
BOOST_FOREACH(MYSTRUCT &element, v) {
element.b = averages[element.a].value();
}
Bonus points for having reusable "average" type.
struct MYSTRUCT {
float x;
float y;
operator float() const { return y; }
};
class cmp {
float val;
public:
cmp(float v) : val(v) {}
bool operator()(MYSTRUCT const &a) { return a.x != val; }
};
float masked_mean(std::vector<MYSTRUCT> const &in, MYSTRUCT const &mask) {
std::vector<float> temp;
std::remove_copy_if(in.begin(), in.end(), std::back_inserter(temp), cmp(mask.x));
return std::accumulate(temp.begin(), temp.end(), 0.0f) / temp.size();
}
I need a structure to hold a value based on a key that has a range.
My implementation is C++, so any STL or Boost would be excellent.
I have as range-key, which are doubles, and value
[0,2) -> value1
[2,5) -> value2
[5,10) -> value3
etc
Such that a search of 1.23 should return value1, and so on.
Right now I am using a vector containing all three parts, key1/key2/value, with custom searching, but it feels like there should be a cleaner structure.
Edit: Thanks all. Given the ranges in this case are supposed to be contiguous and non-overlapping, the use of upper_bound will work just fine. Thanks for the class Range solutions as well, they are filed away for future reference.
class Range
{
public:
Range( double a, double b ):
a_(a), b_(b){}
bool operator < ( const Range& rhs ) const
{
return a_ < rhs.a_ && b_ < rhs.b_;
}
private:
double a_;
double b_;
};
int main()
{
typedef std::map<Range, double> Ranges;
Ranges r;
r[ Range(0, 2) ] = 1;
r[ Range(2, 5) ] = 2;
r[ Range(5, 10) ] = 3;
Ranges::const_iterator it1 = r.find( Range( 2, 2 ) );
std::cout << it1->second;
Ranges::const_iterator it2 = r.find( Range( 2, 3 ) );
std::cout << it2->second;
Ranges::const_iterator it3 = r.find( Range( 6, 6 ) );
std::cout << it3->second;
return 0;
}
If your ranges are contiguous and non-overlapping, you should use std::map and the upper_bound member function. Or, you could use a sorted vector with the upper_bound algorithm. Either way, you only need to record the lowest value of the range, with the upper part of the range being defined by the next higher value.
Edit: I phrased that confusingly, so I decided to provide an example. In coding the example, I realized you need upper_bound instead of lower_bound. I always get those two confused.
typedef std::map<double, double> MyMap;
MyMap lookup;
lookup.insert(std::make_pair(0.0, dummy_value));
lookup.insert(std::make_pair(2.0, value1));
lookup.insert(std::make_pair(5.0, value2));
lookup.insert(std::make_pair(10.0, value3));
MyMap::iterator p = lookup.upper_bound(1.23);
if (p == lookup.begin() || p == lookup.end())
...; // out of bounds
assert(p->second == value1);
How about something along these lines:
#include "stdafx.h"
#include <iostream>
#include <string>
#include <map>
#include <algorithm>
#include <sstream>
class Range
{
public:
Range(double lower, double upper) : lower_(lower), upper_(upper) {};
Range(const Range& rhs) : lower_(rhs.lower_), upper_(rhs.upper_) {};
explicit Range(const double & point) : lower_(point), upper_(point) {};
Range& operator=(const Range& rhs)
{
lower_ = rhs.lower_;
upper_ = rhs.upper_;
return * this;
}
bool operator < (const Range& rhs) const
{
return upper_ <= rhs.lower_;
}
double lower_, upper_;
};
typedef std::string Thing;
typedef std::map<Range, Thing> Things;
std::string dump(const std::pair<Range,Thing> & p)
{
stringstream ss;
ss << "[" << p.first.lower_ << ", " << p.first.upper_ << ") = '" << p.second << "'" << endl;
return ss.str();
}
int main()
{
Things things;
things.insert( std::make_pair(Range(0.0, 5.0), "First") );
things.insert( std::make_pair(Range(5.0, 10.0), "Second") );
things.insert( std::make_pair(Range(10.0, 15.0), "Third") );
transform( things.begin(), things.end(), ostream_iterator<string> (cout,""), dump );
cout << "--------------------------------------" << endl;
things[Range(1.5)] = "Revised First";
transform( things.begin(), things.end(), ostream_iterator<string> (cout,""), dump );
return 0;
}
... program output:
[0, 5) = 'First'
[5, 10) = 'Second'
[10, 15) = 'Third'
--------------------------------------
[0, 5) = 'Revised First'
[5, 10) = 'Second'
[10, 15) = 'Third'