我有一系列对象,其唯一不同的内部状态是2-d位置(2个整数)的固定长度列表(或其他)。也就是说,它们都具有相同数量的元素,具有(可能)不同的2-d值。
我将不断地将新实例与之前存在的所有实例进行比较,因此编写一个良好的散列函数以最大限度地减少比较次数非常重要。
你会如何推荐我哈希呢?
答案 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的结果。