7

I would like to count the number of entries between two iterators of an std::multimap in less than O(N) time. Are there any tricks or clever ways to do this?

Since std::multimap has bidirectional iterators, my understanding is that something like std::distance could do this in O(N) time.

Additional details: The multimap's key is an N-tuple. I'm trying to find the number of entries in the multimap whose key's first element is 0. The options for the first element of they key are 0 and 1, and the multimap uses a strict weak ordering in which the first element of the key is always the most important. i.e., all elements with 0 come before any elements with 1.

Context: The iterators are returned by equal_range, which runs in logarithmic time. Declaratively, I'd like to measure the length of the range.

Thank you.

skyw
  • 349
  • 4
  • 15
  • Is having a counter out of the question? – imreal Mar 25 '14 at 20:26
  • It is not but I would prefer not to manually manage a counter, if there is another way to do this that is built into STL or that doesn't require a separate counter. – skyw Mar 25 '14 at 20:28
  • It's always irked me that map/set don't provide access to tree traveral bits that could do this sort of thing. Also `std::lower_bound(map_iter, map_iter, V)` is inefficient for the same reason. – Mooing Duck Mar 25 '14 at 20:41

2 Answers2

5

What you are looking for is so-called heterogeneous comparison lookup that was proposed in N3465. It allows for a different but compatible comparison function in the equal_range member function that is being used to store the Key. In your case, the lookup comparison operator (first tuple member) would be different from but consistent with the tuple lexicographical comparison.

Unfortunately, only a small portion of that paper has been accepted into the draft C++14 Standard, according to this Q&A. However, the N3465 paper's author is also the author of Boost.MultiIndex that has implemented this feature. You can emulate a std::multimap by following the Boost.MultiIndex documentation.

Once you have used the generalized equal_range member function of an adapted boost::multiindex_container, you can then simply do std::distance() on the returned iterators. Complexity would be logarithmic in the container size for the equal_range and linear in the size of the returned iterator range. If you are not interested in the result but only in the count, there is also a generalized count() member function returning that in the same logarithmic + linear time.

Community
  • 1
  • 1
TemplateRex
  • 69,038
  • 19
  • 164
  • 304
  • Interesting that the boost library improves upon the `std::multimap` container with the `boost::multiindex_container`. – CPlusPlus OOA and D Mar 25 '14 at 21:18
  • @CPlusPlusOOAandD Boost has traditionally been the place where many of the current Standard Library features have been implemented and tested for serious usage (smart pointers, hash tables, etc.). Hence the guidance from e.g. books as Effective C++ to "familiarize yourself with Boost". BTW, nice quote from N3465: "The author has received some reports pointing to this functionality as reason alone to use Boost.MultiIndex in place of standard associative containers" – TemplateRex Mar 25 '14 at 21:22
  • I am very aware of the Boost proving ground for new Standard Library features. It is a bit surprising that the MultiIndex container is not part of the standard already (e.g. available since Boost 1.32.0). My 2001 copy of `Effective C++` seems to have very little Boost material. Any recommendations besides this page: [where can I find a good boost reference? - closed](http://stackoverflow.com/questions/1126118/where-can-i-find-a-good-boost-reference/1126210#1126210) on Boost reference books? I am currently obtaining the web site documentation with `wget`. – CPlusPlus OOA and D Mar 25 '14 at 22:01
  • A fast review of this excellent Boost reference: [The Boost C++ Libraries](http://en.highscore.de/cpp/boost/index.html) indicates a great alternative to the Boost site documentation. The specific link to the MultiIndex material is here: [13.4 Boost.MultiIndex](http://en.highscore.de/cpp/boost/containers.html#containers_multiindex). – CPlusPlus OOA and D Mar 25 '14 at 22:20
  • https://www.youtube.com/playlist?list=PL5jc9xFGsL8GrLgRN3NXDoIglex3ruLKV short but good boost stuff – NoSenseEtAl Mar 25 '14 at 23:44
0

You want to use the std::distance method from the Iterator library. The reference is at std::distance. Here is the main reference: Iterator library.

The description reads thus as of March 25, 2014:

template< class InputIt >
typename std::iterator_traits<InputIt>::difference_type
    distance( InputIt first, InputIt last );

Returns the number of elements between first and last.

The behavior is undefined if last is not reachable from first by (possibly repeatedly) incrementing first.

Parameters
first - iterator pointing to the first element
last - iterator pointing to the last element

Type requirements
- InputIt must meet the requirements of InputIterator. The operation is more efficient if InputIt additionally meets the requirements of RandomAccessIterator

Return value
The number of elements between first and last.

Complexity
Linear.
However, if InputIt additionally meets the requirements of RandomAccessIterator, complexity is constant.


Note that the above details are subject to change. The most accurate information will be obtained by navigating to the std::distance reference page directly.

Sample code from the std::distance reference web page:

include <iostream>
#include <iterator>
#include <vector>

int main() 
{
    std::vector<int> v{ 3, 1, 4 };

    auto distance = std::distance(v.begin(), v.end());

    std::cout << distance << '\n';
}