tags:

views:

829

answers:

6

I have a sorted array of double values in C++. Is there an STL function that will return the index of the nearest value in the array to a given double value?

For example, given the following array

double myarray[5] = { 1.0, 1.2, 1.4. 1.5, 1.9 };

the function call

search(myarray, 1.6);

should return 3, the index of the element nearest to 1.6, instead of -1 (or some other flag value) indicating that the value 1.6 wasn't found.

+7  A: 

Pretty much a duplicate of this post.

mwigdahl
The solutions are similar enough, but the starting point that led to the question is different. If someone doesn't know that pointers to array elements and iterators over collections are essentially the same thing they'll never find that question.
Bill the Lizard
...and yet, +1 because the link *is* helpful.
Bill the Lizard
+11  A: 

maybe std::lower_bound std::upper_bound will help you.

bb
+3  A: 

Is the array guaranteed to be in ascending order? If so, give std::lower_bound a look.

eduffy
Yes, it is guaranteed to be in ascending order. std::lower_bound did work, thank you.
Bill the Lizard
+1  A: 

I think my example do exactly what you want :

(I use std::min_element and a functor)

#include <algorithm>
#include <cmath>

const int ARRAY_LENGTH = 5;
double myarray[ARRAY_LENGTH] = { 1.0, 1.2, 1.4, 1.5, 1.9 };

struct CompareDistanceFromLevel
{
    CompareDistanceFromLevel(double cLevel) : level(cLevel) {}

    bool operator()(double lhs, double rhs)
    {
        return std::abs(level - lhs) < std::abs(level - rhs);
    }

private:
    double level;
};

size_t getPositionOfLevel(double level)
{
    double *result;
    result = std::min_element(myarray, myarray+ARRAY_LENGTH, CompareDistanceFromLevel(level));
    return (result-myarray); // returns the index
}
Rexxar
+2  A: 

Here is a generic solution using std::lower_bound:

template <typename BidirectionalIterator, typename T>
BidirectionalIterator getClosest(BidirectionalIterator first, 
                                 BidirectionalIterator last, 
                                 const T & value)
{
    BidirectionalIterator before = std::lower_bound(first, last, value);

    if (before == first) return first;
    if (before == last)  return --last; // iterator must be bidirectional

    BidirectionalIterator after = before;
    --before;

    return (*after - value) < (value - *before) ? after : before;
}

You'll notice that I used Bidirectional Iterators, meaning that the function can only work with iterators that can be both incremented and decremented. A better implementation would only impose the Input Iterators concept, but for this problem this should be good enough.

Since you want the index and not an iterator, you can write a little helper function:

template <typename BidirectionalIterator, typename T>
std::size_t getClosestIndex(BidirectionalIterator first, 
                            BidirectionalIterator last, 
                            const T & value)
{
    return std::distance(first, getClosest(first, last, value));
}

And now you end up with a code like this:

const int ARRAY_LENGTH = 5;
double myarray[ARRAY_LENGTH] = { 1.0, 1.2, 1.4. 1.5, 1.9 };

int getPositionOfLevel(double level)
{
    return getClosestIndex(myarray, myarray + ARRAY_LENGTH, level);
}

which gives the following results:

level | index
 0.1  |  0
 1.4  |  2
 1.6  |  3
 1.8  |  4
 2.0  |  4
Luc Touraille
+1 - I didn't use your code, but it helped me find a bug in my own.
Bill the Lizard
A: 
#include "stdafx.h"
#include <limits>

using namespace std;

static const int array_len = 5;
double myarray[array_len] = { 1.0, 1.2, 1.4, 1.5, 1.9 };

int approx_search(const double val)
{
    double min_val = numeric_limits<double>::max();
    int index = 0;

    for(int i=0;i<array_len;++i)
    {
        double diff = abs(myarray[i] - val);
        if(diff<min_val)
        {
            min_val = diff;
            index = i;
        }
    }
    return index;
}
int _tmain(int argc, _TCHAR* argv[])
{
    printf("approximate %d\n",approx_search(1.6));
    printf("approximate %d\n",approx_search(1.7996));
    printf("approximate %d\n",approx_search(1.4996));
    printf("approximate %d\n",approx_search(0.0002));

    return 0;
 }
Indeera