二维位置列表的良好哈希函数?

时间:2010-10-14 14:17:32

标签: java hash hashcode

我有一系列对象,其唯一不同的内部状态是2-d位置(2个整数)的固定长度列表(或其他)。也就是说,它们都具有相同数量的元素,具有(可能)不同的2-d值。

我将不断地将新实例与之前存在的所有实例进行比较,因此编写一个良好的散列函数以最大限度地减少比较次数非常重要。

你会如何推荐我哈希呢?

3 个答案:

答案 0 :(得分:6)

选择31的重点是你的素数能够使用位移和减法相乘。

让我们说这是一个Point类:

class Point {
    public final int x;
    public final int y;

    public Point(int x, int y)
    {
        this.x = x;
        this.y = y;
    }

    @Override
    public int hashCode()
    {
        int hash = 17;
        hash = ((hash + x) << 5) - (hash + x);
        hash = ((hash + y) << 5) - (hash + y);
        return hash;
    }
}

选择31作为素数的要点是能够使用位移和单次减法运算相乘。请注意,5位移位相当于乘以32,减法使得相当于乘以31.这两个运算比单个真正的乘法更有效。

然后你的目标是:

class TheObject
{
    private final java.util.List<Point> points;

    public TheObject(List<Point> points)
    {
        this.points = points;
    }

    @Override
    public int hashCode()
    {
        int hash = 17;int tmp = 0;
        for (Point p : points)
        {
            tmp = (hash + p.hashCode());
            hash = (tmp << 5) - tmp;
        }
        return hash;
    }
}

答案 1 :(得分:1)

嗯,二元搜索树的内容怎么样?

在伪代码中进行比较:

position1 > position2 := 
   (position1.x > position2.x) || 
   ((position1.x == position2.x) && (position1.y > position2.y))

list1.x > list2.x := {
    for (i in 0...n) 
        if (list1[i] > list2[i]) return true;
        else if (list1[i] > list2[i]) return false;
    return false;
}

其中n当然是列表的长度。

我不是一个java-pro,我真的不知道标准库,但我想,你可以自己编写树。实现一个getID方法,该方法将尝试查找此列表或以其他方式插入它以及一个唯一的id,只需递增一个计数器就可以获得。

这样,你得到一个没有碰撞的ID(而不是哈希)。在最坏的情况下,比较2个列表是O(n),因此查找/插入是O(n) * O(log(m))(假设树是平衡的),其中m是所有列表的总数。

因此,在最坏的情况下,确定ID比散列更昂贵,但如上所述,结果保证是唯一的。

我对平均值几乎没什么说,因为你没有给出数字。实际上我很惊讶你不想直接比较,因为我预计2个位置相等的概率小于1%,因此列表比较约为O(1),因为你需要的概率比较5个条目真的很小。

此外,目前尚不清楚这些清单是否可变,因为如果它们是不可变的,那么成本应该不太重要。

答案 2 :(得分:0)

根据整数的大小,您可能希望将第一个坐标乘以最大可能坐标并添加第二个坐标。例如,如果X和Y为正且限制为256,则可以尝试X * 256 + Y作为哈希函数。如果X和Y也可以是负数,您可能希望首先抵消它们以使它们为非负数。此外,如果将X乘以max会溢出整数,则可能需要多int哈希值或者mod或bitwise,以及UINT_MAX的结果。