我有一个存储四叉树条目的哈希表。
哈希函数如下所示:
四叉树哈希
#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
对于截断为 32 位 = 16 字节的 4 个指针,FNV 散列需要27 个周期(手工组装)
不幸的是,这会导致 81% 的散列冲突,而应该是 37%。
运行完整的 15 次乘法需要 179 个周期。