【问题标题】:passing operator== to set in c++将 operator== 传递给 C++ 中的设置
【发布时间】:2012-02-06 18:31:22
【问题描述】:

我想要一个set<double> S; 并在其中插入一些双打。但我希望集合考虑1.0000001 == 1.0000000 (使用epsilon 比较双打)(我的意思是如果我将两个数字都插入到集合中,set.size() 应该等于1)。我知道如何将 operator() (用于比较)传递给集合,但我不知道如何传递函数:

const double eps = 1e-8;
bool operator==(double a, double b)
{
    return abs(a - b) < eps;
}

到集合。

P.S:感谢希德。 @Sid:我发现:std::set 不使用 operator==。当 !(a

【问题讨论】:

  • 我不是 C++ 专家,但我很确定您应该使用传递关系作为集合的比较。您的 operator== 不具有传递性。
  • @Complicatedseebio 这两个数字中的哪一个在集合中并不重要。重要的是其中一个在集合中。
  • 这个等式的定义听起来很麻烦,因为你打破了等式的传递属性(如果 A==B 和 B==C 则 A==C)。取 A=1,B=1+eps 和 C=1+2*eps,A==B,B==C,但 A!=C。如果这会以不可预知的方式破坏 std::set 的实现,我不会感到惊讶。
  • 你可能想要 fabs( ) 而不是 abs( )。并且感兴趣的比较函数将是 less()。

标签: c++ templates comparison operator-overloading set


【解决方案1】:

简单的答案是你不能,至少不是那么容易。你 必须定义一个定义严格弱的比较运算符 订购。如果你有类似的东西:

bool
cmpDouble( double lhs, double rhs )
{
    return abs( lhs - rhs ) < eps
        ? false
        : lhs < rhs;
}

那么! (a &lt; b) &amp;&amp; ! (b &lt; a) 没有定义等价 关系,因此没有满足主要要求。

可以使用类似的东西:

bool
cmpDouble( double lhs, double rhs )
{
    double iLhs;
    modf( 1e8 * lhs, &iLhs );
    double iRhs;
    modf( 1e8 * rhs, &iRhs );
    return iLhs < iRhs;
}

但坦率地说,我怀疑如果你的双打来源需要这个 之类的,它们可能不适合存储在 set 中。

【讨论】:

    【解决方案2】:

    如果您有比较功能,那么为什么需要 operator== ?看看下面的线程。

    std::set with user defined type, how to ensure no duplicates

    Mehrdad's answer

    【讨论】:

    • 见附注我有一个问题
    • 是的,这是真的。但是您可以在比较函数中控制该行为。例如,如果 a=1.0000000 和 b=1.0000001 你可以检查 fabs(ab) 是否小于 1e-6 (或任何你想要的精度)然后你返回一个 false (既不是 a 小于 b 也不是 b 小于a) 因此告诉 a 和 b 被认为是相等的。
    • 这不能满足比较器函数的要求,因为!comp(k1, k2) &amp;&amp; !comp(k2, k1) 不会定义等价关系,并且关系不是传递性的:comp(k1, k2) &amp;&amp; comp(k2, k3) 即使@987654326 也可以返回false @ 是真的。
    • @JamesKanze 你是对的——这会破坏传递性。正在考虑更好的解决方案。
    • @Farzam 这是关于该主题的现有讨论。 Farzam 使用我建议的方法可能有缺陷:stackoverflow.com/questions/4816156/…
    【解决方案3】:

    关闭@Sid 提供的链接,您似乎可以通过如下定义比较运算符来做到这一点:

    const double eps = 1e-8;
    bool less_than(double a, double b)
    {
        return a < b - eps;
    }
    
    bool greater_than(double a, double b)
    {
        return a > b + eps;
    }
    

    【讨论】:

    • 它没有定义等价关系,也不是传递的,所以它没有定义严格的弱排序。 (这意味着将其用作std::set&lt;double&gt; 的比较函数是未定义的行为。)
    【解决方案4】:

    您需要在将双打插入到集合之前量化双打。

    如果您认为 1.0000000 在将它们插入到集合中。同样对于 1.0000002

    这种方法避免了比较运算符和传递性的所有问题。

    【讨论】:

      【解决方案5】:

      这似乎是个坏主意。考虑数字1.00000001.00000011.0000002 的情况。如果您先插入1.0000001,则不允许将任何其他数字添加到集合中。如果您先添加1.00000001.0000002,则可以稍后添加另一个。

      此外,set 使用operator&lt; 来定义其关系,而不是相等。我看不到编写依赖于 epsilon 的严格弱排序的方法,它只是不会产生正确排序的容器。容器只是依赖于operator&lt; 或其他比较,无法指定相等操作。

      更好的是只使用普通的&lt; 比较,并在构建集合后进行后处理以清理您不再需要的元素。如果您向我们提供有关您尝试解决的真正问题的更多信息,我们可能会提供帮助。

      【讨论】:

      • 您始终可以使用modf(或可能还有其他东西)定义一组离散范围,然后使用每个范围中的特征元素进行比较。但你的最后一个建议可能更好。
      【解决方案6】:
      const double eps = 1e-8;
      bool compare(double a, double b)
      {
          return (abs(a - b) > eps) ? (a < b) : false;
      }
      
      set<int,bool(*)(double,double)> set (compare);
      

      struct Compare
      {
          bool operator()(double a, double b) const
          {
              return (abs(a - b) > eps) ? (a < b) : false;
          }
      };
      
      set<int,Compare> set;
      

      【讨论】:

      • 啊,比较函数不应该定义“小于”关系,而不是“等于”吗?
      • 但是这通过了操作符
      • @Farzam std::set 使用排序关系对其元素进行排序,默认情况下为 std::less。它不测试相等性,但依靠!cmp(lhs, rhs) &amp;&amp; !cmp(rhs, lhs) 定义等价关系。 (建议的功能没有。)
      猜你喜欢
      • 2015-09-17
      • 1970-01-01
      • 2013-08-23
      • 1970-01-01
      • 1970-01-01
      • 2014-03-22
      • 1970-01-01
      • 2021-05-23
      • 2013-01-16
      相关资源
      最近更新 更多