Consier I have some buckets (vector<float>). I need to access this buckets based on some indexes. Example:
int indexX, indexY, indexZ, indexW;
So when a new point arrives I need to put the point in the correct buckets. Now I am doing something like this:
// X Y Z W => Bucket
unordered_map<int, unordered_map<int, unordered_map<int, unordered_map<int, vector<float>>>>> indexedTable;
// New point arrives and I put it in the right bucket:
indexedTable[indexX][indexY][indexZ][indexW].push_back(myValue);
But I find this very ugly, and sadly it's also very slow. For example accessing it for 1700 points, it takes 0.56 secs, that's too slow.
Are there any better/faster alternatives, without using Boost ?
Note that this data structure in my needs is comparable to a Sparse Matrix (multidimensional), because very few "buckets" will be filled with something.
Instead of using this 4-deep monstrosity, you could just use an unordered_map with a struct containing the 4 indices as key, and vector<float> as the value type. Provide an equality comparator and hash function for the struct and you're in business.
struct indices
{
int indexX, indexY, indexZ, indexW;
bool operator==(indices const& other) const
{
return std::tie(indexX, indexY, indexZ, indexW) ==
std::tie(other.indexX, other.indexY, other.indexZ, other.indexW);
}
};
struct indices_hash
{
std::size_t operator()(indices const& i) const
{
std::size_t seed = 0;
boost::hash_combine(seed, i.indexX);
boost::hash_combine(seed, i.indexY);
boost::hash_combine(seed, i.indexZ);
boost::hash_combine(seed, i.indexW);
return seed;
}
};
std::unordered_map<indices, std::vector<float>, indices_hash> m;
Since you don't want to use Boost, either come up with your own hash_combine alternative or copy the implementation from here.
Live example
As long as you don't need to access all of the points within a dimension (ie, all buckets where y==2), then you should consider using a single structure to represent your point, rather than nesting maps.
Something along these lines:
struct Point_t
{
int x;
int y;
int z;
int w;
Point_t (int _x, int _y, int _z, int _w) : x (_x), y (_y), z (_z), w (_w) {}
};
// Make sure the points are sortable
struct CmpPoint_t
{
bool operator() (const Point_t& lhs, const Point_t& rhs)
{
return (lhs.x < rhs.x &&
lhs.y < rhs.y &&
lhs.z < rhs.z &&
lhs.w < rhs.w);
}
};
typedef std::vector<float> Bucket_t;
typedef std::map<Point_t, Bucket_t, CmpPoint_t> BucketMap_t;
Applying your example use case:
BucketMap_t indexedTable;
indexedTable[Point_t(indexX, indexY, indexZ, indexW)].push_back (myValue);
Related
I have a class ShapeDisplay that stores a set of Rectangles. I would like to store them sorted, therefore I use a std::set. My intention is to provide a custom comparator, which compares the origin (x, y) of the rectangle to a reference point (x, y) in the display.
However, in order to achieve this, the comparator needs access to m_reference. How do I use a custom comparator, that needs access to the class members? Is my design flawed? I know there are newer ways to provide the comparator as in this link, but that doesn't solve my access issue.
Alternatively, I could just have a std::vector that I keep sorted, such that each new Rectangle is inserted in the right position. But since std::set::insert() should do that automatically with a custom comparator, I would prefer that.
Thank you.
struct Point
{
int x;
int y;
};
struct Rectangle
{
int x;
int y;
int width;
int height;
};
class ShapeDisplay
{
void insertShape(Rectangle rect)
{
m_shapes.insert(rect);
}
void setReference(Point reference)
{
m_reference = reference;
}
private:
struct CenterComparator
{
bool operator() (const Rectangle & a, const Rectangle & b) const
{
double distA = std::sqrt(std::pow(a.x - m_reference.x, 2)
+ std::pow(a.y - m_reference.y, 2));
double distB = std::sqrt(std::pow(b.x - m_reference.x, 2)
+ std::pow(b.y - m_reference.y, 2));
return distA < distB;
}
};
std::set<Rectangle, CenterComparator> m_shapes;
Point m_reference;
};
CenterComparator isn't related to ShapeDisplay, it isn't aware of its members and it isn't derived from ShapeDisplay. You need to provide CenterComparator with its own reference Point. You then need to provide an instance of CenterComparator whose reference point is set.
Note that if you change that comparator's reference point in any way you will break std::set's sorting resulting in Undefined Behavior if you try to use it. So whenever setReference is called, you need to create a new set with a new comparator and copy over the old set.
Here is your code, adapted with these changes. I assumed you meant setReference and insertShape to be part of the public interface.
#include <cmath>
#include <set>
struct Point
{
int x;
int y;
};
struct Rectangle
{
int x;
int y;
int width;
int height;
};
class ShapeDisplay
{
public:
void insertShape(Rectangle rect)
{
m_shapes.insert(rect);
}
void setReference(Point reference)
{
m_reference = reference;
// Create a comparator using this new reference
auto comparator = CenterComparator{};
comparator.reference = m_reference;
// Create a new set
auto new_shapes = std::set<Rectangle, CenterComparator>(
std::begin(m_shapes), std::end(m_shapes), // Copy these shapes
comparator); // Use this comparator
m_shapes = std::move(new_shapes);
}
private:
struct CenterComparator
{
bool operator() (const Rectangle & a, const Rectangle & b) const
{
double distA = std::sqrt(std::pow(a.x - reference.x, 2)
+ std::pow(a.y - reference.y, 2));
double distB = std::sqrt(std::pow(b.x - reference.x, 2)
+ std::pow(b.y - reference.y, 2));
return distA < distB;
}
Point reference;
};
std::set<Rectangle, CenterComparator> m_shapes;
Point m_reference;
};
For example two structures like point and square as given below if it is possible to do so how may i insert new elements into it?
typedef struct _point{
int x;
int y;
}Point;
typedef struct _square{
float belowLeftX;
float belowLeftY;
}Square;
multimap <Square, Point > dictionary;
Yes, a structure as key is not different compared to a class as key. You have two options to get your code working.
Option 1:
Supply ordering to the Square type.
typedef struct _square {
float belowLeftX;
float belowLeftY;
bool operator<(struct _square const& rhs) const
{
if (belowLeftX < rhs.belowLeftX) return true;
if (rhs.belowLeftX < belowLeftX) return false;
return belowLeftY < rhs.belowLeftY;
}
Option 2:
Supply ordering of the Square type to the dictionary.
auto comparator = [](Square const& lhs, Square const& rhs)
{
if (lhs.belowLeftX < rhs.belowLeftX) return true;
if (rhs.belowLeftX < lhs.belowLeftX) return false;
return lhs.belowLeftY < rhs.belowLeftY;
};
std::multimap <Square, Point, decltype(comparator)> dictionary(comparator);
I have many points (a,b) and I stored x coordinate in a[] and y coordinate in b[]. Now I need to sort these points wrt x-coordinate or y-coordinate. I know that there is concept of pairs in C++ but is there any better way of doing this. Please give answers in C/C++.
You can store the pair of coordinates using std::pair<int, int>, or as the answer by #Gopi indicates by a struct.
A collection of either of those can be sorted by the X coordinate or by the Y coordinate by using a lambda function, a functor, or a global function.
// A vector of vertices.
std::vector<std::pair<int, int>> vertices;
// Sort the vertices by X coordinates using a lambda function to order them
std::sort(vertices.begin(), vertices.end(),
[](auto const& a, auto const& b) { return a.first < b.first; });
// Sort the vertices by Y coordinates using a lambda function to order them
std::sort(vertices.begin(), vertices.end(),
[](auto const& a, auto const& b) { return a.second < b.second; });
struct vertex
{
int x;
int y;
};
Then sort the structures accordingly.
You can use a struct as pointed out and shown in other answers. However, if you define your own struct you will need to define a comparator function to use with the sorting algorithm or overload the < operator.
The advantage of using std::pair is that you won't need to define a comparator because std::pair overloads the operator < to sort by first element first then second element.
See this answer for an example.
The best way is struct as the answer by #Gopi. For lexicographical sorting you can use std::tie (http://en.cppreference.com/w/cpp/utility/tuple/tie).
struct vertex
{
int x;
int y;
bool less_x(const struct vertex& b) const { return std::tie(x,y) < std::tie(b.x, b.y); }
bool less_y(const struct vertex& b) const { return std::tie(y,x) < std::tie(b.y, b.x); }
};
int c = x*n + y where, n>x and n>y
x=c/n
y=c%n
When you need x simply c/n will give x and for y use c%n gives y.
Note: Works for positive coordinates only
I attempted to do something like this but it does not compile:
class point
{
public:
int x;
int y;
};
int main()
{
vector<point> vp1;
vector<point> vp2;
vector<point> vp3;
map < vector<point>, int > m;
m[vp1] = 1;
m[vp2] = 2;
m[vp3] = 3;
map < vector<point>, int >::iterator it;
for (it=m.begin(); it!=m.end(); it++)
{
cout<<m[it->first]<<endl;
}
return 0;
}
You can use anything as the index type into a std::map as long as it supports an operator< (which could could define as a free-standing function -- doesn't have to be a member function, as long as you can write a < b for a and b being instances of your type of interest) with the usual semantics (antireflexive, transitive, ...). Or, you can pass a binary function with the same semantics to use in lieu of <, if that suits you better.
You can, but the type used as a key in a map needs to be comparable, either using operator<, or using a comparison function/functor you supply as the third template parameter for the map type.
Yes, you can. Vectors, like all containers, are comparable. The resulting map will sort the vectors in lexicographic order.
The problem is that point is not comparable. You have to define a sort order for points, and then this will in turn define lexicographic order over vector<point>.
class point
{
public:
int x;
int y;
};
bool operator<( point const &l, point const &r ) {
return l.x < r.x? true
: r.x < l.x? false
: l.y < r.y;
}
A simpler solution is to use std::pair instead of defining your own point.
typedef pair< int, int > point; // point::first = x, point::second = y
// pair is already comparable; order defined as in previous example
typedef vector<point> pointvec; // OK
You haven't defined a function to compare a vector<point> Maps make requiremets of keys checking of equivalence and comparison.
You have to declare the operator<. It would look like this (please keep in mind, that the three vectors in your sample code actually look the same):
bool operator<(const vector<point>& left, const vector<point>& right)
{
return left.size() < right.size();
}
I want to sort points_vec vector as shown in the pseudocode below. I want to sort this vector, by a coordinate value like x or y or z
class A{
std:vector<double*> points_vec;
void doSomething();
}
Then, in method A::doSomething, I want sort this vector:
void A::doSomething() {
std::sort(points_vec.begin(), points_vec.end(), sortPoints());
}
Can someone please show me syntax for the sortPoints() method.. Preferably I want it to be a method of class A. this post creates a struct to do this, not sure if I should create a similar struct within the class. Is there another way to handle this?
thanks
The simplest way is to provide a functor which is used by the sort algorithm to compare two values. You can write like this:
struct Compare
{
bool operator()(double* first, double* second) const
{
//Compare points here
}
};
And use like:
std::sort(p.begin(), p.end(), Compare());
EDIT for comment by OP: Yes, this sample code compiles fine:
class A
{
public:
struct c
{
bool operator()(int a, int b) const
{
return a < b;
}
};
};
int main()
{
std::vector<int> a1;
a1.push_back(2);
a1.push_back(1);
std::sort(a1.begin(), a1.end(), A::c());
return 0;
}
You have two options for sorting: either pass a function/functor to sort or define the operator< for your class. Now, your class A seems to be more of a wrapper for a set of coordinates. So, create another class for your co-ordinates.
struct Point {
double x_, y_, z_;
Point(double x, double y, double z) : x_(x), y_(y), z_(z) {}
// just an example, you can refine the following as much as you need
bool operator<(Point const& other) {
return x < other.x;
}
};
bool sortOnY(Point const& l, Point const& r) const {
return l.y < r.y;
}
class A {
std::vector<Point> pts_;
void doSomething() {
sort(pts_.begin(), pts_.end());
}
// if sorting on y is also required, you will need
// to use a custom comparator which can be either
// a functor or a function
void doSomeOtherThing() {
sort(pts_.begin(), pts_.end(), sortOnY);
}
};
First of all - what you have will break all your points - as you'll sort by single doubles not by "points consisting of 3 doubles".
The best way to do this I think is:
Store the points as some Point3D class not a couple doubles
Define the less then operator for Point3D
Just call std::sort(points_vec.begin(), points_vec.end() );
If you'd want to sort them by in different ways that's when you'd use the sort functor and create different functors with operators() for different purposes.
I don't think this thread would be complete without a mention of Boost.Bind:
struct Point3D {
double x, y;
Point3D(double x=0., double y=0.) : x(x), y(y) {
}
};
int main() {
std::vector<Point3D> points;
points.push_back(Point3D(-1., 2.));
points.push_back(Point3D( 2., -1.));
points.push_back(Point3D(-2., 0.));
using boost::bind;
std::sort(points.begin(), points.end(),
bind(&Point3D::x, _1) < bind(&Point3D::x, _2));
// points sorted by x coord
std::sort(points.begin(), points.end(),
bind(&Point3D::y, _1) < bind(&Point3D::y, _2));
// points sorted by y coord
}
What a shame std::tr1::bind does not support that. But of course, with a C++0x compiler you'll be able to do this:
std::sort(points.begin(), points.end(),
[](Point3D const & a, Point3D const & b) { return a.x < b.x; });
If you want to sort by x or y or z, those are three different functionalities. Which coordinate to sort by is extra information which doesn't really come from std::sort. You need have an object to pass it on.
struct coord_comparison {
int coord_id; // <= critical information
bool operator()( double (*l)[3], double (*r)[3] ) {
return (*l)[ coord_id ] < (*r)[ coord_id ];
}
coord_comparison( int id ) { coord_id = id; }
};
Create this struct inside your class or outside, but it needs to be a structure and not a free function, and operator() cannot be static. Call:
std::sort(points_vec.begin(), points_vec.end(), compare_points( 1 /*for y*/) );
Sorting by all 3 coords at once:
You have
std:vector<double*> points_vec;
I'm going to presume that the double* points to an array of 3 coordinates. This is cleaner:
std:vector<double(*)[3]> points_vec;
std::sort's third argument is a functor which compares two sequence objects:
bool compare_coords( double(*l)[3], double(*r)[3] ) {
Fortunately, comparing two sequences is already coded for you by std::less:
return std::less( *l, *l + ( sizeof *l/sizeof **l ), r );
(perhaps I did more work than necessary to get the size of the array)
return std::less( *l, *l + 3, r );
}
This function may be useful outside the class, so I'd make it a free function. You need to make it static if it's going to stay inside the class.
Finally, leave off the parens when passing the function to std::sort:
std::sort(points_vec.begin(), points_vec.end(), compare_points );