2012-03-29 259 views
2
std::map<String, double> m1,m2; 
m1["A"] = 20; 
m2["A"] = 20.01; 

if (m1 == m2) 
    cout << "True"; 
else 
    cout << "False"; 

示例代码打印False,因为20不等于20.1。但是在我的应用程序中,我想将这些值视为相等,因为这些值之间的差异在允许的容差范围内。那么有什么办法可以为数据提供自定义的比较函数吗?如何将C++ std :: map与关联数据的自定义比较函数进行比较?

任何帮助表示赞赏。

修改: 对不起,在代码中的错误。我复制了我试图找到解决这个问题的代码。密钥必须与我的场景相同。

+0

你关心一个键是否小于另一个?还是只是平等? – Omnifarious 2012-03-29 18:37:19

+0

你不能以这样一种方式定义这样的比较,即在不使所有*值等于的情况下给你一个严格的弱排序:你想要20等于20.1,等于20.2,...,等于99.9,等于100.0。排序是传递的! – 2012-03-29 18:59:42

+0

@KerrekSB:哦,你是对的!如果以这种方式使用数据值,排序将具有配置。所以是的,真正的*小于*关系是不可能的。我需要修改我的修改答案。 – Omnifarious 2012-03-29 19:31:41

回答

1

如果你关心的是整个容器的平等性,我会向你推荐::std::equal算法。具体方法如下:

const double tolerance = 0.01; 
bool equal = m1.size() == m2.size() && 
      ::std::equal(m1.begin(), m1.end(), m2.begin(), 
          [tolerance](const decltype(m1)::value_type &a, 
             const decltype(m2)::value_type) &b) { 
    return (a.first == b.first) && 
      (::std::abs(a.second - b.second) < tolerance); 
}); 

如果你在乎一个“小于”的关系,那么::std::lexicographical_compare是你想要的。这需要C++ 11 lambda功能才能工作。

如果你真的想要的是以模糊的方式比较相等的数据值,我向你展示一些黑客(并且还需要一些C++ 11功能)fuzzy-double.cpp。我禁用排序比较,因为这会诱使您将这些东西装入容器中,并且因为(2.0 == 2.1) && (2.1 == 2.2),但是(2.0 != 2.2),它们不适合此目的。

#include <cmath> 
#include <iostream> 

template <const double &tolerance> 
class fuzzy_double { 
public: 
    fuzzy_double(double x) : x_(x) { 
     static_assert(tolerance >= 0, "tolerance must be >= 0"); 
    } 

    operator double() const { return x_; } 
    const fuzzy_double &operator =(double x) { x_ = x; } 

    bool equals(const fuzzy_double &b) const { 
     return ::std::abs(x_ - b.x_) <= tolerance; 
    } 
    // This cannot be used as the basis of a 'less than' comparison operator for 
    // the purposes of other algorithms because it's possible for a transitive 
    // equality relationship to exit that equates all fuzzy_double's to 
    // eachother. There is no strict ordering that makes sense. 
    bool fuzzy_less(const fuzzy_double &b) const { 
     return (b.x_ - x_) > tolerance; 
    } 

private: 
    double x_; 
}; 

template <const double &tolerance> 
bool operator ==(const fuzzy_double<tolerance> &a, 
       const fuzzy_double<tolerance> &b) 
{ 
    return a.equals(b); 
} 

template <const double &tolerance> 
bool operator !=(const fuzzy_double<tolerance> &a, 
       const fuzzy_double<tolerance> &b) 
{ 
    return !a.equals(b); 
} 

template <const double &tolerance> 
bool operator <(const fuzzy_double<tolerance> &a, 
       const fuzzy_double<tolerance> &b) 
{ 
    // tolerance < 0 should be an impossible condition and always be false, but 
    // it's dependent on the template parameter and so only evaluated when the 
    // template is instantiated. 
    static_assert(tolerance < 0, "fuzzy_doubles cannot be ordered."); 
    return false; 
} 

template <const double &tolerance> 
bool operator >=(const fuzzy_double<tolerance> &a, 
       const fuzzy_double<tolerance> &b) 
{ 
    // tolerance < 0 should be an impossible condition and always be false, but 
    // it's dependent on the template parameter and so only evaluated when the 
    // template is instantiated. 
    static_assert(tolerance < 0, "fuzzy_doubles cannot be ordered."); 
    return false; 
} 

template <const double &tolerance> 
bool operator >(const fuzzy_double<tolerance> &a, 
       const fuzzy_double<tolerance> &b) 
{ 
    // tolerance < 0 should be an impossible condition and always be false, but 
    // it's dependent on the template parameter and so only evaluated when the 
    // template is instantiated. 
    static_assert(tolerance < 0, "fuzzy_doubles cannot be ordered."); 
    return false; 
} 

template <const double &tolerance> 
bool operator <=(const fuzzy_double<tolerance> &a, 
       const fuzzy_double<tolerance> &b) 
{ 
    // tolerance < 0 should be an impossible condition and always be false, but 
    // it's dependent on the template parameter and so only evaluated when the 
    // template is instantiated. 
    static_assert(tolerance < 0, "fuzzy_doubles cannot be ordered."); 
    return false; 
} 

extern constexpr double ten_e_minus_2 = 0.01; 

int main() 
{ 
    fuzzy_double<ten_e_minus_2> a(3); 
    fuzzy_double<ten_e_minus_2> b(3.009); 
    fuzzy_double<ten_e_minus_2> c(2.991); 
    fuzzy_double<ten_e_minus_2> d(3.011); 
    fuzzy_double<ten_e_minus_2> e(2.989); 

    using ::std::cout; 

    cout << "a == a: " << (a == a) << '\n'; 
    cout << "a == b: " << (a == b) << '\n'; 
    cout << "a == c: " << (a == c) << '\n'; 
    cout << "a == d: " << (a == d) << '\n'; 
    cout << "a == e: " << (a == e) << '\n'; 
    return 0; 
} 

C++ 11不允许double,甚至没有const一个,是一个模板参数。 OTOH允许指针和引用具有外部链接的对象作为模板参数。因此,如果您将容差声明为extern constexpr double,则可以使用指定容差作为模板参数。

+0

问题已更新。 – 2012-03-29 18:17:10

+0

谢谢您的信息。 std :: equal对我有效。 – 2012-03-30 07:06:26

1

您正在比较两个完全独立的地图,里面有不同的键和值。我很确定这不是你的意图。

您可以制作具有自定义比对操作的密钥的地图,以回答我认为您所要求的内容,但使用变量的容差可能不会很好。 STL对低于和等于平均值​​以及它们的行为方式有严格的要求;如果你违反规则,你的地图就会随机失败。所以我不会使用这个地图。在你的情况中,你对价值感兴趣,而不是关键,在这种情况下,STL根本不在乎(它根本不看你的价值)。

+0

问题已更新 – 2012-03-29 18:16:05

0

当您比较M1和M2时,您会将地图彼此比较,而不是地图中的值。如果你想比较双打,请参考this post

+0

问题已更新。 – 2012-03-29 18:16:46

1

您可以使用lexicographical_compare与对自定义比较忽略的数值小的差异,像这样:

bool mycomp (pair<string,double> lhs, pair<string,double> rhs) { 
    if (lhs.first < rhs.first) { 
     return true; 
    } 
    if (lhs.first > rhs.first) { 
     return false; 
    } 
    // Here is the "check tolerance" part: 
    if (abs(lhs.second-rhs.second) < 0.05) { 
     return false; 
    } 
    return lhs.second < rhs.second; 
} 

int main() { 
    std::map<string, double> m1,m2; 
    m1["A"] = 20; 
    m2["A"] = 20.01; 
    bool res = lexicographical_compare(
     m1.begin(), m1.end(), 
     m2.begin(), m2.end(), 
     mycomp 
    ); 
    cerr << res << endl; 
    return 0; 
} 
+0

@MooingDuck我拿起编辑,而我确认我的代码做它预期的,谢谢:) – dasblinkenlight 2012-03-29 18:18:55