Most effective way to compare two pairs of integers - c++

A class contains two integers; there are two instances of this class. I want to compare them to ensure that the two instances contain the same two numbers (their orders don't matter).
I can do this:
bool operator==(const Edge &e, const Edge &f) {
return ((e.p1 == f.p1) || (e.p1 == f.p2)) && ((e.p2 == f.p1) || (e.p2 == f.p2));
}
Is this the best way there is? There will be many such comparisons so I want to make sure I make the most efficient choice. BTW, the operator will be primarily used by the std::unordered_set class - in case this information matters.

I think you have logic mixed up a bit... if I understand you correctly, given pairs (a,b) and (x,y), you want to check that (a,b) == s(x,y), for some permutation s?
bool operator==(const Edge &e, const Edge &f) {
return ((e.p1 == f.p1) && (e.p2 == f.p2)) ||
((e.p2 == f.p1) && (e.p1 == f.p2));
}
As for performance... there is nothing to optimize here. Go look somewhere else if your program is slow.

This is probably not the fastest, and it requires C++11. But it's nice and short:
bool operator==(const Edge& e, const Edge& f) {
return std::minmax(e.p1, e.p2) == std::minmax(f.p1, f.p2);
}
It also suggests an optimization (which I generally use): keep p1 and p2 in order so that minmax doesn't need to be called every time. Then you do have an optimal solution.

This will work ok for two. However, for any more, it obviously gets very ugly very fast. In fact, you'll need to do n! comparisons to check when you have n variables if you do this in the "naive" way.
An easier way is something like the following:
static constexpr Edge::number()
{
return <number_of_values>;
}
bool operator==(const Edge& e, const Edge& f)
{
constexpr size = Edge::number();
std::array<int, size> earr = {{e.p1, e.p2, ..., e.pn}};
std::array<int, size> farr = {{f.p1, f.p2, ..., f.pn}};
return std::is_permutation(earr.begin(), earr.end(), farr.begin());
}
If it is always two, you can simply write this as:
bool operator==(const Edge& e, const Edge& f)
{
std::array<int, 2> earr = {{e.p1, e.p2};
std::array<int, 2> farr = {{f.p1, f.p2}};
return std::is_permutation(earr.begin(), earr.end(), farr.begin());
}
Testing unordered equality is the same as testing if one sequence is a permutation of the other.
Edit: Which, as should be obvious to me, can be tested with by checking the sorted sequences are equal. Replace std::is_permutation with std::sort and std::equal in the above, which will be O(n log n) instead of O(n^2).

According to my understanding of your question I suggest you following solution:
Just Add on function in your class as below:
bool isExist(int point)
{
if(this.p1==point || this.p2==point)
return true;
else
return false;
}
You can apply your logic with calling this function.
Please correct me if I am wrong...

Related

Checking whether an element is in a C++ set is really slow

I'm implementing an algorithm that implies a lot of checking whether elements are in a set/list. I was using std::vector containers but time was increasing exponentially as the vector would grow.
I've decided I would try using std::set containers in order not to have to explore the entire container to know whether it contains a certain element.
I implemented the following function that checks whether an element is part of a given set:
bool in_set(set<Node> node_set){
return node_set.find(*this) != node_set.end();
}
However, that function is taking around 2s for very small sets (1-3 elements) which makes my entire algorithm unusable.
The custom class I'm using look like this:
class Node{
public:
int d;
int h_score;
int coordinates [3];
Node* parent_address;
};
The comparison operator that I implemented look like this:
bool operator<(Node other) const{
return concatenate(concatenate(this->coordinates[0], this->coordinates[1]), this->coordinates[2]) <
concatenate(concatenate(other.coordinates[0], other.coordinates[1]), other.coordinates[2]);
}
Edit: The concatenate function does not seem to take a lot of time while executing, it looks like this:
int concatenate(int i, int j) {
int result = 0;
for (int x = i; x <= j; x++) {
result = result * 10 + x;
}
return result;
}
Do you know why it is taking so much time, and more importantly, how to make it faster?
First of all, you can try to pass Set as const & and not in operator< also as const &.
bool in_set(const set<Node>& node_set){
return node_set.find(*this) != node_set.end();
}
And
bool operator<(const Node& other) const
It will use ref instead of a copy of your set and Node objects.
Do you know why it is taking so much time
concatenate(1, 100000000) takes 1.3 second on my raspberry pi, that way to do is too slow, and in fact useless
Note also that because of the possible overflows concatenate can give the same result for different nodes, this is non compatible for an operator<
how to make it faster?
you have to find something else than these calls of concatenate to implement your operator<
What is your need ? is the order in the set is important or it can be replaced by any one else ?
It is not mandatory to create a unique identifier to compare two nodes, compare them directly, for instance :
bool operator<(const Node & other) const{
if (coordinates[0] < other.coordinates[0])
return true;
if (coordinates[0] >= other.coordinates[0])
return false;
if (coordinates[1] < other.coordinates[1])
return true;
if (coordinates[1] >= other.coordinates[1])
return false;
return (coordinates[2] < other.coordinates[2]);
}
To understand that operator< works you can consider node.coordinates supports a big number having 3 times the size of an int, so I compare the higher bits, then if equals the medium bits, then if equals the lower bitsused for a set
Your operator< takes a copy of the Node. There's also no need to create strings to compare, the built-in tuple class can do that:
How about:
bool operator<(const Node& other) const {
return std::make_tuple(coordinates[0], coordinates[1], coordinates[2]) <
std::make_tuple(other.coordinates[0], other.coordinates[1], other.coordinates[2]);
}

C++ STL Binary Search (lower_bound, upper_bound)

I have implemented a binary search like this:
typedef std::vector<Cell>::iterator CellVectorIterator;
typedef struct _Point {
char x,y;
} CoordinatePoint;
typedef struct _Cell {
...
CoordinatePoint coordinates;
} Cell;
struct CellEqualityByCoordinates
{
bool
operator()(const Cell& cell1, const Cell& cell2) const
{ return cell1.coordinates.x == cell2.coordinates.x && cell1.coordinates.y == cell2.coordinates.y; }
};
CellVectorIterator FindCellByCoordinates (CellVectorIterator first, CellVectorIterator last, const Cell &val)
{
return std::upper_bound(first, last, val, CellEqualityByCoordinates());
}
But it doesn't always find a value.
What's wrong with that?
Your comparison function will not work for a binary search. It is not supposed to determine equality, it is supposed to determine an order relation. Specifically, it should return true if the first argument would definitively come before the second in a sorted range. If the arguments should be considered equal, or the second would come before the first, it should return false. Your range also needs to be sorted by this same criteria in order for the binary search to work.
An example function that might work:
bool operator()(const Cell& cell1, const Cell& cell2) const
{
if (cell1.coordinates.x < cell2.coordinates.x) return true;
if (cell2.coordinates.x < cell1.coordinates.x) return false;
return cell1.coordinates.y < cell2.coordinates.y;
}
A similar example that doubles as a lesson in short-circuit boolean evaluation would be something like:
bool operator()(const Cell& cell1, const Cell& cell2) const
{
return (cell1.coordinates.x < cell2.coordinates.x) ||
(!(cell2.coordinates.x < cell1.coordinates.x) &&
cell1.coordinates.y < cell2.coordinates.y);
}
Both exhibit a property called strict weak ordering. It is frequently required for various sorting and/or searches in standard library collections and search algorithms.
Yet another example utilizes a std::pair, which already has a proper std::less overload available that does the above, and thus makes this considerably less complicated:
bool operator()(const Cell& cell1, const Cell& cell2) const
{
return std::make_pair(cell1.coordinates.x, cell1.coordinates.y) <
std::make_pair(cell2.coordinates.x, cell2.coordinates.y);
}
A similar algorithm is available for tuples via std::tie.
Of course, all of this assumes you have an actual ordered sequence in the first place, ordered by the same comparison logic. (which we can only assume is true, as no evidence of such was posted).

C++ set with arbitrary comparator

I have the following C++ code
#include <set>
#include <string>
#include <iostream>
using namespace std;
class Pair {
public:
string lhs;
string rhs;
Pair();
Pair( string l, string r ) {
lhs=l;
rhs=r;
};
};
struct compare {
bool operator()(const Pair& a, const Pair& b) const{
if ( ( a.lhs == b.lhs && a.rhs == b.rhs ) || ( a.lhs == b.rhs && a.rhs == b.lhs ) ) {
cout << "MATCH" << endl;
}
return ( a.lhs == b.lhs && a.rhs == b.rhs ) || ( a.lhs == b.rhs && a.rhs == b.lhs );
}
};
int main () {
set<Pair, compare > s;
Pair p( string("Hello"), string("World") );
s.insert(p);
cout << s.size() << "\n";
Pair q( string("World"), string("Hello") );
s.insert(q);
cout << s.size() << "\n";
compare cmp;
cout << cmp( p, q );
return 0;
}
Invoking the compiled code gives:
1
MATCH
MATCH
2
MATCH
Somehow the set s ends up with both Pairs p, and q in spite of the fact that the comparator identifies them as identical.
Why?
Any help will be much appreciated!
UPDATE:
Many thanks for the great answers and your kind and professional help.
As you might have guessed already, I am quite a newby to C++.
Anyway, I was wondering, if Antoine's answer could be done with a lambda expression?
Something like:
std::set< …, [](){ my_comparator_code_here } > s;
????
The comparison operator for a std::set (which is an ordered container) needs to identify a strict weak ordering not any arbitrary test you wish. Normally a properly implemented operator< does the job.
If your comparison operator does not provide a strict weak ordered (as yours does not) the behavior will be undefined. There is no way to work around this requirement of the C++ standard.
Note that in certain cases where an equality comparison is needed it will have to use the operator< twice to make the comparison.
Also have you considered using std::pair<std::string, std::string> instead of rolling your own?
I've reread your question about five times now and I'm starting to wonder if what you want is a set of pairs where which string is in first and second doesn't matter as far as the comparison goes. In that case #Antoine has what appears to be the correct solution for you.
A comparator for a set, map or any algorithm such as lower_bound or sort which require an order need to implement a strict weak ordering (basically, behave like a <).
Such an ordering is required to have 3 properties:
irreflexive: not (a < a) is always true
asymmetric: a < b implies not (b < a)
transitive: a < b and b < c imply a < c
Which you will not < has.
Such an ordering defines equivalence classes, which are groups of elements that compare equal according to the ordering (that is not (a < b) and not (b < a) is verified). In a set or map, only a single element per equivalence class can be inserted whereas a multiset or multimap may hold multiple elements per equivalence class.
Now, if you look at your comparator, you will realize that you have implemented == which does not define any order at all. You need to implement something akin to < instead.
A simple, but extremely efficient trick, is to use tuples which have < (and == and any other comparison operator) already implemented in a lexicographical order. Thus, std::tuple<std::string, std::string> has exactly the order you which; and even better, std::tuple<std::string const&, std::string const&> also has it, and can be constructed very easily using std::tie.
Therefore, the implementation of a straightforward comparator is as simple as:
struct comparator {
bool operator()(Pair const& left, Pair const& right) const {
return std::tie( left.a, left.b)
< std::tie(right.a, right.b);
}
};
Note: although not discussed much, it is absolutely essential that the ordering of the comparator be stable across calls. As such, it should generally only depend on the values of the elements, and nothing external or runtime-related (such as their addresses in memory)
EDIT: as noted, your comparator is slightly more complicated.
In your case, though, you also need to take into account that a and b have a symmetric role. In general, I would suggest uniquifying the representation in the constructor of the object; if not possible, you can uniquify first and compare second:
struct comparator {
bool operator()(Pair const& left, Pair const& right) const {
auto uleft = left.a < left.b ? std::tie(left.a, left.b)
: std::tie(left.b, left.a);
auto uright = right.a < right.b ? std::tie(right.a, right.b)
: std::tie(right.b, right.a);
assert(get<0>(uleft) <= get<1>(uleft) and "Incorrect uleft");
assert(get<0>(uright) <= get<1>(uright) and "Incorrect uright");
return uleft < uright;
}
}; // struct comparator
As Mark B said compare represents an ordering and not an equality, by default it is std::less. In your case, you don't want the comparison to depend on the order in your pair, but at the same time, your operator< must be satisfy a number of conditions.
All the answers here propose to change your specification and make the comparison order-dependant. But if you don't want that, here is the solution:
bool operator()(const Pair & a, const Pair & b) {
const bool swapA = a.lhs < a.rhs;
const std::string & al = swapA ? a.lhs : a.rhs;
const std::string & ar = swapA ? a.rhs : a.lhs;
const bool swapB = b.lhs < b.rhs;
const std::string & bl = swapB ? b.lhs : b.rhs;
const std::string & br = swapB ? b.rhs : b.lhs;
return al < bl || (al == bl && ar < br);
}
At least, it works on your example, and the relation is reflexive and transitive.
Here is how it works: it is the lexicographic order for pairs: al < bl || (al == bl && ar < br), applied to sorted pairs.
In fact your data structure is a (set of size N) of (set of size 2). Internally, std::set sorts its elements using your comparison operators. For your "set of size 2" Pair you also need to consider them as internally sorted.
If the comparison code looks too heavy, you could move the pair sorting into the Pair class, like implement two methods min() and max(). Also, you implement operator< and then don't need a compare class:
struct Pair {
string lhs, rhs;
Pair();
Pair( string l, string r ) : lhs(l), rhs(r) {}
const std::string & min() const { return lhs < rhs ? lhs : rhs; }
const std::string & max() const { return lhs < rhs ? rhs : lhs; }
bool operator<(const Pair& b) const {
return min() < b.min() || (min() == b.min() && max() < b.max());
}
};
from here
The set object uses this expression to determine both the order the elements follow in the container and whether two element keys are equivalent (by comparing them reflexively: they are equivalent if !comp(a,b) && !comp(b,a)). No two elements in a set container can be equivalent.
Sorry all jumped the gun becuase I disliked another answer. I will exapand and correct momentarily. AS pointed out, an order needs to be implemented. typcially this would be a lexicographical order. Importantly however you still need to make sure that the case for which you consider two pairs to be equal returns false for both cases.
if (( a.lhs == b.lhs && a.rhs == b.rhs ) || ( a.lhs == b.rhs && a.rhs == b.lhs )) return false;
//ordinary lexicographical compare
if( a.lhs < b.lhs) return true;
else if( a.lhs == b.lhs && a.rhs < b.rhs) return true;
else return false;
Notic the "!", simple. Your code is saying pair one is less than pair two which is less than pair one. You want it to say that neither is less than the other.
DISCLAIMER STILL WRONG ON A TECHNICALITY, ANTOINE'S IS THE CORRECT ONE

C++ std::set Find function overloading == operator

I am using sets. I use a custom struct as the key. I am inserting a value and trying to find the inserted value. But it never seems to find the element.
I have overridden both the == operator and the < operator.
Here is the code of the structure:
struct distance_t
{
public:
int id;
double distance;
bool operator<(const distance_t& rhs) const
{
if(distance < rhs.distance)
return true;
else
return false;
}
bool operator==( const distance_t& rhs)
{
if(id == rhs.id)
return true;
else
return false;
}
};
And this is the code of main
int main()
{
set<distance_t> currentSet;
distance_t insertDistance;
insertDistance.id =1;
insertDistance.distance = 0.5;
currentSet.insert(insertDistance);
distance_t findDistance;
findDistance.id = 1;
assert(currentSet.find(findDistance) != currentSet.end());
}
It always fails in the assert statement. What am I doing wrong?
Edit -Ok now I understand that it does not use the == operator at all. Here is what I want. I need the data structure to be ordered by distance. But I should be able to remove it using the id. Is there any clean way or already existing datastructure to do this?
It fails because your less-than comparison uses distance_t::distance, which you are not setting in findDistance:
distance_t findDistance;
findDistance.id = 1;
std::set does not use operator== for anything. It only uses operator<. So you would have to change it's logic to use distance_t::id.
If you want to search by id without changing the set's ordering, you can use std::find:
set<distance_t>::iterator it = std::find(currentSet.begin(),
currentSet.end(),
findDistance);
This will use your operator==. Bear in mind that this has linear time complexity.
Because operator== is not invoked at all. Comparing elements is like:
!(a < b) && !(b < a)
In other words, it uses operator<.
As you haven't assigned a value to findDistance.distance the result of the less then comparison is undefined.
Note that your definitions of the equality and less then comparison operators is dangerous, because it is easy to define instances of distance_t where their result is inconsistent. One example is two instances with the same distance but different id's.

std::map unique std::less<> function for a 2D point as key

Well, after four hours of debugging, confused as I could be, I found out the cause of the problem...
I am making some program that saves some point in a std::map and render those in my window.
But weirdly, some points failed to make it into the map.
std::map<Point2, Prop*> m_Props_m;
void AddProp(std::pair<Point2, Prop*> p)
{
m_Props_m.insert(p);
}
struct Point2
{
unsigned int Point2::x;
unsigned int Point2::y;
//--------
Point2::Point2()
:x(0)
,y(0)
{}
bool Point2::operator< (const Point2& b) const
{
return ( x+y < b.x+b.y );
}
bool Point2::operator> (const Point2& b) const
{
return ( x+y > b.x+b.y );
}
};
Thank god I have some experience with binary trees so I could find out the cause of my problem.
Imagine we have 2 Point2's.
Point2 a(0,1);
Point2 b(1,0);
As you can see, with the operator< method I have written it would return false, and the operator> would also return false. Thus if a is already in the map, and b gets inserted, the insertion fails.
Now, this is all good and well, but how can I fix this? Is there any way I could have a less than operator for a 2D point that would allow me to store every unique point in the map?
std::map doesn't use operator> at all, so you don't have to worry about that.
To sort on multiple fields (in this case, two), use a so-called "lexicographical ordering", meaning that the first field is most important, and the second breaks ties:
bool operator<(const Point2 &lhs, const Point2 &rhs) {
return (lhs.x < rhs.x) || ((lhs.x == rhs.x) && (lhs.y < rhs.y));
}
Your comparison function considers points equivalent if the sum of their coordinates is equal. For example, (2, 5) is equivalent to (3, 4), because 2 + 5 = 3 + 4. Points that already have their equivalent in the map won't make it.
A better idea would be to compare by x first, and by y second if x value is equal in both points.
bool operator< (const Point2 &lhs, const Point2 &rhs) {
return (lhs.x < rhs.x) || ((lhs.x == rhs.x) && (lhs.y < rhs.y));
}
This should do the trick:
bool Point2::operator< (const Point2& b) const
{
if (x<b.x) return true;
else if (!(b.x<x) && y<b.y) return true;
else return false;
}
std::map only uses operator<. If !(a<b) && !(b<a), a and b are equivalent (not the same as equal), and thus the map will only store one of them. operator> is not used.
It might not make sense for comparing Point2's in other cases, so to avoid misunderstandings, I'd suggest that you provide a compare-function for your map, and remove the operator< from your class.
bool mapLessPoint2(const Point2& a, const Point2& b);
std::map<Point2, Prop*, &mapLessPoint2> m_Props_m;
In my opinion operator overloading should only be used when it makes sense for the given type, and that one point in a 2D space is larger than another point in a 2D space is not intuitive.