有限欧氏空间中二维点的哈希函数

时间:2014-02-04 09:46:10

标签: java hash hashcode

我将大量具有地理位置的对象存储为以米为单位的2D点(x,y)。为了代表世界,我使用的网格划分为1平方公里的单元格。目前我正在使用HashMap<Position, Object>。任何其他地图或适当的数据结构都可以,但我的解决方案是有效的,所以我只对解决细节感兴趣。

我一直在阅读很多关于制作好的哈希函数的内容,特别是2D点。到目前为止,没有任何解决方案真的很好(尽可能无碰撞地评价)。

为了测试一些想法,我写了一个非常简单的java程序,为从任意数字(-1000,-1000)到(1000,1000)(x1,y1 - &gt; x2,y2)的点生成哈希码并存储他们在HashSet<Integer>中,这是我的结果:

# java HashTest
4000000 number of unique positions
test1: 3936031 (63969 buckets, 1,60%) collisions using Objects.hash(x,y)
test2: 0 (4000000 buckets, 100,00%) collisions  using (x << 16) + y
test3: 3998000 (2000 buckets, 0,05%) collisions using x
test4: 3924037 (75963 buckets, 1,90%) collisions using x*37 + y
test5: 3996001 (3999 buckets, 0,10%) collisions using x*37 + y*37
test6: 3924224 (75776 buckets, 1,89%) collisions using x*37 ^ y
test7: 3899671 (100329 buckets, 2,51%) collisions using x*37 ^ y*37
test8: 0 (4000000 buckets, 100,00%) collisions using PerfectlyHashThem
test9: 0 (4000000 buckets, 100,00%) collisions using x << 16 | (y & 0xFFFF)

图例:碰撞次数,存储量(碰撞次数),perc(碰撞次数)

这些散列函数中的大多数都表现得很糟糕。事实上,唯一的好处是将x移位到整数的前16位。我想,限制是两个最远点不得超过Integer.MAX_INT的平方根,即面积必须小于46 340平方公里。

这是我的测试函数(只为每个新的哈希函数复制):

  public void test1() {

    HashSet<Integer> hashCodes = new HashSet<Integer>();
    int collisions = 0;

    for (int x = -MAX_VALUE; x < MAX_VALUE; ++x) {
      for (int y = -MAX_VALUE; y < MAX_VALUE; ++y) {
        final int hashCode = Objects.hash(x,y);

        if (hashCodes.contains(hashCode))
          collisions++;

        hashCodes.add(hashCode);
      }
    }

    System.console().format("test1: %1$s (%2$s buckets, %3$.2f%%) collisions using Objects.hash(x,y)\n", collisions, buckets(collisions), perc(collisions));
  }

我在这里错了吗?我应该微调素数以获得更好的结果吗?

编辑:

添加了更多哈希函数(test8和test9)。 test8来自@nawfal在Mapping two integers to one, in a unique and deterministic way的响应(从short转换为int)。

2 个答案:

答案 0 :(得分:1)

public void test1() {

    int MAX_VALUE = 1000;

    HashSet<Integer> hashCodes = new HashSet<Integer>();
    int collisions = 0;

    for (int x = -MAX_VALUE; x < MAX_VALUE; ++x) {
        for (int y = -MAX_VALUE; y < MAX_VALUE; ++y) {
            final int hashCode = ((x+MAX_VALUE)<<16)|((y+MAX_VALUE)&0xFFFF);

            if (hashCodes.contains(hashCode))
                collisions++;

            hashCodes.add(hashCode);
        }
    }

    System.out.println("Collisions: " + collisions + " // Buckets: " +  hashCodes.size());
}

打印:碰撞:0 //铲斗:4000000

答案 1 :(得分:1)

我是一个类似的问题,答案是使用Cantor配对功能。这里: Mapping two integers to one, in a unique and deterministic way

Cantor配对函数也可以用于负整数,使用双射。