I'd like to store a bunch of range items in std::set container.
This data structure should provide fast decision whether a specific input range contained by one of the ranges that the set currently holds, by overloading the comparison of std::set in order use the set::find method to check one of the items in set contain the input range argument.
It should also support range item that represents a single point (start_range == end_range).
Here's my implementation :
#include <iostream>
#include <map>
#include <set>
using std::set;
using std::map;
class range : public std::pair<int,int>
{
public:
range(int lower, int upper)
{
if (upper < lower)
{
first = upper;
second = lower;
}
else
{
first = lower;
second = upper;
}
}
range(int val)
{
first = second = val;
}
bool operator<(range const & b) const
{
if (second < b.first)
{
return true;
}
return false;
}
};
And here's how I test my data structure:
int main(int argc, const char * argv[])
{
std::map<int, std::set<range>> n;
n[1].insert(range(-50,-40));
n[1].insert(range(40,50));
n[2].insert(range(-30,-20));
n[2].insert(range(20,30));
n[3].insert(range(-20,-10));
n[3].insert(range(10,20));
range v[] = {range(-50,-41), range(30,45), range(-45,-45), range(25,25)};
int j[] = {1,2,3};
for (int l : j)
{
for (range i : v)
{
if (n[l].find(i) != n[l].end())
{
std::cout << l << "," << i.first << "," << i.second << " : "
<< n[l].find(range(i))->first << " "
<< n[l].find(range(i))->second << std::endl;
}
}
}
}
and here are the results I get:
1,-50,-41 : -50 -40 --> good
1,30,45 : 40 50 --> bad
1,-45,-45 : -50 -40 --> good
2,30,45 : 20 30 --> bad
2,25,25 : 20 30 --> good
So as you can see, my code does support perfectly well single point range (-45 is contained by range (-50,-40) and 25 is contained by by range (20,30))
However, as for wider ranges, my current operator < is capable of finding the contained relationship which is equal for the set terminology (meaning that for ranges a and b a<b && a<b.
Is there anyway to change this operator to make it work ?
Sounds like a perfect match for using Boost Interval Container Library. In short, you can
#include <boost/icl/interval_set.hpp>
// Helper function template to reduce explicit typing:
template <class T>
auto closed(T&& lower, T&& upper)
{
return boost::icl::discrete_interval<T>::closed(std::forward<T>(lower),
std::forward<T>(upper));
}
boost::icl::interval_set<int> ranges;
ranges.insert(closed(1, 2));
ranges.insert(closed(42, 50));
std::cout << contains(ranges, closed(43, 46)) << "\n"; // true
std::cout << contains(ranges, closed(42, 54)) << "\n"; // false
This should easily be pluggable into your std::map and be usable without further adjustments.
Your operator < defines partial order:
(30,45) < (40, 50) == false and simultaneously (40, 50) < (30, 45) == false so in terms of std::set and std::map they are equal. That is why you got these results.
There is a paper about partial order: https://en.wikipedia.org/wiki/Partially_ordered_set
You might want use std::unordered_map or define somehow total order for your ranges.
I suggest operator < that compares the arithmetical mean of range bounds, i.e.
(a, b) < (c, d) if and only if (a+b)/2 < (c+d)/2 for total order. Note that you might want use float for arithmetical mean.
For testing I suggest the following code draft (I write here from scratch and didn't tested it). -1 meanst that are no range that contains this
int range::firstContainsMe(const std::vector<range> rangesVec)
{
for (size_t i = 0; i < rangesVec; i++) {
if (lower >= rangesVec[i].lower && upper <= rangesVec[i].upper) {
return i;
}
}
return -1;
}
Your comparison operator is unsuitable.
If you wish to use any container or algorithm based on ordering in C++, the ordering relation needs to be a Strict Weak Ordering Relation. The definition can be found on Wikipedia, in short the following rules must be respected:
Irreflexivity: For all x in S, it is not the case that x < x.
Asymmetry: For all x, y in S, if x < y then it is not the case that y < x.
Transitivity: For all x, y, z in S, if x < y and y < z then x < z.
Transitivity of Incomparability: For all x, y, z in S, if x is incomparable with y (neither x < y nor y < x hold), and y is incomparable with z, then x is incomparable with z.
Your comparison operator fails, and therefore is unsuitable. In general, a quick way of obtaining a good comparison operator is to do what tuples do:
bool operator<(range const & b) const
{
return std::tie(first, second) < std::tie(b.first, b.second);
}
You want a map, not a set.
In order to solve your problem, you want a map, not a set.
For disjoint intervals, a map from lower-bound to upper-bound is sufficient:
std::map<int, int> intervals;
The .lower_bound and .upper_bound operations allow finding the closest key in O(log N) time, and from there containment is quickly asserted.
For non-disjoint intervals, things get trickier I fear, and you'll want to start looking into specialized data-structures (Interval Trees for example).
Related
I have an array of n double arrays of size 2:
double **stored_points_;
I need to write a function that sorts these coordinates in ascending order based on a given axis (x or y) and store these sorted coordinates in a new 2d array. I also need a function that calculates a bounding box for the coordinates and stores in the two given output parameters.
I have already successfully written copy constructor, getter, setter, etc. I have tried doing a kind of bubble sort but cannot figure out how to make it work with a 2d array.
What I expect is
if coordinates are (1,5), (2,2), (1,1), (1,3)
result when axis = 0: (1,1), (1,3), (1,5), (2,2)
result when axis = 1: (1,1), (2,2), (1,3), (1,5)
//function definitions from class Points2D{}:
void SortByAxis(size_t axis, double** sorted_points) const;
//axis: 0 means sort by x-axis, 1 means sort by y-axis
void CalcBoundingBox(double lower_left[2], double upper_right[2]) const;
//some members of class Points2D{}:
public:
static const size_t x = 0;
static const size_t y = 0;
private: 0;
double **stored_points_;
As already pointed out by immibis:
Notice that sorting your 2D array, is the same as sorting a normal 1D array where the items you're sorting happen to be arrays.
I would like to add that OP is hopefully aware that a 2D array (array of arrays) is not what was exposed by OP.
double **stored_points is a pointer to double* and may represent an array of double*. This is not a compatible type to e.g. double points[][2]. (There are numerous Q/As in SO concerning this:
SO: Why can't we use double pointer to represent two dimensional arrays?
is actually tagged with c but applies to c++ as well.)
The standard library already provides a ready std::sort() to sort a variety of containers (including arrays) which can be used in most common cases – the one of OP inclusive:
Sorts the elements in the range [first, last) in ascending order. The order of equal elements is not guaranteed to be preserved.
The granted complexity of std::sort() is O(N·log(N)) → much better than the complexity of Bubble sort (OP considered to use) which is O(N²).
There are multiple flavors available. For OPs case, a custom comparator is needed as the meaning of ascending shall be changable on request.
Hence,
template< class RandomIt, class Compare >
void sort( RandomIt first, RandomIt last, Compare comp )
is chosen.
Parameters
first, last - the range of elements to sort
comp - comparison function object (i.e. an object that satisfies the requirements of Compare) which returns true if the first argument is less than (i.e. is ordered before) the second.
The signature of the comparison function should be equivalent to the following:
bool cmp(const Type1 &a, const Type2 &b);
While the signature does not need to have const &, the function must not modify the objects passed to it and must be able to accept all values of type (possibly const) Type1 and Type2 regardless of value category (thus, Type1 & is not allowed, nor is Type1 unless for Type1 a move is equivalent to a copy (since C++11)).
The types Type1 and Type2 must be such that an object of type RandomIt can be dereferenced and then implicitly converted to both of them.
For double **stored_points, in first stored_points may be passed, in last stored_points + n. Thereby, n is the size of the array. It's not mentioned in OPs exposed code but it's an absolutely necessary value. A pointer can represent an array of any length. I know only two ways to get the length of an array from a pointer: either provide it separately or use a specific value as end marker (like done in C strings with '\0').
For the comparator, a function (or functor) with matching signature has to be passed.
In this specific case, it is
bool(double* const &, double* const &)
but (even better)
bool(double*, double*)
will do as well.
This could be a function, a functor (i.e. a class with operator()), or a lambda (which resembles one of the former). I decided to use a lambda (to keep my code minimal):
[](double *pt1, double *pt2) {
return pt1[0] != pt2[0] // if first elements unequal
? pt1[0] < pt2[0] // return whether first first < second first
: pt1[1] < pt2[1]; // else whether first second < second second
}
This provides a less operator comparing first sub-element, considering second sub-element only if first is equal. This less comparator defines an Order which is needed in std::sort() to define the meaning of ascending.
To change the order (for sorting with leading y coordinates), just another lambda is used:
[](double *pt1, double *pt2) {
return pt1[1] != pt2[1] // if second elements unequal
? pt1[1] < pt2[1] // return whether first second < second second
: pt1[0] < pt2[0]; // else whether first first < second first
Looks actually quite similar – just the indices have been swapped.
The complete example:
#include <algorithm>
#include <iostream>
#include <sstream>
#include <string>
// a print function (usable in output streams)
std::string print(double **data, size_t n)
{
std::ostringstream out;
const char *sep = "";
for (size_t i = 0; i < n; ++i) {
out << sep << '(' << data[i][0] << ", " << data[i][1] << ')';
sep = ", ";
}
return out.str();
}
int main()
{
// sample data of OP
double points[][2] = {
{ 1, 5 }, { 2, 2 }, { 1, 1 }, { 1, 3 }
};
const size_t n = sizeof points / sizeof *points; // let compiler determine
// resemble input data of OP
double *stored_points[n];
for (size_t i = 0; i < n; ++i) stored_points[i] = points[i];
// show input data
std::cout
<< "Input data:\n"
<< " " << print(stored_points, n) << '\n';
// sort in ascending order with leading x:
std::sort(stored_points, stored_points + n,
[](double *pt1, double *pt2) {
return pt1[0] != pt2[0] // if first elements unequal
? pt1[0] < pt2[0] // return whether first first < second first
: pt1[1] < pt2[1]; // else whether first second < second second
});
// show result
std::cout
<< "Data sorted by leading x:\n"
<< " " << print(stored_points, n) << '\n';
// sort in ascending order with leading y:
std::sort(stored_points, stored_points + n,
[](double *pt1, double *pt2) {
return pt1[1] != pt2[1] // if second elements unequal
? pt1[1] < pt2[1] // return whether first second < second second
: pt1[0] < pt2[0]; // else whether first first < second first
});
// show result
std::cout
<< "Data sorted by leading y:\n"
<< " " << print(stored_points, n) << '\n';
// done
return 0;
}
Output:
Input data:
(1, 5), (2, 2), (1, 1), (1, 3)
Data sorted by leading x:
(1, 1), (1, 3), (1, 5), (2, 2)
Data sorted by leading y:
(1, 1), (2, 2), (1, 3), (1, 5)
Live Demo on coliru
I have this following program for map with custom keys:
class MyClass
{
public:
MyClass(int i): val(i) {}
bool operator< (const MyClass& that) const { return val <= that.val; }
private:
int val;
};
int main()
{
MyClass c1(1);
MyClass c2(2);
MyClass c3(3);
map<MyClass, int> table;
table[c1] = 12;
table[c2] = 22;
table[c3] = 33;
cout << "Mapped values are: " << table.lower_bound(c1)->second << " " << table[c2] << " " << table[c3] << endl;
}
The output comes as:
Mapped values are: 22 0 0
But if I compare using < or > in the operator< instead of <= then everything works fine. And the output comes as:
Mapped values are: 12 22 33
Can someone explain why <= does not work at all, but < and even > works?
The comparison function used by std::map must implement a strict weak ordering. That means it must implement the following rules given objects x, y, and z:
op(x, x) must always be false
if op(x, y) is true then op(y, x) must be false
if op(x, y) && op(y, z) is true then op(x, z) must also be true
if !op(x, y) && !op(y, x) is true then !op(x, z) && !op(z, x) must also be true
The <= operator does not satisfy these conditions because, given x = y = 1, x <= x is not false and both x <= y and y <= x are true.
std::map uses these rules to implement its comparisons. For example, it could implement an equality check as !(op(x, y) || op(y, x)). Given x = 4, y = 4, and op = operator<= this becomes !(4 <= 4 || 4 <= 4), so 4 does not compare equal to 4 because the first rule above was broken.
On cppreference we find this quote.
Everywhere the standard library uses the Compare concept, uniqueness is determined by using the equivalence relation. In imprecise terms, two objects a and b are considered equivalent (not unique) if neither compares less than the other: !comp(a, b) && !comp(b, a).
This means that with you current compare
bool operator< (const MyClass& that) const { return val <= that.val; }
if you have two MyClass with val 5 and 5, 5 <= 5 will return true, and they will not be considered equivalent.
I understand that if the < operator is overloaded in C++ (for example, to insert custom structs into std::set), the implementation must be a strict weak order over the underlying type.
Consider the following struct and implementation. This implementation is not a strict weak order, but the code compiles and runs without throwing an error (I would expect it to throw an error, given the requirement of a strict weak order):
#include <iostream>
#include <set>
using namespace std;
struct Pixel {
int x;
int y;
};
bool operator < (Pixel lhs, Pixel rhs){
return lhs.x < rhs.x || lhs.y < rhs.y;
};
int main(){
set<Pixel> mySet;
Pixel *newPixelA = new Pixel;
newPixelA->x = 1;
newPixelA->y = 3;
Pixel *newPixelB = new Pixel;
newPixelB->x = 4;
newPixelB->y = 2;
mySet.insert(*newPixelA);
mySet.insert(*newPixelB);
}
Is this the expected behavior? EDIT: using Xcode.
The compiler has no way of determining whether your operator< is a strict weak ordering. Instead, what is meant by std::set requiring this is that it will only work correctly if you give it a strict weak ordering. It makes no guarantees about what will happen if you give it something else.
In general, what C++ means when it requires something is that it is your responsibility to make sure that something happens. If you do, then the compiler and library will guarantee that you get the right results.
Standard guarantees expected behavior if comparator requirements are met. Otherwise, what happens depends on implementation and data sets. Your comparison function may work properly for some data sets (where for all points greater x implies greater y). Set cannot contain equal elements (as a math concept), and for std::set equivalence means equality, so it'll just prevent you from inserting value a if there is already value b, such that:
a < b == true
b < a == true
even though a may be not equal to b
When the comparison operator implements strictly weak ordering of the contained elements, the objects in the std::set are ordered in a predictable patten. If not, there is no telling which object appears first in the std::set when you iterate over the objects.
Take the following sample program in which ordering of Pixel1 is not done right and ordering of Pixel2 is done right.
#include <iostream>
#include <set>
struct Pixel1 {
int x;
int y;
};
bool operator < (Pixel1 lhs, Pixel1 rhs){
return lhs.x < rhs.x || lhs.y < rhs.y;
};
struct Pixel2 {
int x;
int y;
};
bool operator < (Pixel2 lhs, Pixel2 rhs){
if ( lhs.x != rhs.x )
{
return (lhs.x < rhs.x);
}
return (lhs.y < rhs.y);
};
template <typename Pixel> void print(std::set<Pixel> const& mySet)
{
for ( Pixel p : mySet )
{
std::cout << "(" << p.x << ", " << p.y << ") ";
}
std::cout << std::endl;
}
template <typename Pixel> void test1()
{
std::set<Pixel> mySet;
Pixel pixelA = {2, 3};
Pixel pixelB = {4, 2};
Pixel pixelC = {4, 1};
mySet.insert(pixelA);
mySet.insert(pixelB);
mySet.insert(pixelC);
print(mySet);
}
template <typename Pixel> void test2()
{
std::set<Pixel> mySet;
Pixel pixelA = {2, 3};
Pixel pixelB = {4, 2};
Pixel pixelC = {4, 1};
mySet.insert(pixelB);
mySet.insert(pixelA);
mySet.insert(pixelC);
print(mySet);
}
int main()
{
std::cout << "Pixel1 ... \n";
test1<Pixel1>();
test2<Pixel1>();
std::cout << "Pixel2 ... \n";
test1<Pixel2>();
test2<Pixel2>();
}
Output
Pixel1 ...
(4, 1) (4, 2) (2, 3)
(4, 1) (2, 3) (4, 2)
Pixel2 ...
(2, 3) (4, 1) (4, 2)
(2, 3) (4, 1) (4, 2)
The order of objects in the std::set<Pixel1> depends on the order of insertion while the order of objects in the std::set<Pixel2> is independent of the order of insertion.
Only you can tell whether that is acceptable in your application,
I want to find the minimum number using STL in C++, I know the syntax should be min(x,y). But I want to find the minimum +ve numbers in the list. Not inlcuding the -ves. How do I do that?
P.S My numbers are in an array
For finding the minimum number, it makes sense to use std::min_element. Fortunately, it comes with an optional comparison parameter, which we can make use of: (sample here)
auto pos = std::min_element(std::begin(arr), std::end(arr),
[](const T &t1, const T &t2) {return t1 > 0 && (t2 <= 0 || t1 < t2);}
);
You just have to be careful to take into account that if it's comparing a positive t1 to a negative number, it should always be true. If none of the elements are positive, this will give the location of the first number in the array. If 0 should be treated as part of the positives, change t1 > 0 to t1 >= 0 and t2 <= 0 to t2 < 0.
I'd use std::accumulate with a suitable operation:
auto minpos = std::accumulate(myrange.begin(), myrange.end(), MAX_VALUE,
[](T acc, T x)
{ return (x > 0 && x < acc) ? x : acc; });
Here T is the type of your elements and MAX_VALUE is the maximal value of that type (e.g. defined as std::numeric_limits<T>::max()).
First use the remove_if algorithm to move all the negative numbers to the end of the collection, then call min_element on the positive range. In C++11
auto pos = remove_if(coll.begin(), coll.end(), [](int x){ return x < 0; });
auto min = *min_element(coll.begin(), pos);
If you're not using C++11 just replace the lambda with a pre-canned functor from like less<>
You may use std::min_element with Boost::filter_iterator
Something like:
struct is_positive_number {
bool operator()(int x) const { return 0 < x; }
};
void foo(const std::vector<int>& numbers)
{
typedef boost::filter_iterator<is_positive_number, base_iterator> FilterIter;
is_positive_number predicate;
FilterIter filter_iter_begin(predicate, begin(numbers), end(numbers + N));
FilterIter filter_iter_end(predicate, end(numbers + N), end(numbers + N));
FilterIter it = std::min_element(filter_iter_begin, filter_iter_end);
if (it != filter_iter_end) {
// *it is the min elem
} else {
// no positive numbers.
}
}
I am using a simple function (y(x)), and I want to generate an x value from a certain y value. While typically reverse mapping does not give a single x value, I am using the maximum from my y values. This means that there will be a unique x value for the y value I input(the maximum). I don't understand how to code this in c++
If you don't need interpolation, only exact reverse lookup, then it's relatively straighforward:
std::map<YType, XType> lookup;
// (code to read the file goes here)
// for each x {
YType y = f(x);
if ((lookup.count(y) == 0) || (lookup[y] < x)) {
lookup[y] = x;
}
// }
Then your reverse lookup is just lookup[y], which will return 0 (or a default-constructed value where applicable) if y in fact was missing from the data.
Be aware that my code is a bit inefficient, it looks up y several times in the map, up to 3. You can optimize using iterators, but I'm concerned that obscures what's going on if you're not already familiar with them:
typedef std::map<YType, XType> maptype;
typedef std::pair<maptype::iterator, bool> resulttype;
resulttype result = lookup.insert(std::make_pair(y, x));
if (!result.second) {
// key already existed, so value was not inserted. Check for max.
maptype::iterator pos = result.first;
if ((*pos).second < x) {
(*pos).second = x;
}
}
If I understand correctly, you are given a finite range of values x, say x[0], x[1], ..., x[N], and a function f, and you want to find the index k for which f(x[k]) is the largest possible. In that case, a simple search will do:
size_t k = 0;
T m = f(x[k]);
T tmp;
for (size_t i = 1; i <= N; ++i)
{
if ((tmp = f(x[i])) > m)
{
k = i;
m = tmp;
}
}
// Maximum is (x[k], m)
Here T is the type such that f is T f(T);