idiom for iterating a range of angles in a container? - c++

Suppose I have data of 2D lines in the form
struct Point { int x, y; };
struct Line {
Point p1, p2;
double angle() const { return atan2(p2.y-p1.y, p2.x-p1.x); }
};
And I want to store these sorted by angle, which must be in the interval (-PI, PI].
My problem: I want to iterate a range in this container, but allow it to wrap around the ends of the interval. For example "all lines between angles PI*3/4 to -PI*3/4".
To clarify, if I use standard containers like multimap, I can't simply do the usual:
std::multimap<double, Line> lm;
//insert elements...
auto begin = lm.lower_bound(PI*3/4);
auto end = lm.upper_bound(-PI*3/4);
for(auto & i = begin; i != end; ++i) { //infinite loop: end is before begin!
//do stuff with i
}
I could hack up a "circularly iterate i" function to take the place of the ++i in the loop, I guess. But this seems like it should be a common problem, so I'm wondering if there's already an existing idiom to address it?

There is trigonometric approach to solve problems with circular ranges. For range - normalize its ends (examples here), and get middle angle and half-angle
if range_end < range_start then
range_end = range_end + 2 * Pi
half = (range_end - range_start) / 2
mid = (range_end + range_start) / 2
coshalf = Cos(half)
Now compare that difference of angle and range middle is lower then half-angle. Cosine solves potential troubles with periodicity, negative values etc.
if Cos(angle - mid) >= coshalf then
angle lies in range

Related

how to correctly call std::lower_bound()?

I'm plotting a data file with two columns (frequency and voltage) and I need to look for the closest value to a given value val. The thing is that my data behaves like a gaussian, so there are two values that satisfies that, above and below a max value. First I get each column in the data file into a vector array, having that I defined this function for finding those values
#include<iostream>
#include<vector>
#include<cmath>
typedef std::vector <double> vector;
vector posi(vector vec, int ref, double val);
int main(void){
//define a custom volt vector here e.g vector volt{...};
auto auxvmax = std::max_element(volt.begin(), volt.end());
int posvmax = auxvmax - volt.begin();//this is what I take as ref value
double val = 0.7;
vector fpos(2, 0.0);
fpos = posi(volt, posvmax, val);
double auxf_1 = fpos[0];
double auxf_2 = fpos[1];
std::cout << "closest value to " << val << " are " << volt[auxf_1] << " below and " << volt[auxf_2] << " above\n";
return 0;
}
vector posi(vector vec, int ref, double val){
vector posvec(2, 0.0);
auto pos1 = std::lower_bound(vec.begin(), vec.begin() + ref, val);
auto pos2 = std::lower_bound(vec.begin() + ref, vec.end(), val);
double val1a = *(pos1 - 1.0);
double val1b = *pos1;
double val2a = *(pos2 - 1.0);
double val2b = *pos2;
if(fabs(val - val1a) < fabs(val - val1b)){
posvec[0] = pos1 - vec.begin() - 1;
}
if(fabs(val - val1a) > fabs(val1b)){
posvec[0] = pos1 - vec.begin();
}
if(fabs(val - val2a) < fabs(val - val2b)){
posvec[1] = pos2 - vec.begin() - 1;
}
if(fabs(val - val2a) > fabs(val - val2b)){
posvec[1] = pos2 - vec.begin();
}
return posvec;
}
Let me explain how and why I constructed the function like this, so you can tell me where am I wrong.
Basically, I'm trying to use std::lower_bound() in two "regions" of the vector in which the values are, this is so that the program looks for only one closest value in each region, I know where the max value is (in main function, via std::max_element()) so I can easily make the split. ref is the position (iterator) where the max value is allocated in vec vector, so it is supposed to get the closest value to val above and below that ref position.
Next, it only makes sure that the value is the closest, considering the smallest next value to val (default value given by std::lower_bound()) and the previous one, getting the closest in each case (region). Finally, the position in the vector of each value is stored into posvec vector (since I have to get the frequencies where voltage is the closest to val, so I don't need the voltage value but his position, since freq and volt make pairs).
when I compile it gives no errors, no warnings, but the closest values are not the closest. I found (cppreference) that the array that std::lower_bound() gets must be sorted from low to max, and my data is sorted from lowest to max below the max value, and max to lowest above max value, so there should be a problem with the above data, but -and here is my question- why am I not getting the closest value even with the data below? am I not getting something with the std::lower_bound() behavior?, is it maybe something when I use the if statements? the output, printing an example voltage vector as well is below
Here you can see the "closest" values are indeed the furthest.
Thanks for your help in advance.
EDIT: asked in comments, the output is
closest values to 0.7 are 0.485437 below, and 0.320388 above
0.485437
0.500971
0.524272
0.543689
0.563107
0.594175
0.617476
0.648544
0.679612
0.71068
0.741748
0.786408
0.825243
0.864078
0.893204
0.932039
0.961165
0.980583
0.990291
1
0.990291
0.961165
0.941748
0.893204
0.854369
0.805825
0.757282
0.708738
0.669903
0.621359
0.582524
0.547573
0.512621
0.481553
0.454369
0.427184
0.403883
0.384466
0.361165
0.341748
0.320388

How to optimize the sorting of a vector with coordinates based on distance?

I need to sort a vector of coordinates (x, y >= 1) in a way that every next point from the vector is the closest one to the previous by calculating the distance with the formula from getDistance().
My current solution is too slow as I need the program to be able to finish in 5 seconds or less with vector length (N) equal to 100 000.
struct Point {
int ind;
int x;
int y;
double dist;
};
double getDist(int x1, int y1, int x2, int y2) {
return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
}
vector<Point> cordSort(vector<Point> vect) {
vector<Point> finalDistVect;
finalDistVect.push_back(vect[0]);
Point firstPoint = vect[0];
vect.erase(vect.begin());
for (i = 0; i < pcVect.size() - 1; i++) {
sort(vect.begin(), vect.end(), [firstPoint](const Point & a, const Point & b) {
return getDist(firstPoint.x, firstPoint.y, a.x, a.y) < getDist(firstPoint.x, firstPoint.y, b.x, b.y);
});
finalDistVect.push_back(vect[0]);
finalDistVect[i].dist = getDist(firstPoint.x, firstPoint.y, vect[0].x, vect[0].y);
firstPoint = vect[0];
vect.erase(vect.begin());
}
return finalDistVect;
}
vect is the initial vector with coordinates sorted by:
sort(vect.begin(), vect.end(), [](const Point & a, const Point & b) {
if (a.x + a.y != b.x + b.y) {
return a.x + a.y < b.x + b.y;
}
return a.x < b.x;
});
I am thinking about implementing bucket sort but I don't know if it will work for my problem.
your implementation indeed increase inefficiency by repeatedly erase the first element from a vector. std::vector is not designed to be used to frequently erase elements from other the back.
Not sure if I read your algorithm correctly. The first element is predetermined as the first element of the input, then your program repeatedly find the points that has shortest distance from the last element (point) in the output point vector.
If that's the case, it has no benefit to sort at all.
A naive algorithm is like bubbling:
1. add first point to outputVector, add all rest to openSet;
2. if openSet is empty, we are done.
3. take the last point from output Vector, check with all points in `openSet`, to find the one with shortest distance from it, add it to outputVector, remove it from openSet;
4. goto 2
Basically I am recommending you do use a std::set to keep track of openset, or maybe even better, std::unordered_set.
Another way is to do it in place, just swap the chosen points with the one who is taking its place.
e.g. we have P0, P1, P2, P3, P4 as input in a vector
1. int pos = 1; // P0 is in right place, we are looking for
// the point that shall go to index 1;
2. check all points from index `pos` to `4`(which is the max index) and find the one with shortest distance from `P0`, let's say we get `P4`;
3. swap `P0` (the one at index `pos`) and `P4` (the chosen one);
4. ++ pos;
5. if pos!=4(max index), goto 2.
We are using pos to keep track of sorted and open and do it in place.
This is not a sorting problem, you have to come up with a different algorithm. For example, there may be no solution at all, which would not be the case for a sorting problem.
Consider four points on a single line: A=(1, 1), B=(100, 1), C=(101, 1), D=(1000, 1). Point D is not the closest point for any other point, so it should come first. Then we should put C, followed by B, and now we cannot put A because the closest point to B is actually C, not A.
And even if it was, you should come up with a faster algorithm. You have N iterations of the for loop, each iteration looks for the smallest element among N other elements, which is already at least O(N^2). Using sort instead of min_element makes it even worse: O(N^2 log N). This won't fly for N ~ 100'000 at all in competitive programming/home assignments.

Points on the same line

I was doing a practice question and it was something like this,We are given N pair of coordinates (x,y) and we are given a central point too which is (x0,y0).We were asked to find maximum number of points lying on a line passing from (x0,y0).
My approach:- I tried to maintain a hash map having slope as the key and I thought to get the maximum second value to get maximum number of points on the same line.Something like this
mp[(yi-y0)/(xi-x0))]++; //i from 0 to n
And iterating map and doing something line this
if(it->second >max) //it is the iterator
max=it->second;
and printing max at last;
Problem With my approach- Whenever I get (xi-x0) as 0 I get runtime error.I also tried atan(slope) so that i would get degrees instead of some not defined value but still its not working.
What i expect->How to remove this runtime error and is my approach correct for finding maximum points on a line passing from a point(x0,y0).
P.S -My native language is not english so please ignore if something goes wrong.I tried my best to make everything clear If i am not clear enough please tell me
I'm assuming no other points have the same coordinates as your "origin".
If all your coordinates happen to be integers, you can keep a rational number (i.e. a pair of integers, i.e. a numerator and a denominator) as the slope, instead of a single real number.
The slope is DeltaY / DeltaX, so all you have to do is keep the pair of numbers separate. You just need to take care to divide the pair by their greatest common divisor, and handle the case where DeltaX is zero. For example:
pair<int, int> CalcSlope (int x0, int y0, int x1, int y1)
{
int dx = abs(x1 - x0), dy = abs(y1 - y0);
int g = GCD(dx, dy);
return {dy / g, dx / g};
}
Now just use the return value of CalcSlope() as your map key.
In case you need it, here's one way to calculate the GCD:
int GCD (int a, int b)
{
if (0 == b) return a;
else return gcd(b, a % b);
}
You have already accepted an answer, but I would like to share my approach anyway. This method uses the fact that three points a, b, and c are covariant if and only if
(a.first-c.first)*(b.second-c.second) - (a.second-c.second)*(b.first-c.first) == 0
You can use this property to create a custom comparison object like this
struct comparePoints {
comparePoints(int x0 = 0, int y0 = 0) : _x0(x0), _y0(y0) {}
bool operator()(const point& a, const point& b) {
return (a.first-_x0)*(b.second-_y0) - (b.first-_x0)*(a.second-_y0) < 0;
}
private:
int _x0, _y0;
};
which you can then use as a comparison object of a map according to
comparePoints comparator(x0, y0);
map<pair<int, int>, int, comparePoints> counter(comparator);
You can then add points to this map similar to what you did before:
if (!(x == x0 && y == y0))
counter[{x,y}]++;
By using comparitor as a comparison object, two keys a, b in the map are considered equal if !comparator(a, b) && !comparator(b,a), which is true if and only if a, b and {x0,y0} are collinear.
The advantage of this method is that you don't need to divide the coordinates which avoids rounding errors and problems with dividing by zero, or calculate the atan which is a costly operation.
Move everything so that the zero point is at the origin:
(xi, yi) -= (x0, y0)
Then for each point (xi, yi), find the greatest common divisor of xi and yi and divide both numbers by it:
k = GCD(xi, yi)
(xi', yi`) = (yi/k, yi/k)
Now points that are on the same ray will map to equal points. If (xi, yi) is on the same ray as (xj, yj) then (xi', yi') = (xj', yj').
Now find the largest set of equal points (don't forget any (xi, yi) = (0, 0)) and you have your answer.
You've a very original approach here !
Nevertheless, a vertical line has a infinite slope and this is the problem here: dividing by 0 is not allowed.
Alternative built on your solution (slope):
...
int mpvertical=0; // a separate couner for verticals
if (xi-x0)
mp[(yi-y0)/(xi-x0))]++;
else if (yi-y0)
mpvertical++;
// else the point (xi,yi) is the point (x0,y0): it shall not be counted)
This is cumbersome, because you have everything in the map plus this extra counter. But it will be exact. A workaround could be to count such points in mp[std::numeric_limits<double>::max()], but this would be an approximation.
Remark: the case were xi==x0 AND yi==y0 corresponds to your origin point. These points have to be discarded as they belong to every line line.
Trigonomic alternative (angle):
This uses the general atan2 formula used to converting cartesian coordinates into polar coordinates, to get the angle:
if (xi!=x0 && yi!=y0) // the other case can be ignored
mp[ 2*atan((yi-y0)/((xi-x0)+sqrt(pow(xi-x0,2)+pow(yi-y0,2)))) ]++;
so your key for mp will be an angle between -pi and +pi. No more extra case, but slightly more calculations.
You can hide these extra details and use the slighltly more optimized build in function:
if (xi!=x0 && yi!=y0) // the other case can be ignored
mp[ atan2(yi-y0, xi-x0) ]++;
you can give this approach a try
struct vec2
{
vec2(float a,float b):x(a),y(b){}
float x,y;
};
bool isColinear(vec2 a, vec2 b, vec2 c)
{
return fabs((a.y-b.y)*(a.x-c.x) - (a.y-c.y)*(a.x-b.x)) <= 0.000001 ;
}

Angle between two edges of a graph

Im trying to calculate the angle between two edges in a graph, in order to do that I transfer both edges to origin and then used dot product to calculate the angle. my problem is that for some edges like e1 and e2 the output of angle(e1,e2) is -1.#INDOO.
what is this output? is it an error?
Here is my code:
double angle(Edge e1, Edge e2){
Edge t1 = e1, t2 = e2;
Point tail1 = t1.getTail(), head1 = t1.getHead();
Point u(head1.getX() - tail1.getX(), head1.getY() - tail1.getY());
Point tail2 = t2.getTail(), head2 = t2.getHead();
Point v(head2.getX() - tail2.getX(), head2.getY() - tail2.getY());
double dotProduct = u.getX()*v.getX() + u.getY()*v.getY();
double cosAlpha = dotProduct / (e1.getLength()*e2.getLength());
return acos(cosAlpha);
}
Edge is a class that holds two Points, and Point is a class that holds two double numbers as x and y.
Im using angle(e1,e2) to calculate the orthogonal projection length of a vector like b on to a vector like a :
double orthogonalProjectionLength(Edge b, Edge a){
return (b.getLength()*sin(angle(b, a) * (PI / 180)));
}
and this function also sometimes gives me -1.#INDOO. you can see the implementation of Point and Edge here.
My input is a set S of n Points in 2D space. Iv constructed all edges between p and q (p,q are in S) and then tried to calculate the angle like this:
for (int i = 0; i < E.size(); i++)
for (int j = 0; j < E.size(); j++){
if (i == j)
cerr << fixed << angle(E[i], E[j]) << endl; //E : set of all edges
}
If the problem comes from cos() and sin() functions, how can I fix it? is here other libraries that calculate sin and cos in more efficient way?
look at this example.
the inputs in this example are two distinct points(like p and q), and there are two Edges between them (pq and qp). shouldnt the angle(pq , qp) always be 180 ? and angle(pq,pq) and angle(qp,qp) should be 0. my programm shows two different kinds of behavior, sometimes angle(qp,qp) == angle(pq,pq) ==0 and angle(pq , qp) == angle(pq , qp) == 180.0, and sometimes the answer is -1.#INDOO for all four edges.
Here is a code example.
run it for several times and you will see the error.
You want the projection and you go via all this trig? You just need to dot b with the unit vector in the direction of a. So the final answer is
(Xa.Xb + Ya.Yb) / square_root(Xa^2 + Ya^2)
Did you check that cosAlpha doesn't reach 1.000000000000000000000001? That would explain the results, and provide another reason not to go all around the houses like this.
It seems like dividing by zero. Make sure that your vectors always have 0< length.
Answer moved from mine comment
check if your dot product is in <-1,+1> range ...
due to float rounding it can be for example 1.000002045 which will cause acos to fail.
so add two ifs and clamp to this range.
or use faster way: acos(0.99999*dot)
but that lowers the precision for all angles
and also if 0.9999 constant is too big then the error is still present
A recommended way to compute angles is by means of the atan2 function, taking two arguments. It returns the angle on four quadrants.
You can use it in two ways:
compute the angles of u and v separately and subtract: atan2(Vy, Vx) - atan2(Uy, Ux).
compute the cross- and dot-products: atan2(Ux.Vy - Uy.Vx, Ux.Uy + Vx.Vy).
The only case of failure is (0, 0).

Improve minimum distance filter for pointset

I create a minimum distance filter for points.
The function takes a stream of points (x1,y1,x2,y2...) and removes the corresponding ones.
void minDistanceFilter(vector<float> &points, float distance = 0.0)
{
float p0x, p0y;
float dx, dy, dsq;
float mdsq = distance*distance; // minimum distance square
unsigned i, j, n = points.size();
for(i=0; i<n; ++i)
{
p0x = points[i];
p0y = points[i+1];
for(j=0; j<n; j+=2)
{
//if (i == j) continue; // discard itself (seems like it slows down the algorithm)
dx = p0x - points[j]; // delta x (p0x - p1x)
dy = p0y - points[j+1]; // delta y (p0y - p1y)
dsq = dx*dx + dy*dy; // distance square
if (dsq < mdsq)
{
auto del = points.begin() + j;
points.erase(del,del+3);
n = points.size(); // update n
j -= 2; // decrement j
}
}
}
}
The only problem that is very slow, due to it tests all points against all points (n^2).
How could it be improved?
kd-trees or range trees could be used for your problem. However, if you want to code from scratch and want something simpler, then you can use a hash table structure. For each point (a,b), hash using the key (round(a/d),round(b/d)) and store all the points that have the same key in a list. Then, for each key (m,n) in your hash table, compare all points in the list to the list of points that have key (m',n') for all 9 choices of (m',n') where m' = m + (-1 or 0 or 1) and n' = n + (-1 or 0 or 1). These are the only points that can be within distance d of your points that have key (m,n). The downside compared to a kd-tree or range tree is that for a given point, you are effectively searching within a square of side length 3*d for points that might have distance d or less, instead of searching within a square of side length 2*d which is what you would get if you used a kd-tree or range tree. But if you are coding from scratch, this is easier to code; also kd-trees and range trees are kinda overkill if you only have one universal distance d that you care about for all points.
Look up range tree, e.g. en.wikipedia.org/wiki/Range_tree . You can use this structure to store 2-dimensional points and very quickly find all the points that lie inside a query rectangle. Since you want to find points within a certain distance d of a point (a,b), your query rectangle will need to be [a-d,a+d]x[b-d,b+d] and then you test any points found inside the rectangle to make sure they are actually within distance d of (a,b). Range tree can be built in O(n log n) time and space, and range queries take O(log n + k) time where k is the number of points found in the rectangle. Seems optimal for your problem.