Efficient Search through list of Ranges - c++

I have list of ranges { start, end }, and a value (point) , now I am looking for effective way to get last n th index from range in which given value is present.
For example:
List: [ { 0, 4 }, {5, 10 }, {11, 14 }, {15, 20} , {21, 25} ]
n : 2
value: 22
So here, 22 is in range {21, 25 } which is at index 4 ( 0 based ).
and since n is 2, function should return index of {11, 14 } because this is n th range from matching range.
Here, I can write binary function easily since I have sorted list of ranges. But I do not want to write while / for , I am looking for some C++ 11 / 14 algorithms / lambdas if available, which can solve this issue.
What is an efficient solution?

I like Jan's answer, but since the appropriate solution is notably different if your data is known to be sorted, here's an answer for the question as-asked:
#include <cstddef>
#include <utility>
#include <stdexcept>
#include <algorithm>
#include <iterator>
template<typename RngT, typename ValT, typename OffsetT>
std::size_t find_prev_interval(RngT const& rng, ValT const& value, OffsetT const offset) {
using std::begin; using std::end;
auto const first = begin(rng), last = end(rng);
auto const it = std::lower_bound(
first, last, value,
[](auto const& ivl, auto const& v) { return ivl.second < v; }
);
// optional if value is *known* to be present
if (it == last || value < it->first) {
throw std::runtime_error("no matching interval");
}
auto const i = std::distance(first, it);
return offset <= i
? i - offset
: throw std::runtime_error("offset exceeds index of value");
}
As the implementation only needs forward-iterators, this will work for any standard library container or C-array; however for std::set<> or something like boost::containers::flat_set<>, you'll want to alter the logic to call rng.lower_bound() rather than std::lower_bound(). Also, replace exceptions with something like boost::optional if it is usual for offset to be too large to return a valid index.

Assuming that your point is stored as a std::pair and that returning an iterator instead of an index is acceptable:
template <typename container_t, typename value_t, typename n_t>
auto KailasFind(const container_t& vec, value_t value, n_t n) {
auto match = std::find_if(vec.begin(), vec.end(), [&](const auto& p) {
return value >= p.first && value <= p.second;
});
return match - n;
}
usage:
using point_t = std::pair<int, int>;
std::vector<point_t> vec {{0, 4}, {5, 10}, {11, 14}, {15, 20}, {21, 25}};
auto it_to_result = KailasFind(vec, 22, 2);
auto result = *it_to_result;

Related

How to find std::max_element on std::vector<std::pair<int, int>> in either of the axis?

How do i find the max element in this pair std::vector<std::pair<int, int>> in either of the axis.
Let this be the sample pair:
0, 1
0, 2
1, 1
1, 2
1, 4
2, 2
3, 1
I tried using std::minmax_element():
const auto p = std::minmax_element(edges.begin(), edges.end());
auto max = p.second->first;
But this generates the max element only of the first column i.e 3, but i want the max element of either the columns i.e 4.
I want the max element to be the highest element of either the columns.
Use std::max_element with a custom compare function, something like:
auto max_pair = *std::max_element(std::begin(edges), std::end(edges),
[](const auto& p1, const auto& p2) {
return std::max(p1.first, p1.second) < std::max(p2.first, p2.second);
});
int max = std::max(max_pair.first, max_pair.second);
You need provide predicate which will define "less" relation for your items:
const auto p = std::minmax_element(
edges.begin(), edges.end(),
[](const auto& a, const auto& b) {
// provide relation less you need, example:
return std::max(a.first, a.second) < std::max(b.first, b.second);
});
By default (in your code) less operator is used. For std::pair it works in lexicographical ordering of elements (if first elements are less returns true if they are equal checks second elements if the are less).

How to get iterator to a certain object in a container?

I'm currently trying to copy a part of a std::vector, starting with the first value until a sequence of values has been "encountered". I’m using mainly STL algorithms and especially std::find_if() (I know there are other ways to accomplish the goal stated in the first sentence, but I'm mainly doing this to understand the STL, so using them would be defeating the underlying purpose).
As an example, let's say a vector holding integer elements (originalvec in the code) is to be copied until first a 6 and then in direct succession a 7 is encountered. I know how to compare for the 6, and then I would like to compare in the same call of the lambda if behind the 6 there is a 7. I think (not sure) for that, I would need to get an iterator to the 6, then use either std::advance() or just operator++ on the iterator and compare the dereferenced value to 7. However, I do not know how to get an iterator to the 6/the number currently compared?
#include <algorithm>
#include <vector>
using namespace std;
int main() {
vector <int> originalvec = { 4, 8, 7, 6, 55, 2, 6, 7, 8 };
vector <int> newvec;
copy(originalvec.begin(),
find_if(originalvec.begin(), originalvec.end(), [](int curnum) {
return (curnum == 6);
}),
back_inserter(newvec));
//why does newvec.begin() (instead of back_inserter(newvec)) not work?
//current result: newvec = {4, 8, 7}
//wanted result : newvec = {4, 8, 7, 6, 55, 2}
/*wanted function is roughly in this style:
copy(originalvec.begin(),
find_if(originalvec.begin(), originalvec.end(), [](int curnum) {
return (curnum == 6 && [curnum* +1] == 7);
}),
back_inserter(newvec));
*/
return 0;
}
You can use std::adjacent_find in this case:
auto it = std::adjacent_find( originalvec.begin(), originalvec.end(), []( int i1, int i2 ) {
return i1 == 6 and i2 == 7;
} );
Live example
You can use a custom find function, for example (not tested)
template <typename It, typename T>
It find_succ(It begin, It end, T v1, T v2)
{
if (begin == end)
return end;
It next = begin;
++next;
while (next != end) {
if (*begin == v1 && *next == v2)
return begin;
++begin, ++next;
}
return end;
}

Find the max and min element above a threshold in a container

I have a container that looks like this: std::map<size_t, std::set<char>>
I want to find the absolute max element and the min element above a threshold x based on the size of the std::set.
I can get the max and min using the following line:
auto minMaxElements = std::minmax_element(cbegin(map), cend(map),
[](const auto &lhs, const auto &rhs){
return lhs.second.size() < rhs.second.size();
});
Is it possible to get the min element above a threshold x using std::minmax_element?
Example:
Say I have a std::map of 5 elements. The sizes of the std::set values are as 1, 2, 1, 10, and 5. And I set the threshold as 1, then I want the max element to give the iterator containing std::set of size 10 and the min element to give the iterator containing std::set of size 2.
You can use std::find_if to do that
auto it = std::find_if(map.begin(), map.end(),
[x](const auto & l) -> bool {
return l.second.size() >= x; // >= or >, depending upon requirement
}
);
Then use this iterator in your std::minmax_element
auto minMaxElements = std::minmax_element(it, map.end(),
[](const auto & lhs, const auto & rhs) {
return lhs.second.size() < rhs.second.size();
}
);

Calculate the union of an ordered set in C++

I would like to combine three variants of runlength encoding schemes (the runlengths are cumulated, hence the variant).
Let's start with two of them:
The first one contains a list of booleans, the second a list of counters. Let's say that the first looks as follows: (value:position of that value):
[(true:6), (false:10), (true:14), (false:20)]
// From 1 to 6, the value is true
// From 7 to 10, the value is false
// From 11 to 14, the value is true
// From 15 to 20, the value is false
The second looks as follows (again (value:position of that value)):
[(1:4), (2:8), (4:16), (0:20)]
// From 1 to 4, the value is 1
// From 5 to 8, the value is 2
// From 9 to 16, the value is 4
// From 17 to 20, the value is 0
As you can see, the positions are slightly different in both cases:
Case 1 : [6, 10, 14, 20]
Case 2 : [4, 8, 16, 20]
I would like to combine those "position arrays", by calculating their union:
[4, 6, 8, 10, 14, 16, 20]
Once I have this, I would derive from there the new schemes:
[(true:4), (true:6), (false:8), (false:10), (true:14), (false:16), (false:20)]
[(1:4), (2:6), (2:8), (4:10), (4:14), (4:16), (0:20)]
I would like to know: is there any C++ standard type/class which can contain the "arrays" [6, 10, 14, 20] and [4, 8, 16, 20], calculate their union and sort it?
Thanks
Dominique
You'll want to use std::set_union from <algorithm>.
I use a std::vector<int> here, but it can be any template type.
#include <iostream>
#include <array>
#include <algorithm>
int main() {
std::vector<int> a{6, 10, 14, 20};
std::vector<int> b{4, 8, 16, 20};
std::vector<int> c;
std::set_union(a.begin(), a.end(), b.begin(), b.end(), std::back_inserter(c));
for(auto e: c) {
std::cout << e << ' ';
}
std::cout << '\n';
}
Here's the ideone
If you'd like to maintain only two std::vectors without introducing c, you could simply append b to a, sort the array, then call std::unique on a. There may be a clever way to do this in O(n), but here's the naïve approach:
#include <iostream>
#include <algorithm>
#include <vector>
int main() {
std::vector<int> a{6, 10, 14, 20};
std::vector<int> b{4, 8, 16, 20};
a.insert(a.end(), b.begin(), b.end());
std::sort(a.begin(), a.end());
auto last = std::unique(a.begin(), a.end());
a.erase(last, a.end());
for(auto e: a) {
std::cout << e << ' ';
}
std::cout << '\n';
}
Here's the ideone
Finally, you can use std::inplace_merge instead of std::sort. In the worst case it's O(nlogn) like std::sort, but in the best case it's O(n). Quite an increase in performance:
#include <iostream>
#include <algorithm>
#include <vector>
int main() {
std::vector<int> a{6, 10, 14, 20};
std::vector<int> b{4, 8, 16, 20};
auto a_size = a.size();
a.insert(a.end(), b.begin(), b.end());
// merge point is where `a` and `b` meet: at the end of original `a`.
std::inplace_merge(a.begin(), a.begin() + a_size, a.end());
auto last = std::unique(a.begin(), a.end());
a.erase(last, a.end());
for(auto e: a) {
std::cout << e << ' ';
}
std::cout << '\n';
}
Here's the ideone
I would like to know: is there any C++ standard type/class which can contain the "arrays" [6, 10, 14, 20] and [4, 8, 16, 20], calculate their union and sort it?
I guess you didn't do much research before asking this question. There's a class template that manages an ordered set, called set. If you add all the elements of two sets into a single set, you will have the union.
std::set<int> s1{6, 10, 14, 20};
std::set<int> s2{4, 8, 16, 20};
std::set<int> union = s1;
union.insert(s2.begin(), s2.end());
As hinted at by erip, there is an algorithm that only requires you to iterate both vectors once. As a precondition, both of them have to be sorted at the start. You can use that fact to always check which one is smaller, and only append a value from that vector to the result. It also allows you to remove duplicates, because if you want to add a value, that value will only be a duplicate if it is the last value added to the result vector.
I have whipped up some code; I haven't run extensive tests on it, so it may still be a little buggy, but here you go:
// Assume a and b are the input vectors, and they are sorted.
std::vector<int> result;
// We know how many elements we will get at most, so prevent reallocations
result.reserve(a.size() + b.size());
auto aIt = a.cbegin();
auto bIt = b.cbegin();
// Loop until we have reached the end for both vectors
while(aIt != a.cend() && bIt != b.cend())
{
// We pick the next value in a if it is smaller than the next value in b.
// Of course we cannot do this if we are at the end of a.
// If b has no more items, we also take the value from a.
if(aIt != a.end() && (bIt == b.end() || *aIt < *bIt))
{
// Skip this value if it equals the last added value
// (of course, for result.back() we need it to be nonempty)
if(result.size() == 0 || *aIt != result.back())
{
result.push_back(*aIt);
}
++aIt;
}
// We take the value from b if a has no more items,
// or if the next item in a was greater than the next item in b
else
{
// If we get here, then either aIt == a.end(), in which case bIt != b.end() (see loop condition)
// or bIt != b.end() and *aIt >= *bIt.
// So in either case we can safely dereference bIt here.
if(result.size() == 0 || *bIt != result.back())
{
result.push_back(*bIt);
}
++bIt;
}
}
It allows some optimizations in both style and performance but I think it works overall.
Of course if you want the result back in a, you can either modify this algorithm to insert directly into a, but it's probably faster to keep it like this and just a.swap(result) at the end.
You can see it in action here.

Get range in sorted std::vector satisfying to some condition

I have a sorted std::vector. Now I need to get range of items that satisfy to some condition. E.g.
vector -> 1, 4, 25, 73 450
get range that is smaller then 100 -> {1, 4, 25, 73}
How can I do this using std?
The simplest way is to use standard algorithm std::lower_bound
For example
#include <iostream>
#include <algorithm>
#include <vector>
int main()
{
std::vector<int> v = { 1, 4, 25, 73, 450 };
auto last = std::lower_bound( v.begin(), v.end(), 100 );
for ( auto it = v.begin(); it != last; ++it ) std::cout << *it << ' ';
std::cout << std::endl;
return 0;
}
The output is
1 4 25 73
If to substitute statement
auto last = std::lower_bound( v.begin(), v.end(), 100 );
for
auto last = std::lower_bound( v.begin(), v.end(), 50 );
then the output will be
1 4 25
And so on.:)
I think you can use std::equal_range. Simply define your predicate in a correct manner. Please note that the example condition you give meets the criteria for equal_range but for other conditions you may not be able to use std::equal_range.
Here is an example predicate for the given example:
bool smaller(int a, int b) {
return (a < 100) > (b < 100);
}
Here I consider two numbers equal if they compare to 100 the same way. If they do not a number smaller than 100 is smaller than a number not less than 100. Now you can call:
equal_range(a.begin(), a.end(), 50 /* any number < 100 */, smaller);
It sounds like remove_if or remove_if_copy is what you're looking for, e.g.:
std::vector<int> results;
std::remove_copy_if( original.cbegin(), original.cend(), std::back_inserter( results ),
[]( int value ) { return value < 100; } );
Put whatever you need in the lambda for the actual problem. (In pre C++11, you'll have to define a functional object with the predicate outside the function which calls remove_copy_if, but otherwise, the principle is the same.)