patterncppMinor
Exponential search
Viewed 0 times
exponentialsearchstackoverflow
Problem
I recently discovered the exponential search algorithm which can be used to find whether a value exists in an ordered range. An exponential search checks whether a value is smaller than the \$2^n\$th element of the range, then if the searched value is indeed smaller, it performs a binary search in the range \$[2^n, 2^{n-1})\$ to check whether the value exists in this range, otherwise it starts again by comparing the value to the \$2^{n+1}\$th element, etc... Here is my implementation of the algorithm for random-access iterators (I could make it work with forward iterators too but it has a cost):
However, while implementing this algorithm, I remembered a nice property of binary search, which is notably exploited by the Ford-Johnson merge-insertion sort: searching a value in \$2^n\$ elements and in \$2^{n+1}-1\$ takes the same number of comparisons (e.g. binary search in collections of size \$16\$ and \$31\$ requires the same number of comparisons). Therefore I modified the algorithm so that it would always search in sequences whose size is \$2^n-1\$ to maximize its efficiency:
```
#include
#include
#include
template
>
auto exponential_search(Iterator first, Iterator last,
const T& key, Compare compare={})
-> bool
{
using difference_type = typename std::iterator_traits::difference_type;
difference_type size = std::distance(first, last);
if (firs
#include
#include
#include
template
>
auto exponential_search(Iterator first, Iterator last,
const T& key, Compare compare={})
-> bool
{
using difference_type = typename std::iterator_traits::difference_type;
difference_type size = std::distance(first, last);
if (first == last) return false;
difference_type bound = 1;
while (bound < size && not compare(key, first[bound]))
{
bound *= 2;
}
auto end = first + std::min(bound, size);
return std::binary_search(first + bound / 2, end, key, compare);
}However, while implementing this algorithm, I remembered a nice property of binary search, which is notably exploited by the Ford-Johnson merge-insertion sort: searching a value in \$2^n\$ elements and in \$2^{n+1}-1\$ takes the same number of comparisons (e.g. binary search in collections of size \$16\$ and \$31\$ requires the same number of comparisons). Therefore I modified the algorithm so that it would always search in sequences whose size is \$2^n-1\$ to maximize its efficiency:
```
#include
#include
#include
template
>
auto exponential_search(Iterator first, Iterator last,
const T& key, Compare compare={})
-> bool
{
using difference_type = typename std::iterator_traits::difference_type;
difference_type size = std::distance(first, last);
if (firs
Solution
Just a few miscellaneous comments about a few points:
Rather than just
I realize some people like to use trailing return types (in general), but I'd prefer to see them reserved for times they're really needed. In a case like this, it just adds syntactic noise.
Given that you've just computed the size, it's probably at least worth considering whether to use that result, and just use:
...instead of pretty much re-computing the same thing via
Below you use
template
>Rather than just
Iterator, I'd rather see it given a name reflecting the (minimum) category of iterator expected. In this case, it looks like it probably only really makes sense for a random-access iterator, but I'm not entirely certain of that.auto exponential_search(Iterator first, Iterator last,
const T& key, Compare compare={})
-> boolI realize some people like to use trailing return types (in general), but I'd prefer to see them reserved for times they're really needed. In a case like this, it just adds syntactic noise.
using difference_type = typename std::iterator_traits::difference_type;
difference_type size = std::distance(first, last);
if (first == last) return false;Given that you've just computed the size, it's probably at least worth considering whether to use that result, and just use:
if (size == 0) return false;...instead of pretty much re-computing the same thing via
first == last.Below you use
first[bound] and in general, the algorithm seems to really require random access iterators to make much sense. If so, I'd rather just use last - first instead of std::distance to compute the difference.Code Snippets
template<
typename Iterator,
typename T,
typename Compare = std::less<>
>auto exponential_search(Iterator first, Iterator last,
const T& key, Compare compare={})
-> boolusing difference_type = typename std::iterator_traits<Iterator>::difference_type;
difference_type size = std::distance(first, last);
if (first == last) return false;if (size == 0) return false;Context
StackExchange Code Review Q#119665, answer score: 4
Revisions (0)
No revisions yet.