在map或unordered_map之间选择由计算的double值组成的键。

时间:2015-06-17 14:12:27

标签: c++ dictionary hash unordered-map comparison-operators

为了在3d空间中快速找到一堆平面实体中的共面实体,我想创建一个从3d平面到位于该平面中的实体集的映射(估计最大约1000个平面和~100000个实体)

我可以创建自己的自定义类来表示3D平面键,但基本上这个类需要(数学上)四个双精度来唯一地标识一个平面:法线向量的3个坐标和平面上指定点的一个坐标。所以我们有:

struct Plane3d
{
    double n_x, n_y, n_z;
    double u;   // (u*n_x, u*n_y, u*n_z) is a point on the plane

    // some constructors etc.
}

这四个双打是每次从所考虑的实体计算出来的,因此必须考虑舍入误差和浮点比较问题。假设我已计算出合适的(相对)误差容差:

const double EPSILON;

然而,我不想逐一比较所有实体对的共面性(在O(n ^ 2)时间内进行分类),而是创建一个地图来对我的实体进行分类。

最理想的是unordered_map(在O(n)时间内创建):

unordered_map<Plane3d, vector<Entity>, PlaneHash, PlaneEquality> mapping;

这需要编写两个仿函数:PlaneEquality没问题,但是......

  • 是否可以为四个双打(或者甚至只是常规双精度)编写一个Hash函数,该函数考虑了比较误差容限。
  • 在规范中我已经读过,等式仿函数仅用于区分同一个桶内的相等密钥。有没有办法确保相同的密钥实际上最终在同一个桶中?

另一种选择是使用法线贴图(在O(n log n)时间内仍然创建)

map<Plane3d, vector<Entity>, PlaneCompare> mapping; 

PlaneCompare仿函数听起来可行,我可以使用四个双打的词典编排,并使用EPSILON检查每个“小于”。但我还有几个问题:

  • 这实际上有用吗?有没有陷阱?
  • 规范说,密钥的相等性,比如p1和p2,由测试!PlaneCompare(p1,p2) && !PlaneCompare(p2,p1)决定。如果我使用了字典顺序,这应该等同于具有容错性的直接相等测试,但这不是更慢吗?

2 个答案:

答案 0 :(得分:3)

“是否可以为四个双打(或者甚至只是常规双精度)编写一个Hash函数,它考虑了比较误差容差。”

不,不是。

这听起来像是一个非常明确的陈述,我怎么能这么肯定?

假设你想要0.00001的公差。价值无关紧要,我们只是以它为例。这意味着对于这个哈希函数:

  • hash(1.0)必须返回与hash(1.00001)相同的值

这样他们就可以被认为是平等的。但它也意味着:

  • hash(1.00001)必须返回与hash(1.00002)
  • 相同的值

出于同样的原因,

  • hash(1.00002)必须返回与hash(1.00003)
  • 相同的值

...依此类推,达到最高可能的双倍价值 - 实际上是无限的。低于1的值也是如此。

所以任何允许容差的哈希函数都必须为所有值返回相同的哈希值,这使得它无用。

P.S。实际上推荐一种有效的方法,四维四叉树(技术上像sedecimtree)可能是最好的。

答案 1 :(得分:0)

你可以围绕你的双打,例如范围[n - epsilon, n + epsilon)中的所有值都舍入为n n mod epsilon == 0,并对其进行哈希处理。在这种情况下,您的close值将具有相同的哈希值。

如何更好地哈希4个双打取决于你的情况,但即使总结它们也足够好。