C 64位到10位的哈希函数
我想要一个哈希函数,它接受一个长的数字(64位)并产生10位的结果。对于这样的目的,什么是最好的散列函数。输入基本上是变量的地址(在Linux上是64位或8字节的地址),因此我的哈希函数应该为此目的进行优化。我会这样说:C 64位到10位的哈希函数,c,linux,gcc,hash,x86-64,C,Linux,Gcc,Hash,X86 64,我想要一个哈希函数,它接受一个长的数字(64位)并产生10位的结果。对于这样的目的,什么是最好的散列函数。输入基本上是变量的地址(在Linux上是64位或8字节的地址),因此我的哈希函数应该为此目的进行优化。我会这样说: uint32_t hash(uint64_t x) { x >>= 3; return (x ^ (x>>10) ^ (x>>20)) & 0x3FF; } 最低有效的3位不是很有用,因为大多数变量是4字节或8字节对
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)
,但我不确定它们在实践中是否会有任何不同。对于大多数发行版来说,最好的方法是按素数进行修改,1021是最大的10位素数。没有必要去掉低位
static inline int hashaddress(void *v)
{
return (uintptr_t)v % 1021;
}
如果您认为性能可能是一个问题,那么手头上有一些备选方案,并在您的实际程序中进行比较。微基准是废物;几个周期的差异几乎肯定会被缓存效果和大小所淹没。我编写了一个玩具程序来查看堆栈、数据区和堆上的一些真实地址。基本上,我声明了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;
/*放下两个LSB*/
a>>=2;
/*清除MSB。保留46位*/
a&=0x3fffffffff;
/*获取14个MSB并将其折叠以获得32位整数。
MSB基本上都是0,所以我们不会损失太多的熵*/
msbs=(a>>32)您能为我们提供有关64位值在您的宇宙中的分布的哪些信息?没有适用于所有情况的“最佳”哈希函数。您必须研究输入数字的分布和特征。输入是Linux上变量的地址。@MetallicPrist:在这种情况下,您可以删除较低的4+位(假设所有内容都对齐),atm地址空间限制为47位,因此这意味着您只需要43位散列(如果你不想对未来非常安全)当然,这不会影响散列所有位,包括低熵的位(位0-2)或总是相同的位(用户空间中最顶端的16位,至少现在总是零).这样,在3-4年后,您就可以避免重复使用此函数的愚蠢错误,因为您忘记了最初设计此函数的原因和前提,将其用于不符合假设的情况。这样可以避免在哈希表性能差且没有性能的情况下浪费一周的时间而造成公众羞辱y可以解释原因。由于使用异或移位进行混合,我建议使用已知的275个三元组中的一个,其64x64矩阵中的周期为2^64-1,如Marsaglia所述,例如(7,11,10)或(21,17,48)。由于这是以伪随机方式混合位,没有已知的异常,因此在执行&0x3ff之前将所有字异或在一起是有效的。这样,每个输入位都应该有机会影响所有输出位。可能没有加密哈希中的50:50分布那么完美,但尽可能好。除此之外,仍然是好主意,+1
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;
}