is there a way to search a part of an item in a set? I have a set of pairs std::set< std::pair<double, unsigned> > and want to search for an item via a given double. Is there any way I can do this convenient instead of manually creating a pair and searching for it?
Given that your set uses the standard comparison operator to define the ordering within the set, and given that the double element comes first in the pair, the sorting order inside the set is defined primarily by the double elements of the pair (and only for pairs that share the double element will the second element be taken into account for the ordering).
Therefore, the only thing you need to do is to define a comparison operator that compares pairs with single doubles in both directions (note I use C++11 syntax in several places):
using std::pair;
using std::set;
typedef pair<double,unsigned> pair_t;
typedef set<pair_t> set_t;
typedef set_t::iterator it_t;
struct doublecmp
{
/* Compare pair with double. */
bool operator()(const pair_t &p, double d)
{ return (p.first < d); }
/* Compare double with pair. */
bool operator()(double d, const pair_t &p)
{ return (d < p.first); }
};
And with this in place, you can use the std::equal_range algorithm to find the range of all pairs in the set that have a given double d as first element:
std::equal_range(begin(s),end(s),d,doublecmp());
If this is compiled with optimization, the instantiation of doublecmp() is a no-op.
You'll find a fully working code example here.
Why does this work?
Given that your set is declared as set<pair<double,unsigned>>, you are using the default comparison operator less<pair<double,unsigned>>, which is the same as the standard operator< for pair. That is defined as the lexicographic ordering (20.3.3/2 in C++11, or 20.2.2/2 in C++03), therefore the first element of each pair is the primary sorting criterion.
Caveat 1 The solution will generally not work if you declare your set to use a different comparison operator than the default one. It also won't work if the part of the pair you use as searching criterion is the second, rather than the first element.
Caveat 2 The data type used in the search criterion is a floating point type. Equality checks (including the operator<-based indirect equality checking performed by std::equal_range) for floating point numbers are generally difficult. The double you are searching for may have been computed in a way that suggests it should be mathematically identical to certain values in the set, but std::equality_range might not find them (nor would std::find_if suggested in other answers). For equality checks it is generally a good idea to allow for a small ("up to some epsilon") difference between the value you are looking for and the values you consider as matches. You can accomplish this by replacing std::equal_range with explicit calls to std::lower_bound and std::upper_bound and taking into account a parameter epsilon:
pair<it_t,it_t> find_range(set_t &s, double d, double epsilon)
{
return {std::lower_bound(begin(s),end(s),d - epsilon,doublecmp()),
std::upper_bound(begin(s),end(s),d + epsilon,doublecmp())};
}
This leaves the question how to determine the right value for epsilon. This is generally difficult. It is usually computed as an integer multiple of std::numeric_limits<double>::epsilon, but choosing the right factor can be tricky. You'll find more information about this in How dangerous is it to compare floating point values.
Since the set isn't ordered according to your search criteria, you could use std::find_if with a predicate that checks only the pair's first element. This will return an iterator to the first matching element, with the usual caveats about comparing floating point numbers for equality.
double value = 42.;
auto it = std::find_if(the_set.begin(), the_set.end(),
[&value](const std::pair<double, unsigned>& p) { return p.first==value; });
I'm not sure if this is what you're looking for or not:
#include <iostream>
#include <set>
#include <utility>
#include <algorithm>
using namespace std;
struct Finder{
template<typename Value>
bool operator()(const Value& first, const Value& v) const{
return first == v;
}
};
template <typename Value>
struct FirstValueValue{
FirstValueValue(const Value& value): value(value){};
template<typename Pair>
bool operator()(const Pair& p) const{
return p.first == value;
}
Value value;
};
int main(int argc, char *argv[]) {
typedef std::set<std::pair<double,unsigned int> > SetOfPairs;
SetOfPairs myset;
myset.insert(std::make_pair(2.0,1));
myset.insert(std::make_pair(5.7,2));
Finder finder;
double v = 2.0;
for(SetOfPairs::iterator it = myset.begin(); it != myset.end(); it++){
if( finder(it->first,v) ){
cout << "found value " << v << std::endl;
}
}
FirstValueValue<double> find_double_two(2.0);
myset.insert(std::make_pair(2.0,100));
unsigned int count = std::count_if(myset.begin(),myset.end(),find_double_two);
cout << "found " << count << " occurances of " << find_double_two.value;
}
Which prints out:
found value 2
found 2 occurances of 2
I don't know what your needs are or if boost libraries are allowed but you could look into Boost Multi Index if you have to index off one part of the pair a lot.
Hope this helps. Good luck.
Related
I need to create a std::set of ranges for finding matching points in these ranges. Each range is defined as follows:
struct Range {
uint32_t start;
uint32_t end;
uint32_t pr;
};
In this structure start/end pair identify each range. pr identifies the priority of that range. It means if a single point falls into 2 different ranges, I like to return range with smaller pr. I like to create a std::set with a transparent comparator to match points like this:
struct RangeComparator {
bool operator()(const Range& l, const Range& r) const {
if (l.end < r.start)
return true;
if (l.end < r.end && l.pr >= r.pr)
return true;
return false;
}
bool operator()(const Range& l, uint32_t p) const {
if (p < l.start)
return true;
return false;
}
bool operator()(uint32_t p, const Range& r) const {
if (p < r.start)
return true;
return false;
}
using is_transparent = int;
};
std::set<Range, RangeComparator> ranges;
ranges.emplace(100,250,1);
ranges.emplace(200,350,2);
auto v1 = ranges.find(110); // <-- return range 1
auto v2 = ranges.find(210); // <-- return range 1 because pr range 1 is less
auto v3 = ranges.find(260); // <-- return range 2
I know my comparators are wrong. I wonder how I can write these 3 comparators to answer these queries correctly? Is it possible at all?
find returns an element that compares equivalent to the argument. Equivalent means that it compares neither larger nor smaller in the strict weak ordering provided to the std::set.
Therefore, to make your use case work, you want all points in a range to compare equivalent to the range.
If two ranges overlap, then the points shared by the two ranges need to compare equivalent to both ranges. The priority doesn't matter for this, since the equivalence should presumably hold if only one of the ranges is present.
However, one of the defining properties of a strict weak ordering is that the property of comparing equivalent is transitive. Therefore in this ordering the two ranges must then also compare equal in order to satisfy the requirements of std::set.
Therefore, as long as the possible ranges are not completely separated, the only valid strict weak ordering is the one that compares all ranges and points equivalent.
This is however not an order that would give you what you want.
This analysis holds for all standard library associative containers, since they have the same requirements on the ordering.
I have a vector which contains lot of elements of my class X .
I need to find the first occurrence of an element in this vector say S such that S.attrribute1 > someVariable. someVariable will not be fixed . How can I do binary_search for this ? (NOT c++11/c++14) . I can write std::binary_search with search function of greater (which ideally means check of equality) but that would be wrong ? Whats the right strategy for fast searching ?
A binary search can only be done if the vector is in sorted order according to the binary search's predicate, by definition.
So, unless all elements in your vector for which "S.attribute1 > someVariable" are located after all elements that are not, this is going to be a non-starter, right out of the gate.
If all elements in your vector are sorted in some other way, that "some other way" is the only binary search that can be implemented.
Assuming that they are, you must be using a comparator, of some sort, that specifies strict weak ordering on the attribute, in order to come up with your sorted vector in the first place:
class comparator {
public:
bool operator()(const your_class &a, const your_class &b) const
{
return a.attribute1 < b.attribute1;
}
};
The trick is that if you want to search using the attribute value alone, you need to use a comparator that can be used with std::binary_search which is defined as follows:
template< class ForwardIt, class T, class Compare >
bool binary_search( ForwardIt first, ForwardIt last,
const T& value, Compare comp );
For std::binary_search to succeed, the range [first, last) must be
at least partially ordered, i.e. it must satisfy all of the following
requirements:
for all elements, if element < value or comp(element, value) is true
then !(value < element) or !comp(value, element) is also true
So, the only requirement is that comp(value, element) and comp(element, value) needs to work. You can pass the attribute value for T, rather than the entire element in the vector to search for, as long as your comparator can deal with it:
class search_comparator {
public:
bool operator()(const your_class &a, const attribute_type &b) const
{
return a.attribute1 < b;
}
bool operator()(const attribute_type &a, const your_class &b) const
{
return a < b.attribute1;
}
};
Now, you should be able to use search_comparator instead of comparator, and do a binary search by the attribute value.
And, all bets are off, as I said, if the vector is not sorted by the given attribute. In that case, you'll need to use std::sort it explicitly, first, or come up with some custom container that keeps track of the vector elements, in the right order, separately and in addition to the main vector that holds them. Using pointers, perhaps, in which case you should be able to execute a binary search on the pointers themselves, using a similar search comparator, that looks at the pointers, instead.
For std::binary_search to succeed, the range need to be sorted.std::binary_search, std::lower_bound works on sorted containers. So every time you add a new element into your vector you need to keep it sorted.
For this purpose you can use std::lower_bound in your insertion:
class X;
class XCompare
{
public:
bool operator()(const X& first, const X& second) const
{
// your sorting logic
}
};
X value(...);
auto where = std::lower_bound(std::begin(vector), std::end(vector), value, XCompare());
vector.insert(where, value);
And again you can use std::lower_bound to search in your vector:
auto where = std::lower_bound(std::begin(vector), std::end(vector), searching_value, XCompare());
Don't forget to check if std::lower_bound was successful:
bool successed = where != std::end(vector) && !(XCompare()(value, *where));
Or directly use std::binary_search if you only want to know that element is in vector.
I have to write my own implementation of heap in C++, which stores objects of type:
std::pair<City, int>
where City is a structure to store two integers, which represent city coords and string - city name.
I do know how to do this with plain integers, but using pair of values is a little problematic to me.
I've already started to write my heap class, but, as I said, I don't know how to do this with those pairs.
I want the heap to be sorted by the int value of the pair.
If you know how to do it for ints, you're almost there. Treat the pair objects just as you would treat ints when assigning, but for comparison purposes, use .second instead of the value directly.
You could try to use std::make_heap which will put a sequence of your pairs into a heap order, see this online example. To sort by the int value only, use a C++11 lambda expression that will compare the second element of each pair
Alternatively, given that you cannot use any STL heap-related algorithms, but given any self-made implementation of
template<typename RandomIt>
void my_make_heap(RandomIt first, RandomIt last)
{
/* some algorithm using `a < b` to do comparisons */
}
you can rewrite it as (or add an overload)
template<typename RandomIt, typename Compare>
void my_make_heap(RandomIt first, RandomIt last, Compare, cmp)
{
/* SAME algorithm, but now using `cmp(a, b)` to do comparisons */
}
and then call it as my_make_heap(first, last, int_cmp) where the lambda expression compares pairs like this:
typedef std::pair<City, int> Element;
auto int_cmp = [](Element const& lhs, Element const& rhs) {
return lhs.second < rhs.second;
};
So from what i understand :
Your structure is something like this ,
struct node
{
int X_coord;
int y_coord;
string name;
}
And you need to form the Heap based on "int' value of pair ,call it 'x' .
So your pair is
pair<node n , int x> ;
This , is a very readable code for Heap , implemented in a class.
It can be easily modified to your requirement for pair<> value .
Just use , "heap.second" as your key value .
I have a vector of ordered container classes where I need to know the index of the container that has a given element
so, I would like to do the following, but this obviously doesn't work. I could create a dummy Container to house the date to find, but I was wondering if there was a nicer way.
struct FooAccDateComp
{
bool operator()(const Container& d1, const MyDate& f1) const
{ return d1->myDate < f1; }
};
class Container
{
MyDate myDate;
...
};
vector<Container> mystuff;
MyDate temp(2008, 3, 15);
//add stuff to variable mystuff
int index = int(upper_bound(events.begin(), events.end(),temp, FooAccDateComp())-events.begin());
EDIT: The container class can contain other dates.
upper_bound needs to be able to evaluate expressions like Comp(date,container), but you've only provided Comp(container,date). You'll need to provide both:
struct FooAccDateComp
{
bool operator()(const Container& c, const MyDate& d) const
{ return c.myDate < d; }
bool operator()(const MyDate& d, const Container& c) const
{ return d < c.myDate; }
};
Remember that the vector must be sorted according to this comparison for upper_bound and friends to work.
You don't necessarily need a special predicate, just enable comparison between Container and MyDate.
#include <vector>
struct MyDate {
MyDate(int, int, int);
};
struct Container {
MyDate myDate;
};
// enable comparison between Container and MyDate
bool operator<(Container const&, MyDate const&);
bool operator==(Container const&, MyDate const&);
std::vector<Container> v;
//add stuff to variable mystuff
MyDate temp(2008, 3, 15);
std::vector<Container>::iterator i = std::lower_bound(v.begin(), v.end(), temp);
ptrdiff_t index = i != v.end() && *i == temp ? i - v.begin() : -1;
You can use find_if if you don't mind degrading performance (you said that you have a vector of sorted Container, so binary search would be faster)
Or you can add
struct Container {
MyDate myDate;
operator MyDate () {return myDate};
}
bool operator <(MyDate const&, MyDate const&)
{
return // your logic here
};
Now you can use binary search functions
std::vector<Container>::iterator i = std::upper_bound(v.begin(), v.end(), MyDateObject);
Surely, it will work only if your vector is sorted by Container.myDate
Your example is broken in several trivial ways: the class Container should be defined before FooAccDateComp in order for it to be used there, you should make myDate a public member of Container, access that member in the comparison method using .myDate rather than ->myDate, and finally decide whether to call your vector mystuff or events, but not mix both. I'll suppose that appropriate corrections have been made.
You should have defined your comparison function to take a Date parameter as first argument and a Container parameter as second; the opposite to what you did. Or you could use std::lower_bound instead of std::upper_bound if that would suit you purpose (since you don't say what you are going to do with index it is hard to tell) as the choice made in the question is adapted to that. Contrary to what the currently accepted answer says you do not need both if you are only using std::upper_bound or only std::lower_bound (though you would need both if using std::equal_range, or when using both std::upper_bound and std::lower_bound).
You can find these at first sight a bit strange specifications in the standard, but there is a way to understand without looking it up why they have to be like this. When using lower_bound, you want to find the point that separates the Container entries that are (strictly) less than your given Date from those that are not, and this requires calling the comparison function with that Date argument in second position. If however you ask for an upper_bound (as you are), you want to find the point that separates the entries that are not strictly greater than your given Date from those that are, and this requires calling the comparison function with that Date argument in first position (and negating the boolean result it returns). And for equal_range you of course need both possibilities.
I have a list of objects ("Move"'s in this case) that I want to sort based on their calculated evaluation. So, I have the List, and a bunch of numbers that are "associated" with an element in the list. I now want to sort the List elements with the first element having the lowest associated number, and the last having the highest. Once the items are order I can discard the associated number. How do I do this?
This is what my code looks like (kind've):
list<Move> moves = board.getLegalMoves(board.turn);
for(i = moves.begin(); i != moves.end(); ++i)
{
//...
a = max; // <-- number associated with current Move
}
I would suggest a Schwartzian transform sort. Make a new vector (I recommend vector for more efficient sorting) of pairs of the associated value, and a pointer to its item. Sort the vector of pairs and then regenerate the list from the sorted vector. Since operator< is defined on a std::pair to be comparison by the first item of the pair and then the second, you will get a proper ordering.
Example:
#include <algorithm> // gives you std::sort
#include <utility> // gives you std::pair
typedef double CostType;
typedef std::pair<CostType, Move*> Pair;
// Create the vector of pairs
std::vector<Pair> tempVec;
tempVec.reserve(moves.size());
for (std::list<Move>::iterator i = moves.begin(); i != moves.end(); ++i)
{
CostType cost = calcCost(*i);
Move* ptrToI = &(*i);
tempVec.push_back(Pair(cost, ptrToI));
}
// Now sort 'em
std::sort(tempVec.begin(), tempVec.end());
// Regenerate your original list in sorted order by copying the original
// elements from their pointers in the Pair.
std::list<Move> sortedMoves;
for (std::vector<Pair>::iterator i = tempVec.begin(); i != tempVec.end(); ++i)
{
sortedMoves.push_back(*(i->second));
}
Note that you will need a calcCost function that I have assumed here. This approach has an advantage over creating a comparison function if your comparison value calculation is time consuming. This way, you only pay the cost for calculating the comparison N times instead of 2 * N * log(N).
You could make a comparison function that compares the two elements in the way that you would like.
bool compare_m (const Move &first,const Move &second)
{
if (first.thing_you_are_comparing_on() < second.thing_you_are_comparing_on()) return true;
else return false;
}
Where "thing_you_are_comparing_on" is some member of the Move class that gives you the ordering you want. We use const here to make sure that we are only comparing and not actually changing the objects in the comparison function. You can then call the sort method on the list with compare_m as the comparison function:
moves.sort(compare_m)
Something to note is that if the calculation of the comparison function is particularly expensive it may be worthwhile to precompute all the associated rank numbers before sorting.
This would require adding something to the move class to store the rank for use later:
class Move{
//rest of move class
public:
int rank;
};
list<Move>::iterator iter;
for(iter = moves.begin(); iter != moves.end(); ++iter)
{
//...
(*iter).rank = max; // store the number associated with current Move
}
bool compare_rank (const Move &first,const Move &second)
{
if (first.rank < second.rank) return true;
else return false;
}
std::sort is used to sort STL collections. If the elements in the collection you are sorting can be compared simply by calling operator< and the collection in question is a vector, then sorting is very simple:
std::sort(collection.begin(), collection.end());
If the collection in question is not a vector but a list as in your case, then you can't use the general version of std::sort, but you can use std::list's version instead:
list<int> numbers;
numbers.sort();
STL's sort, along with most other algorithms in the STL, come in two flavors. One is the simple version we have already seen, which just uses operator< to do the comparison of two elements. The other is a 'predicated' version, which instead of using operator< uses a comparison functor you provide. This is what you need to use in your case. There is a predicated version of sort for list, and this is what you need to use in your case.
You can create a functor in a number of ways, but one of the most useful is to derive a class from std::unary_function or from std::binary_function, depending on how many arguments your functor will take -- in your case, two. Override the function-call operator, operator() and add the code that compares two elements:
class compare_functor : public std::binary_function<Move, Move, bool>
{
public:
bool operator(const Move& lhs, const Move& rhs) const
{
int left_val = lhs.Value();
int right_val = rhs.Value();
return left_val < right_val;
};
Here is a complete working example that puts everything together. In this program, instead of having a list of Moves, I have a list of 10 strings. Each string is 6 random characters. The list is populated by the call to generate_n, which uses the functor generator to create each random string. Then I dump that list of strings, along with their values, by calling copy and passing an output iterator that dumps the values to stdout (ostream_iterator). The value of each string is simply a sum of the numeric value of each character, computed by the function strng_val.
Then I sort the list using list's predicated version of sort. The comparison predicate used by sort is evaluator. Then I finally dump the resulting list and the string values to the screen again as above:
#include <cstdlib>
#include <iostream>
#include <list>
#include <string>
#include <algorithm>
#include <ctime>
#include <sstream>
using namespace std;
class generator
{
public:
generator() { srand((unsigned)time(0)); }
string operator()() const
{
string ret;
for( int i = 0; i < 6; ++i )
ret += static_cast<char>((rand()/(RAND_MAX/26)) + 'A');
return ret;
}
};
unsigned string_val(const string& rhs)
{
unsigned val = 0;
for( string::const_iterator it = rhs.begin(); it != rhs.end(); ++it )
val += (*it)-'A'+1;
return val;
};
class evaluator : public std::binary_function<string,string,bool>
{
public:
bool operator()(const string& lhs, const string& rhs) const
{
return string_val(lhs) < string_val(rhs);
}
};
class string_dumper : public std::unary_function<string, string>
{
public:
string operator()(const string& rhs) const
{
stringstream ss;
ss << rhs << " = " << string_val(rhs);
return ss.str();
}
};
int main()
{
// fill a list with strings of 6 random characters
list<string> strings;
generate_n(back_inserter(strings), 10, generator());
// dump it to the screen
cout << "Unsorted List:\n";
transform(strings.begin(), strings.end(), ostream_iterator<string>(cout, "\n"), string_dumper());
// sort the strings according to their numeric values computed by 'evaluator'
strings.sort(evaluator()); // because this is a 'list', we are using list's 'sort'
// dump it to the screen
cout << "\n\nSorted List:\n";
transform(strings.begin(), strings.end(), ostream_iterator<string>(cout, "\n"), string_dumper());
return 0;
}