If compare between float, I think cannot just use equal ==, need to check if abs(a-b) < epsilon. So when float type value is used as a key, can we use equal_range function?
such as:
std::multimap<float, string> ds;
ds.insert(make_pair(2.0, string("a")));
ds.insert(make_pair(2.0, string("b")));
ds.insert(make_pair(3.0, string("d")));
ds.equal_range(2.0)
std::multimap::equal_range is not actually calculated using operator== at all. It is calculated using < AND > only. It is actually two iterators, the first being the std::multimap::lower_bound (first element not less than the given key) and the second being the std::multimap::upper_bound (first element greater than the given key).
So it is quite safe to use with floats and doubles.
Just test it and you'll see it obviously works.
#include <map>
#include <string>
#include <iostream>
int main()
{
std::multimap<float, std::string> ds;
ds.emplace(2.0f, std::string("a"));
ds.emplace(2.0f, std::string("b"));
ds.emplace(3.0f, std::string("d"));
auto r = ds.equal_range(2.0f);
for ( auto it = r.first; it != r.second; ++it )
std::cout << it->second << std::endl;
}
Output:
a
b
You can define your own less-operator for float. See the following example.
// http://www.cplusplus.com/reference/map/multimap/
#include <map>
#include <cassert>
#include <iostream>
#include <algorithm>
class CApproxFloatLess {
double m_eps;
public:
CApproxFloatLess(float eps) :
m_eps(eps)
{
assert(eps >= 0);
}
bool operator () (float x, float y) const {
return x + m_eps*(1+std::abs(x)) < y;
}
};
template <class It>
void info(float x, It& it) {
std::cout << "Found pair (" << it->first << ", " << it->second << ") for " << x << ".\n";
}
int main() {
typedef std::multimap<float,std::string,CApproxFloatLess> MyMap;
MyMap ds(CApproxFloatLess(1e-3));
ds.insert(make_pair(2.0, std::string("a")));
ds.insert(make_pair(2.0, std::string("b")));
ds.insert(make_pair(3.0, std::string("d")));
float x=2.001;
MyMap::iterator it=ds.find(x);
if( it != ds.end() )
info(x,it);
x=1.999;
it=ds.find(x);
if( it != ds.end() )
info(x,it);
x=2.01;
it=ds.find(x);
if( it != ds.end() )
info(x,it);
x=3.001;
it=ds.find(x);
if( it != ds.end() )
info(x,it);
return 0;
}
The output of this program is:
Found pair (2, a) for 2.001.
Found pair (2, a) for 1.999.
Found pair (3, d) for 3.001.
Who says you cannot use == to compare two floats? == works perfectly fine for floats; it will return true if they are equal and false if they are different. (There is some odd things about NaN's and negative zeroes, but that's not covered by a range check).
Obviously if you search for a value that isn't equal using == to any value in the multi map, it won't be found. And if you add two values that are as close to each other as possible, they will both be added.
Related
I have the following multiset in C++:
template<class T>
class CompareWords {
public:
bool operator()(T s1, T s2)
{
if (s1.length() == s2.length())
{
return ( s1 < s2 );
}
else return ( s1.length() < s2.length() );
}
};
typedef multiset<string, CompareWords<string>> mySet;
typedef std::multiset<string,CompareWords<string>>::iterator mySetItr;
mySet mWords;
I want to print each unique element of type std::string in the set once and next to the element I want to print how many time it appears in the list (frequency), as you can see the functor "CompareWord" keeps the set sorted.
A solution is proposed here, but its not what I need, because I am looking for a solution without using (while,for,do while).
I know that I can use this:
//gives a pointer to the first and last range or repeated element "word"
auto p = mWords.equal_range(word);
// compute the distance between the iterators that bound the range AKA frequency
int count = static_cast<int>(std::distance(p.first, p.second));
but I can't quite come up with a solution without loops?
Unlike the other solutions, this iterates over the list exactly once. This is important, as iterating over a structure like std::multimap is reasonably high overhead (the nodes are distinct allocations).
There are no explicit loops, but the tail-end recursion will be optimized down to a loop, and I call an algorithm that will run a loop.
template<class Iterator, class Clumps, class Compare>
void produce_clumps( Iterator begin, Iterator end, Clumps&& clumps, Compare&& compare) {
if (begin==end) return; // do nothing for nothing
typedef decltype(*begin) value_type_ref;
// We know runs are at least 1 long, so don't bother comparing the first time.
// Generally, advancing will have a cost similar to comparing. If comparing is much
// more expensive than advancing, then this is sub optimal:
std::size_t count = 1;
Iterator run_end = std::find_if(
std::next(begin), end,
[&]( value_type_ref v ){
if (!compare(*begin, v)) {
++count;
return false;
}
return true;
}
);
// call our clumps callback:
clumps( begin, run_end, count );
// tail end recurse:
return produce_clumps( std::move(run_end), std::move(end), std::forward<Clumps>(clumps), std::forward<Compare>(compare) );
}
The above is a relatively generic algorithm. Here is its use:
int main() {
typedef std::multiset<std::string> mySet;
typedef std::multiset<std::string>::iterator mySetItr;
mySet mWords { "A", "A", "B" };
produce_clumps( mWords.begin(), mWords.end(),
[]( mySetItr run_start, mySetItr /* run_end -- unused */, std::size_t count )
{
std::cout << "Word [" << *run_start << "] occurs " << count << " times\n";
},
CompareWords<std::string>{}
);
}
live example
The iterators must refer to a sorted sequence (with regards to the Comparator), then the clumps will be passed to the 3rd argument together with their length.
Every element in the multiset will be visited exactly once with the above algorithm (as a right-hand side argument to your comparison function). Every start of a clump will be visited (length of clump) additional times as a left-hand side argument (including clumps of length 1). There will be exactly N iterator increments performed, and no more than N+C+1 iterator comparisons (N=number of elements, C=number of clumps).
#include <iostream>
#include <algorithm>
#include <set>
#include <iterator>
#include <string>
int main()
{
typedef std::multiset<std::string> mySet;
typedef std::multiset<std::string>::iterator mySetItr;
mySet mWords;
mWords.insert("A");
mWords.insert("A");
mWords.insert("B");
mySetItr it = std::begin(mWords), itend = std::end(mWords);
std::for_each<mySetItr&>(it, itend, [&mWords, &it] (const std::string& word)
{
auto p = mWords.equal_range(word);
int count = static_cast<int>(std::distance(p.first, p.second));
std::cout << word << " " << count << std::endl;
std::advance(it, count - 1);
});
}
Outputs:
A 2
B 1
Live demo link.
Following does the job without explicit loop using recursion:
void print_rec(const mySet& set, mySetItr it)
{
if (it == set.end()) {
return;
}
const auto& word = *it;
auto next = std::find_if(it, set.end(),
[&word](const std::string& s) {
return s != word;
});
std::cout << word << " appears " << std::distance(it, next) << std::endl;
print_rec(set, next);
}
void print(const mySet& set)
{
print_rec(set, set.begin());
}
Demo
With reference to my previously asked question about boost::bimaps and boost associative property maps interface here, I want to use Put and Get helper functions for my bimap.
With reference to a sample code given here, I tried to add the following and i get a long compile error for assertion failed ... Here is the code :
#include <boost/bimap.hpp>
#include <boost/property_map/property_map.hpp>
#include <boost/bimap/property_map/set_support.hpp>
#include <iostream>
using namespace boost;
int main()
{
typedef int vertex_descriptor_t;
typedef boost::bimaps::bimap< vertex_descriptor_t, size_t > vd_idx_bimap_t;
typedef boost::associative_property_map<vd_idx_bimap_t::left_map> asso_vd_idx_bimap_t;
// define bimap
vd_idx_bimap_t my_bimap;
asso_vd_idx_bimap_t my_asso_bimap(my_bimap.left);
typedef typename vd_idx_bimap_t::value_type value_type;
my_bimap.insert( value_type( 1, 100 ) );
// print bimap
for(auto t = my_bimap.left.begin(); t != my_bimap.left.end(); ++t)
std::cout << t->first << " " << t->second << "\n";
int z = 1;
std::cout << "value = " << get ( my_bimap.left, z ) << std::endl; // prints correctly value = 100
// ERROR here .
boost::put( my_asso_bimap, 2, 19 );
}
It gives error as: ( a long list. but i have just put a snippet )
cannot convert âboost::bimaps::detail::non_mutable_data_unique_map_view_access<Derived, Tag, BimapType>::operator[](const CompatibleKey&)::BIMAP_STATIC_ERROR__OPERATOR_BRACKET_IS_NOT_SUPPORTED360::assert_arg<long unsigned int>()â (type âmpl_::failed************ (boost::bimaps::detai
There is also one error which gives me error at line number 364 of the file (property_map.hpp) in boost
put(const put_get_helper<Reference, PropertyMap>& pa, K k, const V& v)
{
static_cast<const PropertyMap&>(pa)[k] = v;
}
I understand the error that associative map cannot mutate the data as it references to the left map view . but How do I use put and get helper functions ?
I can use GET (my_bimap.left, z ) functions, but I am not able to use PUT function. I wanted to use associative property map for get and put functions to operate on actual bimap so that i dont have to use insert( value_type() )...
I hope I am clear enough for my problem. Please suggest.
In general you cannot update bimap entries via iterators:
The relations stored in the Bimap will not be in most cases modifiable directly by iterators because both sides are used as keys of key-based sets. When a bimap left view iterator is dereferenced the return type is signature-compatible with a std::pair< const A, const B >.
So there's your answer. Likewise, you couldn't
my_bimap.left[2] = 19;
http://www.boost.org/doc/libs/release/libs/bimap/doc/html/boost_bimap/the_tutorial/differences_with_standard_maps.html#boost_bimap.the_tutorial.differences_with_standard_maps.iterator__value_type
Now, reading a bit more on there leads me to "suspect" the following solution:
typedef bm::bimap< vertex_descriptor_t, bm::list_of<size_t> > vd_idx_bimap_t;
Disclaimer: I don't know about the semantics that this changes (?) but it at least appears to support writable references. The below sample prints
1 100
value = 100
1 100
2 42
See it Live On Coliru
Full Listing
#include <boost/bimap.hpp>
#include <boost/property_map/property_map.hpp>
#include <boost/bimap/property_map/set_support.hpp>
#include <boost/bimap/list_of.hpp>
#include <iostream>
using namespace boost;
int main()
{
typedef int vertex_descriptor_t;
namespace bm = boost::bimaps;
typedef bm::bimap< vertex_descriptor_t, bm::list_of<size_t> > vd_idx_bimap_t;
typedef boost::associative_property_map<vd_idx_bimap_t::left_map> asso_vd_idx_bimap_t;
// define bimap
vd_idx_bimap_t my_bimap;
asso_vd_idx_bimap_t my_asso_bimap(my_bimap.left);
typedef typename vd_idx_bimap_t::value_type value_type;
my_bimap.insert( value_type( 1, 100 ) );
// print bimap
for(auto t = my_bimap.left.begin(); t != my_bimap.left.end(); ++t)
std::cout << t->first << " " << t->second << "\n";
int z = 1;
std::cout << "value = " << get ( my_bimap.left, z ) << std::endl; // prints correctly value = 100
boost::put( my_asso_bimap, 2, 42 );
for(auto t = my_bimap.left.begin(); t != my_bimap.left.end(); ++t)
std::cout << t->first << " " << t->second << "\n";
}
I have a set of ranges :
Range1 ---- (0-10)
Range2 ---- (15-25)
Range3 ---- (100-1000) and likewise.
I would like to have only the bounds stored since storing large ranges , it would be efficient.
Now I need to search for a number , say 14 . In this case, 14 is not present in any of the ranges whereas (say a number) 16 is present in one of the ranges.
I would need a function
bool search(ranges, searchvalue)
{
if searchvalues present in any of the ranges
return true;
else
return false;
}
How best can this be done ? This is strictly non-overlapping and the important criteria is that the search has to be most efficient.
One possibility is to represent ranges as a pair of values and define a suitable comparison function. The following should consider one range less than another if its bounds are smaller and there is no overlap. As a side effect, this comparison function doesn't let you store overlapping ranges in the set.
To look up an integer n, it can be treated as a range [n, n]
#include <set>
#include <iostream>
typedef std::pair<int, int> Range;
struct RangeCompare
{
//overlapping ranges are considered equivalent
bool operator()(const Range& lhv, const Range& rhv) const
{
return lhv.second < rhv.first;
}
};
bool in_range(const std::set<Range, RangeCompare>& ranges, int value)
{
return ranges.find(Range(value, value)) != ranges.end();
}
int main()
{
std::set<Range, RangeCompare> ranges;
ranges.insert(Range(0, 10));
ranges.insert(Range(15, 25));
ranges.insert(Range(100, 1000));
std::cout << in_range(ranges, 14) << ' ' << in_range(ranges, 16) << '\n';
}
The standard way to handle this is through so called interval trees. Basically, you augment an ordinary red-black tree with additional information so that each node x contains an interval x.int and the key of x is the low endpoint, x.int.low, of the interval. Each node x also contains a value x.max, which is the maximum value of any interval endpoint stored in the subtree rooted at x. Now you can determine x.max given interval x.int and the max values of node x’s children as follows:
x.max = max(x.int.high, x.left.max, x.right.max)
This implies that, with n intervals, insertion and deletion run in O(lg n) time. In fact, it is possible to update the max attributes after a rotation in O(1) time. Here is how to search for an element i in the interval tree T
INTERVAL-SEARCH(T, i)
x = T:root
while x is different from T.nil and i does not overlap x.int
if x.left is different from T.nil and x.left.max is greater than or equal to i.low
x = x.left
else
x = x.right
return x
The complexity of the search procedure is O(lg n) as well.
To see why, see CLRS Introduction to algorithms, chapter 14 (Augmenting Data Structures).
You could put something together based on std::map and std::map::upper_bound:
Assuming you have
std::map<int,int> ranges; // key is start of range, value is end of range
You could do the following:
bool search(const std::map<int,int>& ranges, int searchvalue)
{
auto p = ranges.upper_bound(searchvalue);
// p->first > searchvalue
if(p == ranges.begin())
return false;
--p; // p->first <= searchvalue
return searchvalue >= p->first && searchvalue <= p->second;
}
I'm using C++11, if you use C++03, you'll need to replace "auto" by the proper iterator type.
EDIT: replaced pseudo-code inrange() by explicit expression in return statement.
A good solution can be as the following. It is O(log(n)).
A critical condition is non overlapping ranges.
#include <set>
#include <iostream>
#include <assert.h>
template <typename T> struct z_range
{
T s , e ;
z_range ( T const & s,T const & e ) : s(s<=e?s:e), e(s<=e?e:s)
{
}
};
template <typename T> bool operator < (z_range<T> const & x , z_range<T> const & y )
{
if ( x.e<y.s)
return true ;
return false ;
}
int main(int , char *[])
{
std::set<z_range<int> > x;
x.insert(z_range<int>(20,10));
x.insert(z_range<int>(30,40));
x.insert(z_range<int>(5,9));
x.insert(z_range<int>(45,55));
if (x.find(z_range<int>(15,15)) != x.end() )
std::cout << "I have it" << std::endl ;
else
std::cout << "not exists" << std::endl ;
}
If you have ranges ri = [ai, bi]. You could sort all the ai and put them into an array and search for x having x >= ai and ai minimal using binary search.
After you found this element you have to check whether x <= bi.
This is suitable if you have big numbers. If, on the other hand, you have either a lot of memory or small numbers, you can think about putting those ranges into a bool array. This may be suitable if you have a lot of queries:
bool ar[];
ar[0..10] = true;
ar[15..25] = true;
// ...
bool check(int searchValues) {
return ar[searchValues];
}
Since the ranges are non-overlapping the only thing left to do is performing a search within the range that fit's the value. If the values are ordered within the ranges, searching is even simpler. Here is a summary of search algorithms.
With respect to C++ you also can use algorithms from STL or even functions provided by the containers, e. g. set::find.
So, this assumes the ranges are continous (i.e range [100,1000] contains all numbers between 100 and 1000):
#include <iostream>
#include <map>
#include <algorithm>
bool is_in_ranges(std::map<int, int> ranges, int value)
{
return
std::find_if(ranges.begin(), ranges.end(),
[&](std::pair<int,int> pair)
{
return value >= pair.first && value <= pair.second;
}
) != ranges.end();
}
int main()
{
std::map<int, int> ranges;
ranges[0] = 10;
ranges[15] = 25;
ranges[100] = 1000;
std::cout << is_in_ranges(ranges, 14) << '\n'; // 0
std::cout << is_in_ranges(ranges, 16) << '\n'; // 1
}
In C++03, you'd need a functor instead of a lambda function:
struct is_in {
is_in(int x) : value(x) {}
bool operator()(std::pair<int, int> pair)
{
return value >= pair.first && value <= pair.second;
}
private:
int value;
};
bool is_in_ranges(std::map<int, int> ranges, int value)
{
return
std::find_if(ranges.begin(), ranges.end(), is_in(value)) != ranges.end();
}
I have a multimap defined by
typedef std::pair<int, int> comp_buf_pair; //pair<comp_t, dij>
typedef std::pair<int, comp_buf_pair> node_buf_pair;
typedef std::multimap<int, comp_buf_pair> buf_map; //key=PE, value = pair<comp_t, dij>
typedef buf_map::iterator It_buf;
int summ (int x, int y) {return x+y;}
int total_buf_size = 0;
std::cout << "\nUpdated buffer values" << std::endl;
for(It_buf it = bufsz_map.begin(); it!= bufsz_map.end(); ++it)
{
comp_buf_pair it1 = it->second;
// max buffer size will be summ(it1.second)
//total_buf_size = std::accumulate(bufsz_map.begin(), bufsz_map.end(), &summ); //error??
std::cout << "Total buffers required for this config = " << total_buf_size << std::endl;
std::cout << it->first << " : " << it1.first << " : " << it1.second << std::endl;
}
I would like to sum all the values pointed by it1.second
How can the std::accumulate function access the second iterator values?
Your issue is with the summ function, you actually need something better than that to be able to handle 2 mismatched types.
If you're lucky, this could work:
int summ(int x, buf_map::value_type const& v) { return x + v.second; }
If you're unlucky (depending on how accumulate is implemented), you could always:
struct Summer
{
typedef buf_map::value_type const& s_type;
int operator()(int x, s_type v) const { return x + v.second.first; }
int operator()(s_type v, int x) const { return x + v.second.first; }
};
And then use:
int result = std::accumulate(map.begin(), map.end(), 0, Summer());
I think you'll just need to change your summ function to take the map value_type instead. This is totally untested but it should give the idea.
int summ (int x, const buf_map::value_type& y)
{
return x + y.second;
}
And call it:
total_buf_size = std::accumulate(bufsz_map.begin(), bufsz_map.end(), 0, &summ);
Why do you mess about with pairs containing pairs? It is too complicated and you'll wind up making errors. Why not define a struct?
Accumulate is a generalization of summation: it computes the sum (or some other binary operation) of init and all of the elements in the range [first, last).
... The result is first initialized to init. Then, for each iterator i in [first, last), in order from beginning to end, it is updated by result = result + *i (in the first version) or result = binary_op(result, *i) (in the second version).
Sgi.com
Your attempt was neither first or second version, you missed the init part
total_buf_size = std::accumulate(bufsz_map.begin(), bufsz_map.end(), 0, &summ);
In Ruby I can do:
[1,2,3,4].include?(4) #=>True
In Haskell I can do :
4 `elem` [1,2,3,4] #=> True
What should I do in C++?
Here an example using find:
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> Num(4);
//insert values
Num[0]=1;
Num[1]=2;
Num[2]=3;
Num[3]=4;
std::vector<int>::iterator p = find(Num.begin(), Num.end(), 4);
if (p == Num.end())
std::cout << "Could not find 4 in the vector" << std::endl;
else
std::cout << "Have found 4 in the vector" << std::endl;
return 0;
}
There isn't a built-in function doing exactly that.
There is std::find which comes close, but since it doesn't return a bool it is a bit more awkward to use.
You could always roll your own, to get syntax similar to JIa3ep's suggestion, but without using count (which always traverses the entire sequence):
template <typename iter_t>
bool contains(iter_t first, iter_t last, typename iter_t::value_type val){
return find(first, last, val) != last;
}
Then you can simply do this to use it:
std::vector<int> x;
if (contains(x.begin(), x.end(), 4)) {...}
If the vector is ordered, you can also use std::binary_search.
std::binary_search(vec.begin(), vec.end(), 4) // Returns true or false
To get similar syntax as in OP's question:
std::vector<int> x;
if ( count( x.begin(), x.end(), VAL_TO_FIND ) ) {
// found
} else {
// not found
}
You could use std::set This has a find() method.