简体   繁体   English

在 std::map 中查找最接近或精确的键

[英]Finding the closest or exact key in a std::map

I need to create a lookup table which links a length to a time interval (both are of data type double).我需要创建一个查找表,将长度链接到时间间隔(两者都是双精度数据类型)。 The keys increment linearly as they are inserted, so it will already be sorted (perhaps an unordered_map would be better?).键在插入时线性递增,因此它已经被排序(也许 unordered_map 会更好?)。

What I am looking for is a way to find a key that best matches the current length provided to get the time value, or even better find the two keys that surround the length (the given key is between them) so I can find the interpolated value between the two time values.我正在寻找的是一种找到与提供的当前长度最匹配的键以获取时间值的方法,或者甚至更好地找到围绕长度的两个键(给定的键在它们之间),这样我就可以找到插值两个时间值之间的值。

I also need the best performance possible as it will be called in real time.我还需要尽可能最好的性能,因为它将被实时调用。

EDIT: I would have rather the following was a comment to the first answer below, but the format is hard to read.编辑:我宁愿以下是对下面第一个答案的评论,但格式很难阅读。

I tried to do the following, but it seems to return the same iterator (5.6):我尝试执行以下操作,但它似乎返回相同的迭代器 (5.6):

std::map<double, double> map;
map.insert(std::pair<double, double>(0.123, 0.1));
map.insert(std::pair<double, double>(2.5, 0.4));
map.insert(std::pair<double, double>(5.6, 0.8));

std::map<double, double>::iterator low, high;
double pos = 3.0;
low = map.lower_bound(pos);
high = map.upper_bound(pos);

How would I get 'low' to point to the last element that is < than the key used to search?我如何让“低”指向最后一个元素,该元素小于用于搜索的键?

EDIT 2: Silly me, 'low--' will do it, providing it's not the first element.编辑 2:傻我,'low--' 会这样做,前提是它不是第一个元素。

Getting there :)到达那里 :)

For this, you can use either std::map::lower_bound为此,您可以使用std::map::lower_bound

Returns an iterator pointing to the first element that is not less than key.返回指向不小于 key 的第一个元素的迭代器。

or std::map::equal_rangestd::map::equal_range

Returns a range containing all elements with the given key in the container.返回包含容器中具有给定键的所有元素的范围。


In your case, if you want the closest entry, you need to check both the returned entry and the one before and compare the differences.在您的情况下,如果您想要最接近的条目,则需要检查返回的条目和之前的条目并比较差异。 Something like this might work像这样的事情可能会奏效

std::map<double, double>::iterator low, prev;
double pos = 3.0;
low = map.lower_bound(pos);
if (low == map.end()) {
    // nothing found, maybe use rbegin()
} else if (low == map.begin()) {
    std::cout << "low=" << low->first << '\n';
} else {
    prev = std::prev(low);
    if ((pos - prev->first) < (low->first - pos))
        std::cout << "prev=" << prev->first << '\n';
    else
        std::cout << "low=" << low->first << '\n';
}

“可能的最佳性能” - 假设您按递增顺序插入元素,您可以emplace_back它们push_back / emplace_back放入std::vector然后使用std::lower_bound - 您将获得更好的缓存利用率,因为数据将被打包到连续的地址空间中.

You could of course use lower_bound and upper_bound, which are logarithmic in runtime.您当然可以使用lower_bound 和upper_bound,它们在运行时是对数的。 And they should do what you want.他们应该做你想做的。

std::map<double,double>::iterator close_low;
//... your_map ...
close_low=your_map.lower_bound (current_length);

This should give you an iterator to the the first map element whose key is < current length.这应该为您提供一个迭代器,指向键为 < 当前长度的第一个 map 元素。 Do likewise with upper_bound and you have your time surrounded.对 upper_bound 做同样的事情,你的时间就会被包围。

The functionsstd::lower_bound() andstd::upper_bound() would be useful here.函数std::lower_bound()std::upper_bound()在这里很有用。

lower_bound() gives the first element that is >= to the value you're looking for; lower_bound()>=的第一个元素赋予您要查找的值; upper_bound() gives the first element that is > than the value. upper_bound()给出的与第一元件>比的值。

For instance, searching for the value 5 in the following list: {1,3,5,5,6} 1 using lower_bound() returns the third element, while upper_bound() would return the fifth element.例如,在以下列表中搜索值5{1,3,5,5,6} 1使用lower_bound()返回第三个元素,而upper_bound()将返回第五个元素。 If the two functions return the same thing x , then the value you're looking for is not present in the list.如果这两个函数返回相同的值x ,则您要查找的值不存在于列表中。 The value just before it is x-1 and the value just after it is x .它之前的值是x-1 ,它之后的值是x

1 As pointed out by Tony D in a comment, the question asked for maps, which generally do not contain duplicate elements. 1正如Tony D在评论中指出的那样,该问题要求映射,通常不包含重复元素。 I'm keeping this example though to illustrate the two functions.我保留这个例子是为了说明这两个功能。

Complete generic solution (original idea taken from Olaf Dietsche 's answer ):完整的通用解决方案(来自Olaf Dietsche回答的原始想法):

#include <map>
#include <iostream>
#include <cstdint>

template <typename T1, typename T2>
T1 findClosestKey(const std::map<T1, T2> & data, T1 key)
{
    if (data.size() == 0) {
        throw std::out_of_range("Received empty map.");
    }

    auto lower = data.lower_bound(key);

    if (lower == data.end()) // If none found, return the last one.
        return std::prev(lower)->first;

    if (lower == data.begin())
        return lower->first;

    // Check which one is closest.
    auto previous = std::prev(lower);
    if ((key - previous->first) < (lower->first - key))
        return previous->first;

    return lower->first;
}

int main () {
double key = 3.3;

std::map<double, int> data = {{-10, 1000}, {0, 2000}, {10, 3000}};

std::cout << "Provided key: " << key << ", closest key: " << findClosestKey(data, key) << std::endl;
return 0;
}
#include <map>

template <typename T1, typename T2>
std::map<T1, T2>::iterator nearest_key(const std::map<T1, T2>& map, T1 key) {
    auto lower_bound = map.lower_bound(key);
    auto upper_bound = lower_bound; upper_bound++;
    if (lower_bound == map.end()) return upper_bound;
    if (upper_bound == map.end()) return lower_bound;
    unsigned int dist_to_lower = std::abs((int)lower_bound->first - (int)key);
    unsigned int dist_to_upper = std::abs((int)upper_bound->first - (int)key);
    return (dist_to_upper < dist_to_lower) ? upper_bound : lower_bound;
}

above is wrong.以上是错误的。 should be like this template应该像这个模板

typename std::map<T1, T2>::const_iterator nearest_key(const std::map<T1, T2>& map, T1 key)
{
    auto lower_bound = map.lower_bound(key);
    if (lower_bound == map.end()) return --lower_bound;
    auto upper_bound = lower_bound; upper_bound++;
    if (upper_bound == map.end()) return lower_bound;
    auto dist_to_lower = lower_bound->first - key;
    auto dist_to_upper = upper_bound->first - key;
    return (dist_to_upper < dist_to_lower) ? upper_bound : lower_bound;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM