我有一个存储四叉树条目的哈希表。
哈希函数如下:
四叉树哈希
#define node_hash(a,b,c,d) \
(((int)(d))+3*(((int)(c))+3*(((int)(b))+3*((int)(a))+3)))
请注意,此操作的结果始终使用模质数进行分块,如下所示:
h = node_hash(p->nw, p->ne, p->sw, p->se) ;
h %= hashprime ;
...
与最优哈希的比较一些统计分析表明,这种哈希在减少冲突方面是最优的。
给定一个具有
b
个桶和n
个条目的哈希表。使用完美哈希的碰撞风险为:(n - b * (1 - power((b-1)/b,n)))) * 100 / n
当n = b时,这意味着37%的碰撞风险。
一些测试表明,上述哈希与规范非常匹配(对于哈希表的所有填充级别)。
运行时间运行时间严重依赖于
hashprime
的值。
计时结果(1000次运行中最佳)如下:
hashprime CPU-cycles per run
--------------------------------
4049 56
16217 68
64871 127 <-- whoooh
有没有一种方法可以在保持最佳碰撞风险的同时改进它?
可以通过优化模操作(用计算机外部的“神奇”数字替换为乘法)来实现。或者使用其他哈希函数替换哈希函数。
背景
生成以下汇编代码:
//--------h = node_hash(p->nw, p->ne, p->sw, p->se) ;
mov eax,[rcx+node.nw] <<+
lea eax,[eax+eax*2+3] |
add eax,[rcx+node.ne] |
lea eax,[eax+eax*2] +- takes +/- 12 cycles
add eax,[rcx+node.sw] |
lea eax,[eax+eax*2] |
add eax,[rcx+node.se] <<+
//--------h %= hashprime ;
mov esi,[hashprime]
xor edx,edx
div esi
mov rax,rdx <<--- takes all the rest
[编辑]
我可能可以利用以下事实:
C = A % B
等价于 C = A - B * (A / B)
利用整数除法等同于乘以其倒数的事实。
因此将公式转换为 C = A - B * (A * rB)
注意,对于整数除法,倒数是神奇的数字,请参见:http://www.hackersdelight.org/magic.htm
C代码在此处:http://web.archive.org/web/20070713211039/http://hackersdelight.org/HDcode/magic.c
[FNV哈希]
请参见:http://www.isthe.com/chongo/tech/comp/fnv/#FNV-1a
hash = offset_basis
for each byte to be hashed
hash = hash xor octet_of_data
hash = hash * FNV_prime (for 32 bits = 16777619)
return hash
如果将4个指针截断为32位(即16字节),FNV哈希需要27个周期(手工汇编)。
不幸的是,这导致哈希冲突率达到81%,而应该是37%。
运行完整的15次乘法需要179个周期。
unsigned short
或unsigned char
,但它的大小可能会限制这种可能性,因为它是哈希表的大小。 - anthonyvdlea
习惯用语具有比d+c3+b9+c*27更多的依赖性,这可能会更快。不过,“a,b,c,d”的范围是多少?了解这一点可能有助于模数缩减。 - Aki Suihkonen+3
是没有用的,因为你接下来会执行%= prime
。这实际上是将你的桶旋转了 3 个位置,但并不影响冲突。当x % prime == y % prime
时,也有x+3 % prime == y+3 % prime
。 - MSalters