5

I need to map values ranging between lowerBound and upperBound to a certain value.

Illustrative Example:

For example, imagine I have GPS system which has users subscribed to it. This system is able to provide me the distance of the user from a certain point. Based on the distance of the user I want to assign them an ID.

Thus users in distance from

  • 1 to 100 get ID: 8.4
  • 101 to 200 get ID: 7.2
  • 201 to 300 get ID: 3.6
  • 401 to 600 get ID: 4.1

and so on...

My approach:

So what I did, I created an std::map by initializing it as follows:

   std::map<int, double> distanceToIdMap; 

   distanceToIdMap =
    {
            {100, 8.4},
            {200, 7.2},
            {300, 3.6},
    };

Then I use this code to get the ID for the given distance:

double roundUpToHundred = std::ceil(realDistance / 100.0) * 100;
double powerForDistance = distanceToIdMap.at(roundUpToHundred);

However my approach breaks down for the 401 to 600 distance, because by ceiling to the nearest hundred for a distance of 400+ I get the value 500 for which I don't have an entry in the map. Of course the trivial solution would be to add an entry for 500 to the distanceToIdMap but that is not how I want to handle this problem.

I would like to have a map with {(lowerbound, upperbound) , correspondingID} structure so I can address cases where an ID covers distance spanning more than 100m. And a given can check if the lowerBound < realDistance < upperBound and then provide the ID.

cross
  • 1,018
  • 13
  • 32
  • 2
    Do you want holes in your mapping? If not then it's not necessary to provide both a beginning and ending to each range, the ending should be equal to the beginning of the next. – Mark Ransom May 29 '15 at 19:00

5 Answers5

4

It sounds like a use case for std::lower_bound. Note that lower_bound is the correct implementation, not upper_bound. This code compiles and works. The map does not need to be sorted, as it is already sorted. This should run in O(log(N)).

You'll need to catch the exception...

#include <iostream>
#include <algorithm>
#include <map>
#include <stdexcept>

using namespace std;

std::map<int, double> distanceToIdMap =
    {
            {100, 8.4},
            {200, 7.2},
            {300, 3.6},
            {600, 4.1}
    };   

double Distance(int user)
{
    auto x = std::lower_bound(distanceToIdMap.begin(), distanceToIdMap.end(), std::pair<const int,double>(user,0));
    if (x == distanceToIdMap.end()) throw std::runtime_error("can't find");
    return x->second;
}

int main()
{
    for(int user=25;user < 650;user+=25)
    {
        cout << user << " " << Distance(user) << std::endl;
    }
   return 0;
}

Output:

sh-4.3# g++ -o main *.cpp -std=c++11                                                                                                                                                                                                                    
main                                                                                                                                                                                                                                                    
sh-4.3# main                                                                                                                                                                                                                                            
25 8.4                                                                                                                                                                                                                                                  
50 8.4                                                                                                                                                                                                                                                  
75 8.4                                                                                                                                                                                                                                                  
100 8.4                                                                                                                                                                                                                                                 
125 7.2                                                                                                                                                                                                                                                 
150 7.2                                                                                                                                                                                                                                                 
175 7.2                                                                                                                                                                                                                                                 
200 7.2                                                                                                                                                                                                                                                 
225 3.6                                                                                                                                                                                                                                                 
250 3.6                                                                                                                                                                                                                                                 
275 3.6                                                                                                                                                                                                                                                 
300 3.6                                                                                                                                                                                                                                                 
325 4.1                                                                                                                                                                                                                                                 
350 4.1                                                                                                                                                                                                                                                 
375 4.1                                                                                                                                                                                                                                                 
400 4.1                                                                                                                                                                                                                                                 
425 4.1                                                                                                                                                                                                                                                 
450 4.1                                                                                                                                                                                                                                                 
475 4.1                                                                                                                                                                                                                                                 
500 4.1                                                                                                                                                                                                                                                 
525 4.1                                                                                                                                                                                                                                                 
550 4.1                                                                                                                                                                                                                                                 
575 4.1                                                                                                                                                                                                                                                 
600 4.1                                                                                                                                                                                                                                                 
terminate called after throwing an instance of 'std::runtime_error'                                                                                                                                                                                     
  what():  can't find                                                                                                                                                                                                                                   
Aborted (core dumped)                                                                                                                                                                                                                                   
sh-4.3# main                                                                                                                                                                                                                                            
Mark Lakata
  • 19,989
  • 5
  • 106
  • 123
  • just couple of questions for clarification before I accept an answer: **1)** how does this approach differ from the `map::upper_bound` given in @pascx64 answer? **2)** (just out of curiosity) is there a specific reason for naming `double Distance(int user)` function with the upper-case `Distance`? **3)** what is the role of the `std::pair(user,0)` – cross May 30 '15 at 13:23
  • 1
    1) this is equivalent to the ones using `map::lower_bound`; I wasn't aware that map supported `lower_bound` directly, so I used the general function to perform the same thing. The other answer are simpler 2) My naming convention uses capitals for all functions or methods. 3) the pair is the comparison object. A `map` object is an iterable list of `pairs`, and it is sorted by the `first` (key) of the pair. – Mark Lakata May 31 '15 at 05:05
  • 2
    `map::lower_bound` exists because `std::lower_bound` is inefficient when applied to a `map` - it doesn't have random access iterators. *Always* use the member function when it exists. – Mark Ransom Jun 01 '15 at 15:05
2

Try upper_bound :

 auto upper  = distanceToIdMap.upper_bound( 50 );
 std::cout << "value:" << upper->second << std::endl;

http://www.cplusplus.com/reference/map/map/upper_bound/

pascx64
  • 904
  • 16
  • 31
  • for the map I have in the question, what happens if I do `distanceToIdMap.upper_bound( 312 );` – cross May 29 '15 at 19:21
  • It will take the `401 to 600` range because you didn't put any value between 300 and 401 ( I'm not sure if it's a mistake in your question ? ) – pascx64 May 29 '15 at 19:26
2

with std::map::lower_bound and boost::optional, you may do something like:

const std::map<int, boost::optional<double>> distanceToIdMap =
{
    {0, boost::none},
    {100, 8.4},
    {200, 7.2},
    {300, 3.6},
    {400, boost::none},
    {600, 4.1}
};

for (auto v : {-10, 50, 99, 100, 101, 250, 350, 500, 601})
{
    auto it = distanceToIdMap.lower_bound(v);

    if (it != distanceToIdMap.end() && it->second) {
        std::cout << v << " " << *it->second << std::endl;
    } else {
        std::cout << v << " None" << std::endl;
    }
}

Live example

Jarod42
  • 203,559
  • 14
  • 181
  • 302
1

What about avoiding maps at all?

std::vector<std::pair<double, double>> table =
{
    {0, 0},
    {94.5, 2.1},
    {314.4, 4.7}
} ;

double distance = ...
double value = std::upper_bound(table.begin(), table.end(), std::pair(distance, 0)) ;

Drawback: table must be properly ordered.

marom
  • 5,064
  • 10
  • 14
0

Here is a simple solution:

int mapdistance[] = {100,200,300};
double mapid[] = {8.4,7.2,3.6};
double dist=0;
for(int i=0;i<sizeof(mapdistance)/sizeof(int);i++) {
   dist += mapdistance[i];
   if (realdistance<=dist) return mapid[i];
}
iplayfast
  • 140
  • 10
  • If you keep things sorted, you can use a binary search on your bounds check as well, if your mapdistance array starts getting big. Of course, this is a very C solution to a C++ stated problem :) – Michael Dorgan May 29 '15 at 19:30
  • What do you mean? It compiles in C++ :) The question as asked already had the values sorted, so I assumed they would continue that way. My system is linearly searched (N) and would not work with a binary since I'm adding the value to dist for each mapdistance. – iplayfast May 29 '15 at 20:17