我想要一个带有长数(64位)的哈希函数并产生10位的结果。为此目的,最好的哈希函数是什么。输入基本上是变量的地址(地址在Linux上是64位或8字节),所以我的哈希函数应该为此目的进行优化。
答案 0 :(得分:6)
我会说像这样的事情:
uint32_t hash(uint64_t x)
{
x >>= 3;
return (x ^ (x>>10) ^ (x>>20)) & 0x3FF;
}
至少有效3位不是很有用,因为大多数变量是4字节或8字节对齐,所以我们删除它们。 然后我们采用接下来的30位并将它们混合在一起(XOR),每块10位。
当然,您也可以选择(x>>30)^(x>>40)^(x>>50)
,但我不确定它们在实践中是否会有所不同。
答案 1 :(得分:1)
对于大多数发行版而言,最好是按素数修改,1021是最大的10位素数。没有必要剥离低位。
static inline int hashaddress(void *v)
{
return (uintptr_t)v % 1021;
}
如果您认为性能可能是一个问题,请手头有一些替补,并在实际程序中将它们与进行比赛。微型车标志是浪费;几个周期的差异几乎肯定会被缓存效应所淹没,并且尺寸很重要。
答案 2 :(得分:1)
我写了一个玩具程序来查看堆栈,数据区和堆上的一些真实地址。基本上我宣布4个全局,4个本地人并且做了2个mallocs
。打印地址时,我丢弃了最后两位。以下是其中一个运行的 输出:
20125e8
20125e6
20125e7
20125e4
3fef2131
3fef2130
3fef212f
3fef212c
25e4802
25e4806
这告诉我的是什么:
我接下来会做什么:
/* Drop two LSBs. */
a >>= 2;
/* Get rid of the MSBs. Keep 46 bits. */
a &= 0x3fffffffffff;
/* Get the 14 MSBs and fold them in to get a 32 bit integer.
The MSBs are mostly 0s anyway, so we don't lose much entropy. */
msbs = (a >> 32) << 18;
a ^= msbs;
现在我们通过decent 'half avalanche' hash function,传递此内容,而不是滚动我们自己的。 “半雪崩”意味着输入的每一位都有可能影响同一位置的位和更高:
uint32_t half_avalanche( uint32_t a)
{
a = (a+0x479ab41d) + (a<<8);
a = (a^0xe4aa10ce) ^ (a>>5);
a = (a+0x9942f0a6) - (a<<14);
a = (a^0x5aedd67d) ^ (a>>3);
a = (a+0x17bea992) + (a<<7);
return a;
}
对于10位散列,请使用返回的uint32_t
的10个MSB。如果您为N
位哈希选择N
MSB,则哈希函数继续正常,有效地将每个附加位的桶计数加倍。
我有点无聊,所以我为此写了一个玩具基准。 没什么好看的,它在堆上分配一堆内存并尝试哈希我如上所述。来源可以来自 here 。一个示例结果:
1024个桶,产生256个值,29个碰撞
1024个桶,生成512个值,103个转换
1024个桶,生成1024个值,370个收获
下一步:我尝试了另外两个哈希在这里回答。他们都有类似的表现。看起来像:选择最快的一个;)