要求:
- 基于数字比较键(例如std :: map)
对容器进行排序的容器- 根据浮动容差检查密钥是否存在(例如map.find()并使用自定义比较器)
- 和棘手的问题:比较器使用的浮动公差可以由用户在运行时更改!
醇>
前两个可以使用带有自定义比较器的地图来完成:
struct floatCompare : public std::binary_function<float,float,bool>
{
bool operator()( const float &left, const float &right ) const
{
return (fabs(left - right) > 1e-3) && (left < right);
}
};
typedef std::map< float, float, floatCompare > floatMap;
使用此实现,floatMap.find(15.0001)将在地图中找到15.0。
但是,假设用户不希望浮动容差为1e-3。 使比较器函数在运行时使用可变容差的最简单方法是什么?我不介意每次更新epsilon时都会根据新的比较器重新创建和重新排序地图。
答案 0 :(得分:8)
创建map
之后你无法改变它的顺序(你应该只使用普通的旧operator<
这里的浮点类型),你甚至不能使用“容忍”比较运算符,因为它可能违反map
所需的严格弱顺序以维持其状态。
但是可以使用lower_bound
和upper_bound
进行宽容搜索。要点是你要创建一个与equal_range
非常相似的包装函数,为{value-tolerance“执行lower_bound
,然后为”value + tolerance“执行upper_bound
并查看它是否创建符合条件的非空范围值。
答案 1 :(得分:5)
无法更改map
中元素在实例化后的排序方式。如果你要找到一些技术黑客(比如实现一个可以在运行时改变容差的自定义比较器),它会引起未定义的行为。
更改排序的主要替代方法是使用不同的排序方案创建另一个地图。这个其他地图可以是索引地图,其中键以不同的方式排序,并且值不是元素本身,而是主映射中的索引。
或许也许你真正想做的不是改变顺序,而是保持顺序并改变搜索参数。
你能做到,有几种方法可以做到。
一个是简单地使用map::lower_bound
- 一次使用公差的下限,一次使用公差的上限,刚好超过公差的结束。例如,如果要查找容差为1e-5的15.0
。您可以使用14.99995 lower_bound
然后再使用15.00005(我的数学可能会在这里)找到该范围内的元素。
另一种方法是将std::find_if
与自定义函子lambda或std::function
一起使用。您可以以构造中的公差和值来声明仿函数,并在operator()
中执行检查。
由于这是一个家庭作业问题,我会留下实际执行所有这些的繁琐细节。 :)
答案 2 :(得分:1)
使用简单的自定义比较器无法实现这一点,即使可以在定义后更改它,也不能使用新的比较器。事实是:“宽容比较器”并不是真正的比较器。对于三个值,a < c
(差异足够大)但a < b
和b < c
都不可能(两者差异太小)。示例:a = 5.0, b = 5.5, c = 6.0, tolerance = 0.6
您应该做的是使用operator<
对浮点数进行默认排序,即只是不提供任何自定义比较器。然后,对于查找,请不要使用find
,而应使用lower_bound
和upper_bound
根据容差修改值。这两个函数调用将为您提供两个迭代器,这两个迭代器定义将使用此容差接受的序列。如果此序列为空,则显然未找到密钥。
然后,您可能希望获得最接近要搜索的值的键。如果这是真的,那么你应该找到这个子序列的min_element
,使用比较器来考虑密钥和要搜索的值之间的差异。
template<typename Map, typename K>
auto tolerant_find(const Map & map, const K & lookup, const K & tolerance) -> decltype(map.begin()) {
// First, find sub-sequence of keys "near" the lookup value
auto first = map.lower_bound(lookup - tolerance);
auto last = map.upper_bound(lookup + tolerance);
// If they are equal, the sequence is empty, and thus no entry was found.
// Return the end iterator to be consistent with std::find.
if (first == last) {
return map.end();
}
// Then, find the one with the minimum distance to the actual lookup value
typedef typename Map::mapped_type T;
return std::min_element(first, last, [lookup](std::pair<K,T> a, std::pair<K,T> b) {
return std::abs(a.first - lookup) < std::abs(b.first - lookup);
});
}
答案 3 :(得分:1)
不是使用具有容差的比较器,而是以微妙的方式失败,只需使用从浮点值派生的一致键。使用舍入使浮点值保持一致。
inline double key(double d)
{
return floor(d * 1000.0 + 0.5);
}
答案 4 :(得分:0)
最好不要单独留下std::map
类(好吧,部分至少),然后编写自己的类来实现你提到的三种方法。
template<typename T>
class myMap{
private:
float tolerance;
std::map<float,T> storage;
public:
void setTolerance(float t){tolerance=t;};
std::map<float,T>::iterator find(float val); // ex. same as you provided, just change 1e-3 for tolerance
/* other methods go here */
};
话虽如此,我认为您需要重新创建容器并根据容差对其进行排序。
根据浮动容差检查密钥是否存在
仅表示您必须检查是否存在元素。地图中元素的位置不应更改。您可以从val-tolerance
开始搜索,当找到元素时(函数find
返回iterator
),获取下一个元素,直到map
结束}或者直到他们的值超过val+tolerance
。
这基本上意味着insert
/ add
/ []
/任何函数的行为都不是基于tolerance
,所以存储没有真正的问题价值观。
如果您害怕元素彼此太近,您可能希望从val
开始搜索,然后逐渐增加toleration
,直到达到用户所需的值。< / p>