【问题标题】:Duplicate removal in std::mapstd::map 中的重复删除
【发布时间】:2018-02-12 15:44:53
【问题描述】:

类似于下面的链接,

https://stackoverflow.com/a/30424281/1442787

我的班级 Point 有成员变量 double x,y,z

我在课堂上重载了operator < 以将值插入std::map

bool Point::operator<(const Point &p)const{
    return (   x < p.x
            || (   x == p.x
                && (   y < p.y
                    || (   y == p.y
                        && z < p.z))));
}

我已经将我的地图定义为 Point 类作为键,std::pair 作为值

typedef std::pair<int,int> mypair;
typedef std::map<Point, mypair> mymap;

std::map 不允许插入重复键。

但是,在我的代码中,在插入键/值对时,重复键也被插入,如下所示

map:0.436612,16.527741,0.000000,22,2
map:0.454781,17.427262,15.264347,74,12
map:0.454781,17.427262,15.264347,27,11
map:0.608370,17.373443,20.124160,21,13
map:0.608370,17.373443,20.124160,69,11

重复插入的原因是什么?

【问题讨论】:

  • 查看一些代码会有所帮助,例如:operator&lt;.
  • 我打赌比较双打的平等是问题所在。 0.4547810.454781 看起来相等,但也许它们不是
  • 浮点/双精度与用于打印它们的精度。
  • 两个双精度数几乎不相等,使用 epsilon 容差
  • @tobi303 重载

标签: c++ duplicates containers stdmap


【解决方案1】:

您的问题是浮点值很少比较相等,特别是如果它们是计算的结果(但在更琐碎的情况下它们可能不相等,因为它们在内存中的存储大小与在寄存器中存储的大小不同)。

首先,您应该避免使用浮点作为键。

如果你真的想这样做,那么你应该有一些特定于域的比较运算符。您可以使用 epsilon-tolerance(如果abs(x - y) &lt; e,则两个浮点数相等,其中e 是一个小值),或者您可以使用与数字成比例的容差值(基本上,产生类似“两个数字相等到一定数量的有效数字”)。单元测试库,例如the Boost Test library 使用这些类型的比较。对于点,您可以使用基于距离的相等性:如果两点之间的距离小于某个值,则它们相等。

之前我提到了相等谓词。您可以这样进行比较:

bool Point::operator==(const point &p)const{
    // Using Manhattan distance here.
    return (x - p.x) + (y - p.y) + (z - p.z) < 0.0001;
}

bool Point::operator<(const Point &p)const{
    return p != *this && (   x < p.x
            || (   x == p.x
                && (   y < p.y
                    || (   y == p.y
                        && z < p.z))));
}

【讨论】:

  • 此代码并不比原始代码好,因为如果有一些点在公差范围内,您可能会遇到与原始代码相同的问题,例如您可以有一个点 a 在公差范围内的b 和一个点c 在点b 的容差范围内,但不在点a 的容差范围内。
  • 因此,在这种情况下,要么必须在添加点之前进行舍入,然后与适当的容差(或存储缩放值)进行比较,要么确保其代码能够容忍这些情况......
  • 这就是为什么我说使用浮点作为映射键通常是个坏主意。从概念上讲,它是一个连续值,用作键是没有意义的。
【解决方案2】:

我建议您改用std::multimap,因为它可以存储重复值...

如果您想确保要添加的给定点不在现有点的小公差范围内,那么我将检查x 坐标在公差范围内的任何点。

因此,要找到这样的点,您可以创建一个点:

 Point search { x - tolerance, -Inf, -Inf };

然后以lower_bound为起点,测试任何后续点,直到其x坐标大于x + tolerance

【讨论】:

  • OP 想要唯一的值,并且有(接近)重复,而不是相反
  • 你读过我的全部答案了吗?我的第二个建议准确说明如果 OP 想要唯一值该怎么做。
  • 感谢您在比较器功能中使用 epsilon 提供的所有反馈!
猜你喜欢
  • 1970-01-01
  • 2018-01-02
  • 2011-01-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-23
  • 1970-01-01
相关资源
最近更新 更多