【问题标题】:hashmap for 2d(3d) coordinates (i.e. vector of doubles)?2d(3d)坐标的哈希图(即双精度向量)?
【发布时间】:2013-05-23 11:10:38
【问题描述】:

我想知道对于hash map 坐标(在 2d 或 3d 中,即双精度向量)是否有一个通用的全方位解决方案?

示例here 演示了如何为pair<int,int> 创建自定义哈希映射, 但想出一张从pair<double,double>(可以表示二维坐标)到size_t的独特地图似乎并非易事。

我知道我可以通过提供比较器对象来使用有序映射,但对于我的应用程序来说,不需要对它们进行排序,而且哈希映射似乎更快。 但是,由于我是所有这些hash 东西的新手,我有点迷失了如何继续。

p/s/ 我使用 C++11。

【问题讨论】:

  • 为什么不能在您提供的链接中使用相同的方法?使用pair<double,double> 而不是pair<int,int> 似乎微不足道,而且由于您不需要订购,您仍然可以使用std::unordered_map
  • @André 因为地图不是唯一的。
  • 如果我没记错的话,std::unordered_map 中不能有多个相等键。但是,您可以将不同的键映射到相同的值。因此,密钥本身是独一无二的。你能更好地描述你的问题吗?
  • 坦率地说,我还没有遇到问题。我只是假设地图应该是唯一的,因此将(double)*100+(double) 映射到int 不是唯一的。但也许没关系。正如我所说,我是散列东西的新手。
  • 你是对的,散列应该是唯一的,你应该根据你的数据范围设计一种避免冲突的方法,或者使用一个好的散列方法。

标签: c++ hash c++11 hashmap


【解决方案1】:

使用 Boost 中的hash_combine 怎么样?

http://www.boost.org/doc/libs/1_53_0/doc/html/hash/combine.html

【讨论】:

    【解决方案2】:

    为了避免额外的依赖,你可以使用std::hash。这是一个使用您发布的链接中的代码的示例,并更新为使用std::pair<double,double>

    #include <unordered_map>
    #include <cassert>
    
    using namespace std;
    
    class TPoint3D{
    public:
        TPoint3D(double x, double y, double z) : x(x), y(y), z(z){};
    
        double x, y, z;
    };
    
    struct hashFunc{
        size_t operator()(const TPoint3D &k) const{
        size_t h1 = std::hash<double>()(k.x);
        size_t h2 = std::hash<double>()(k.y);
        size_t h3 = std::hash<double>()(k.z);
        return (h1 ^ (h2 << 1)) ^ h3;
        }
    };
    
    struct equalsFunc{
      bool operator()( const TPoint3D& lhs, const TPoint3D& rhs ) const{
        return (lhs.x == rhs.x) && (lhs.y == rhs.y) && (lhs.z == rhs.z);
      }
    };
    
    typedef unordered_map<TPoint3D, int, hashFunc, equalsFunc> TPoint3DMap;
    
    int main(){
      TPoint3DMap myMap;
    
      // test equalsFunc
      myMap[TPoint3D(10.0, 20.0, 30.0)] = 100;
      myMap[TPoint3D(10.0, 20.0, 30.0)] = 200;
    
      assert(myMap[TPoint3D(10.0, 20.0, 30.0)] == 200);
    
      // test if hashFunc handles well repeated values inside TPoint3D
      myMap[TPoint3D(10.0, 10.0, 10.0)] = 1;
      myMap[TPoint3D(10.0, 20.0, 10.0)] = 2;
      myMap[TPoint3D(10.0, 10.0, 20.0)] = 3;
      myMap[TPoint3D(20.0, 10.0, 10.0)] = 4;
    
      assert(myMap[TPoint3D(10.0, 10.0, 10.0)] == 1);
      assert(myMap[TPoint3D(10.0, 20.0, 10.0)] == 2);
      assert(myMap[TPoint3D(10.0, 10.0, 20.0)] == 3);
      assert(myMap[TPoint3D(20.0, 10.0, 10.0)] == 4);
    
      return 0;
    }
    

    正如我之前所说,如果您希望使用其他结构,则必须同时调整 pairHash 类和 pairEquals 结构 operator() 以分别适当地散列和比较新键。

    干杯

    编辑:

    • 修改代码以使用自定义 TPPoint3D 类和统一仿函数类定义(均使用结构)。
    • 添加了简单的测试来验证哈希和等于函子。

    【讨论】:

    • 感谢您的回复,这可能就是我想要的。 3D 的最佳扩展是什么:(h1^(h2&lt;&lt;1))^h3
    • @Denis:是的,我认为这可行。但是,我认为@Timothy Shields 的解决方案更加优雅且易于扩展。我自己不知道std::tuple&lt;&gt; 已经自动定义了相等和比较函数。你只需要创建你的元组映射,它只适用于 2D、3D、...、nD :) 不过,请注意 Timothy 提到查找必须是 exact 的事实,这意味着元组中的所有项目都必须完全相同(值),才能将键视为相等,并且地图才能按预期工作。
    • @AndreNevas 感谢您的回复。也许我错过了一些东西,但我不知道如何将他的解决方案应用于任意给定的类(std::tuple 除外)。
    • @Denis:他说的是std::tuple自动使用它包含的类型的相等和散列函数来执行元组本身 作为一个整体。如果您使用自定义类作为元组的元素,我猜您必须自己为该类提供相等和散列函数。对于double(我们认为您需要的),std::tuple 将开箱即用,因为它是内置类型。
    • 抱歉不清楚,我的自定义类实际上类似于class TPoint(),例如,有point.xpoint.y。所以我想用它。我可以很容易地在std::map 中使用它,但如果我可以让它工作的话,我会转而使用unordered_map
    【解决方案3】:

    在 3D 情况下,std::unordered_map&lt;std::tuple&lt;double, double, double&gt;, your_value_type&gt; 应该适合您,假设您正在执行精确查找std::tuple&lt;...&gt; 为您定义相等和哈希函数,基于它正在聚合的类型的相等和哈希函数。

    2D 的情况当然是一样的,只是使用了std::tuple&lt;double, double&gt;

    编辑:抱歉提供错误信息。实际上没有为std::tuple 定义默认散列。要使用这种方法,您必须定义一个hash_tuple 模板仿函数类,然后在std::unordered_map 中使用它。其他答案显示了如何做这部分。

    【讨论】:

    • 感谢您的回复。可以查看已实现的 touple 哈希,还是未披露此信息?我将此应用于第三方库中已经编写的类,所以我不想使用 std::tuple。
    • 这是迄今为止提出的最佳解决方案,您基本上只需要一行来定义整个数据结构。
    • @TimothyShields 在另一个上下文中尝试了您的解决方案,似乎默认情况下不提供哈希:static assertion failed: std::hash is not specialized for this type,至少对于std::unordered_map&lt;std::tuple&lt;unsigned int, unsigned int&gt;,double &gt;
    • @Denis 是的,从那以后我学到了同样的东西。只为元组提供比较。定义一个 hash_tuple 模板函子并使​​用它并不难。其他答案建议如何实现哈希。
    • @TimothyShields 当然,实施起来并不难。我实际上使用了第一个答案,但只是想确保我没有忽略某些东西,并且确实没有元组的默认哈希
    【解决方案4】:

    我无法评论 Andre 的回答,因为我还没有足够的声誉,但是任何尝试使用 ^ (XOR) 创建散列函数的人都应该注意 XOR 是关联的。换句话说a ^ (b ^ c) == (a ^ b) ^ c。这意味着

    (h1 ^ (h2 << 1)) ^ h3
    

    这是安德烈的答案的返回值,与:

    h1 ^ ((h2 << 1) ^ h3)
    

    它本身就是,由于 XOR (a ^ b == b ^ a) 的交换性质,相当于:

    (h3 ^ (h2 << 1)) ^ h1
    

    所有这一切意味着我所指的哈希方法对于不同的abc,会为(a,b,c) 返回与(c,b,a) 相同的哈希。换句话说,x 和 z 坐标是顺序无关的/不敏感的。

    根据您使用此哈希方法的方式,这可能不是问题。但是,例如,如果您正在散列的点与网格对齐,您将收到大量的散列冲突。

    我会将 Andre 的答案中的 return 语句中的表达式替换为下面的表达式。这应该取决于订单/敏感。

    (h1 ^ (h2 << 1)) ^ (h3 << 2)
    

    【讨论】:

    • 我也看不出int hashFunc(int h1,int h2,int h3){return h1 ^ (h2&lt;&lt;1) ^ (h3&lt;&lt;2);} 将如何作为散列函数工作。因为hash(4,0,0) = 4hash(0,2,0) = 4hash(0,0,1) = 4hash(4,2,1) = 4 等...所以有很多坐标(在移位/异或模式中)会给出相同的哈希值。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-04
    • 1970-01-01
    • 2018-07-07
    • 1970-01-01
    • 2012-03-06
    • 2018-05-25
    相关资源
    最近更新 更多