can not understand how pairs in c++ stl work? - c++

While reading a tutorial on Topcoder I came across a statement
Pairs are compared first-to-second element. If the first elements are not equal, the result will be based on the comparison of the first elements only; the second elements will be compared only if the first ones are equal.
I cannot understand what this statement is trying to say?

Consider pairs of std::pair<int, int>
std::pair<int, int> a = {1,1};
std::pair<int, int> b = {1,3};
std::pair<int, int> c = {3,2};
To determine a < c we can look at the first item and see that 1 < 3. We don't even need to consider the second element at this point.
But to determine a < b, both first items are 1, so we must then look at the second item to see that 1 < 3.
If you compare b < c, you will find that b has a smaller 1st element, but c has a smaller second element. Since the first element takes precedence, it b will be considered smaller.
So if you were to sort these pairs, they would be arranged
a < b < c

It means the following expression
( p1.first == p2.first ) && ( p1.second == p2.second )
If subexpression
( p1.first == p2.first )
is equal to flase then subexpression
( p1.second == p2.second )
will not be evaluated because it is already clear that the whole expression will be equal to false.
That is the comparison of pairs corresponds to evaluation of logical AND operator that according to the C++ STandard evaluates the following way
1 The && operator groups left-to-right. The operands are both
contextually converted to bool (Clause 4). The result is true if both
operands are true and false otherwise. Unlike &, && guarantees
left-to-right evaluation: the second operand is not evaluated if the
first operand is false.

Let's say you have list of names of persons which you want to sort alphabatically. Assume also that every name has first name and last name only.
How would you sort?
You just compare first names until they match exactly. When first names are same then you check for second name.

Related

How are strings compared in IF statement arguments

In the if function argument, how is the string s and t compared? Why is the condition (s > t) true?
string s = "to be";
string t = "not " + s; // t = “not to be”
string u = s + " or " + t; // u = “to be or not to be”
if (s > t) // true: “to be” > “not to be”
cout << u; // outputs “to be or not to be”
std::string operator comp
All comparisons are done via the compare() member function (which
itself is defined in terms of Traits::compare()):
Two strings are equal if both the size of lhs and rhs are equal and each character in lhs has equivalent character in rhs at the same
position.
The ordering comparisons are done lexicographically -- the comparison is performed by a function equivalent to
std::lexicographical_compare or std::lexicographical_compare_three_way
(since C++20).
So, in short it does a lexicographical compare
I.e. "to be"s > "not to be"s == true because - at first position - 't' > 'n'.
The comparison of std::string was designed to be not surprising, or at least minimally surprising. If you stick to lowercase letters and spaces, as in your example, operator< and operator> follow alphabetical ordering.
not to be
to be
to be or not to be
Since you are sticking to the simple case, string{"to be"} > string{"not to be"} because they are in reverse alphabetical order. That is, 't' > 'n' (as characters).
When you expand into other characters, there might be some surprises. For example, 'Z' < 'a' since ASCII puts capital letters before lowercase letters. Still, the principle still holds: the ordering of std::string is based on the ordering of the underlying character set. Look for the first character position where the strings differ; the strings are ordered the same as the characters in that position. If one (and only one) string ran out of characters before a difference was found, then the shorter string comes before the longer one.

Why is this std::sort comparison failing?

I have a vector of vectors of unsigned ints. Each element of the parent vector is a vector of three unsigned ints. I primarily want to sort the parent vector in descending order of the first element of the child vectors, but I also want to order any child vectors that have the same first element in ascending order of the third element. I initially did this with the following code:
sort(league_vector.begin(), league_vector.end());
reverse(league_vector.begin(), league_vector.end());
sort(league_vector.begin(), league_vector.end(),
[](const std::vector<unsigned int>& a, const std::vector<unsigned int>& b) {return a[0] == b[0] && a[2] < b[2];});
So just sorting, then reversing, the whole thing, which will order by the first element. Then a custom sort using a lambda function which should only return true if the third element is smaller and the first element is equal.
This seems to work fine when I have a relatively small number of elements in the parent vector (around 50 or less), but when I have more than this the final ordering is coming out pretty jumbled with no obvious pattern at all.
I've replaced this with a single custom sort:
sort(league_vector.begin(), league_vector.end(),
[](const std::vector<unsigned int>& a, const std::vector<unsigned int>& b)
{return ((a[0] > b[0]) || (a[0] == b[0] && a[2] < b[2]));});
So this returns true either when the first element is larger, or when the third element is smaller AND the first element is the same. This seems to work fine so I am just using that, but I can't work out what's wrong with the first approach. Particularly as the first approach seems to work some of the time, and the second comparison is just an extension of the first anyway.
First of all, std::sort is not a stable sort, which means it doesn't preserve the order of equivalent elements. If you want a stable sort, use std::stable_sort. Also, your custom comparison function makes no sense. Let's analyze how it behaves:
If a[0] is equal to b[0], your function returns the result of comparing a[2] and b[2]. However, if a[0] is not equal to b[0], your function always returns false. Since equivalence is defined as !(a < b) && !(b < a), according to your comparison function, any two vectors with different first elements are equal.
This function also isn't a valid comparison function because it does not satisfy a strict weak ordering. Any two vectors with different first elements are equal but two vectors with the same first element are not necessarily equal. This means that if a = {1, 2, 3}, b = {2, 3, 4}, and c = {1, 3, 4}, a == b and b == c but a != c.
Why did your first attempt fail? Let's take a concrete example and focus on this comparison, and try to explain why this is invalid by doing a simple test.
Here are the two league vectors as an example:
std::vector<std::vector<unsigned int>> league_vector = {{1,2,3,4}, {2,3,4,5}, {4,5,6,7}};
Now give this to std::sort:
std::sort(league_vector.begin(), league_vector.end(),
[](const std::vector<unsigned int>& a,
const std::vector<unsigned int>& b) {return a[0] == b[0] && a[2] < b[2];});
Concentrate on this:
a[0] == b[0]
So let's say std::sort gives your comparison the first two vectors in league_vector in this order
a={1,2,3,4} b={2,3,4,5}
Your comparison function will return false since a[0] != b[0].
Then what if the compiler does a switch up, and gives you this right afterwards, just to see if your function is not ambiguous:
a={2,3,4,5} b={1,2,3,4}
In other words, a simple switch of the values. You again return false since a[0] != b[0].
How could that make sense, where you are saying on the first test a should come after b and at the second test with just switched values, a should come after b?
The sort algorithm justifiably becomes confused, and puts values in some unorthodox order.
Note that the Visual Studio compiler does this test I described, where the comparison function is given a and b, the return value is checked, and then b and a and the return value checked. If there is an inconsistency as pointed out, the debug runtime asserts with an "invalid comparison" (or similar) condition.

How does std::set comparator function work?

Currently working on an algorithm problems using set.
set<string> mySet;
mySet.insert("(())()");
mySet.insert("()()()");
//print mySet:
(())()
()()()
Ok great, as expected.
However if I put a comp function that sorts the set by its length, I only get 1 result back.
struct size_comp
{
bool operator()(const string& a, const string& b) const{
return a.size()>b.size();
}
};
set<string, size_comp> mySet;
mySet.insert("(())()");
mySet.insert("()()()");
//print myset
(())()
Can someone explain to me why?
I tried using a multi set, but its appending duplicates.
multiset<string,size_comp> mSet;
mSet.insert("(())()");
mSet.insert("()()()");
mSet.insert("()()()");
//print mset
"(())()","()()()","()()()"
std::set stores unique values only. Two values a,b are considered equivalent if and only if
!comp(a,b) && !comp(b,a)
or in everyday language, if a is not smaller than b and b is not smaller than a. In particular, only this criterion is used to check for equality, the normal operator== is not considered at all.
So with your comparator, the set can only contain one string of length n for every n.
If you want to allow multiple values that are equivalent under your comparison, use std::multiset. This will of course also allow exact duplicates, again, under your comparator, "asdf" is just as equivalent to "aaaa" as it is to "asdf".
If that does not make sense for your problem, you need to come up with either a different comparator that induces a proper notion of equality or use another data structure.
A quick fix to get the behavior you probably want (correct me if I'm wrong) would be introducing a secondary comparison criterion like the normal operator>. That way, we sort by length first, but are still able to distinguish between different strings of the same length.
struct size_comp
{
bool operator()(const string& a, const string& b) const{
if (a.size() != b.size())
return a.size() > b.size();
return a > b;
}
};
The comparator template argument, which defaults to std::less<T>, must represent a strict weak ordering relation between values in its domain.
This kind of relation has some requirements:
it's not reflexive (x < x yields false)
it's asymmetric (x < y implies that y < x is false)
it's transitive (x < y && y < z implies x < z)
Taking this further we can define equivalence between values in term of this relation, because if !(x < y) && !(y < x) then it must hold that x == y.
In your situation you have that ∀ x, y such that x.size() == y.size(), then both comp(x,y) == false && comp(y,x) == false, so since no x or y is lesser than the other, then they must be equal.
This equivalence is used to determine if two items correspond to the same, thus ignoring second insertion in your example.
To fix this you must make sure that your comparator never returns false for both comp(x,y) and comp(y,x) if you don't want to consider x equal to y, for example by doing
auto cmp = [](const string& a, const string& b) {
if (a.size() != b.size())
return a.size() > b.size();
else
return std::less()(a, b);
}
So that for input of same length you fallback to normal lexicographic order.
This is because equality of elements is defined by the comparator. An element is considered equal to another if and only if !comp(a, b) && !comp(b, a).
Since the length of "(())()" is not greater, nor lesser than the length of "()()()", they are considered equal by your comparator. There can be only unique elements in a std::set, and an equivalent object will overwrite the existing one.
The default comparator uses operator<, which in the case of strings, performs lexicographical ordering.
I tried using a multi set, but its appending duplicates.
Multiset indeed does allow duplicates. Therefore both strings will be contained despite having the same length.
size_comp considers only the length of the strings. The default comparison operator uses lexicographic comparison, which distinguishes based on the content of the string as well as the length.

multimap with custom keys - comparison function

bool operator<(const Binding& b1, const Binding& b2)
{
if(b1.r != b2.r && b1.t1 != b2.t1)
{
if(b1.r != b2.r)
return b1.r < b2.r;
return b1.t1 < b2.t1;
}
return false;
}
I have a comparison function like above. Basically, I need to deem the objects equal if one of their attribute matches. I am using this comparison function for my multimap whose key is 'Binding' object.
The problem I face is that lower_bound and upper_bound functions return the same iterator which points to a valid object. For example (t1 = 1, r = 2) is already in the map and when I try to search it in the map with (t1 = 1, r = 2), I get a same iterator as return value of upper_bound and lower_bound functions.
Is anything wrong with the comparison function? Is there a way to figure a function where I can still ensure that the objects are equivalent even if just one of their field matches?
Shouldn't the upper_bound iterator return the object past the
The comparator for a map or multimap is expected to express a strict weak ordering relation between the set of keys. Your requirement "two objects are equivalent if just one of their fields matches" cannot be such a relation. Take these three keys:
1: r=1, t1=10
2: r=1, t1=42
3: r=2, t1=42
clearly, keys 1 and 2 are equivalent, because they have the same r. Likewise, 2 and 3 are equivalent because of the same t1. That means, that 1 and 3 have to be equivalent as well, although they have no matching fields.
As a corollary, all possible keys have to be equivalent under these circumstances, which means you dont have any ordering at all and a multimap is not the right way to go.
For your case, Boost.MultiIndex comes to mind. You could then have two separate indices for r and t1 and do your lower_bound, upper_bound and equal_range searches over both indices separately.
Your comparision function after removing redundant code can be re-written as
bool operator<(const Binding& b1, const Binding& b2)
{
if(b1.r != b2.r && b1.t1 != b2.t1)
{
//if(b1.r != b2.r) // always true
return b1.r < b2.r;
//return b1.t1 < b2.t1; // Never reached
}
return false;
}
Or by de-morgan's law
bool operator<(const Binding& b1, const Binding& b2)
{
if(b1.r == b2.r || b1.t1 == b2.t1) return false;
else return b1.r < b2.r;
}
This does not guarantee a < c if a < b and b < c
Ex: Binding(r, t): a(3, 5), b(4, 6), c(5, 5)
If your comparision function doesn't follow above crieteria, you may get strange results. (including infinite loops in some cases if library is not robust)
Your comparison function will return false if either the rs or the ts match because of the && in the if() clause. Did you mean || ? Replacing the && with || would give you a valid comparison function which compare first by the r field then the t field.
Note that std::pair already has a comparison function that does exactly that.
Your text below your code though states:
Basically, I need to deem the objects equal if one of their attribute matches
You cannot do that as it wouldn't be transitive (thus you wouldn't have strict ordering).
The inside of your if block has another if that is certain to be true, as the && clause means both sides are true.

Function object not working properly

I have defined the following function object:
struct Predicate1
{
__device__ bool operator ()
(const DereferencedIteratorTuple& lhs, const DereferencedIteratorTuple& rhs)
{
using thrust::get;
//if you do <=, returns last occurence of largest element. < returns first
if (get<0>(lhs)== get<2>(lhs) && get<0>(lhs)!= 3) return get<1>(lhs) < get<1>(rhs);
else
return true ;
}
};
where the DereferencedIteratorTuple is as follows:
typedef thrust::tuple<int, float,int> DereferencedIteratorTuple;
Moreover, i call it as follows:
result = thrust::max_element(iter_begin, iter_end, Predicate1());
But the result is the tuple (3,.99,4). I am confused why this is the result because the condition get<0>(lhs)== get<2>(lhs) does not hold in the if for this tuple. Thus, the operator returns true for every comparison of this tuple. However, thrust::max_elementis defined as follows :
"This version compares objects using a function object comp.
Specifically, this version of max_element returns the first iterator i
in [first, last) such that, for every iterator j in [first, last),
comp(*i, *j) is false."
Thus, there is no way this should be chosen as for this tuple, operator never returns false. Please let me know what i am doing wrong
The predicate helps algorithm to determine which element to prefer. If predicate returns true the algorithm prefers rhs over lhs. If it return false the algorithm prefers lhs over rhs. In the case when predicate always returns true the algorithm will select last element in the array. This is true for both stl and thrust algorithms.
I guess, that your result never occured as lhs during comparison process and every time it was not filtered since rhs's second value was smaller than 0.99.
If you want to filter such values you'd better rewrite your predicate.