什么是好的哈希函数?我在大学的数据结构课程中看到了很多哈希函数和应用程序,但我大多认为很难创建一个好的哈希函数。作为避免碰撞的经验法则,我的教授说:
function Hash(key)
return key mod PrimeNumber
end
(mod是C和类似语言中的%运算符)
使用素数作为哈希表的大小。我觉得这是一个很好的功能,以避免碰撞和快速,但我怎么能做一个更好的?对于数字键,是否有更好的字符串键哈希函数?
答案 0 :(得分:51)
对于通用哈希来说,没有“良好的哈希函数”这样的东西(编辑。是的,我知道有“普遍哈希”之类的东西,但这不是我的意思)。根据上下文,不同的标准决定了散列的质量。两个人已经提到了SHA。这是一个加密哈希,它对你可能意味着的哈希表一点都不好。
哈希表的要求非常不同。但是,普遍找到一个好的散列函数很难,因为不同的数据类型会暴露可以散列的不同信息。根据经验,最好考虑类型保持的所有信息。这并不总是容易甚至可能。出于统计(以及因此碰撞)的原因,在问题空间(即所有可能的对象)上产生良好的分布也是重要的。这意味着当散列数在100到1050之间时,让最重要的数字在散列中起很大作用是没有好处的,因为对于~90%的对象,这个数字将为0.让最后三个数字更重要。数字确定哈希。
类似地,当散列字符串时,考虑所有字符是很重要的 - 除非事先知道所有字符串的前三个字符都是相同的;考虑到这些是浪费。
这实际上是我建议阅读Knuth在计算机编程艺术中所说的内容的案例之一。另一个好读物是Julienne Walker的The Art of Hashing。
答案 1 :(得分:32)
对于基本上任何类型的数据进行“正常”哈希表查找 - Paul Hsieh的这个是我用过的最好的。
http://www.azillionmonkeys.com/qed/hash.html
如果您关心加密安全或其他更高级的东西,那么YMMV。如果您只想要一个用于哈希表查找的kick ass通用哈希函数,那么这就是您要查找的内容。
答案 2 :(得分:9)
散列函数有两个主要目的:
如果不知道你使用的是什么,就不可能推荐哈希。
如果您只是在程序中创建哈希表,那么您不必担心算法的可逆性或可破解性...... SHA-1或AES对此完全没必要,您将是最好使用variation of FNV。 FNV比你提到的简单素数模型实现更好的色散(因此更少的碰撞),并且它更适合于不同的输入尺寸。
如果您使用哈希来隐藏和验证公共信息(例如哈希密码或文档),那么您应该使用公众审查后审查的主要哈希算法之一。 The Hash Function Lounge是一个很好的起点。
答案 3 :(得分:5)
这是一个很好的例子,也是你永远不想写一个的例子。 这是一个Fowler / Noll / Vo(FNV)Hash,它是计算机科学天才和纯巫术的平等部分:
unsigned fnv_hash_1a_32 ( void *key, int len ) {
unsigned char *p = key;
unsigned h = 0x811c9dc5;
int i;
for ( i = 0; i < len; i++ )
h = ( h ^ p[i] ) * 0x01000193;
return h;
}
unsigned long long fnv_hash_1a_64 ( void *key, int len ) {
unsigned char *p = key;
unsigned long long h = 0xcbf29ce484222325ULL;
int i;
for ( i = 0; i < len; i++ )
h = ( h ^ p[i] ) * 0x100000001b3ULL;
return h;
}
编辑:
答案 4 :(得分:3)
我会说主要的经验法则不是自己动手。尝试使用经过全面测试的东西,例如SHA-1或其他类似的东西。
答案 5 :(得分:1)
好的哈希函数具有以下属性:
鉴于消息的散列,攻击者在计算上无法找到另一条消息,使得它们的哈希值相同。
给定一对消息m'和m,找到两个这样的h(m)= h(m')
这两种情况不相同。在第一种情况下,有一个预先存在的哈希,你正试图找到一个冲突。在第二种情况下,您试图找到任何两条冲突的消息。由于生日“悖论”,第二项任务变得非常容易。
如果性能不是很大问题,则应始终使用安全散列函数。可以通过强制哈希中的冲突来执行非常聪明的攻击。如果你从一开始就使用强大的东西,你就可以抵御这些。
不要在新设计中使用MD5或SHA-1。包括我在内的大多数密码学家会认为它们已被破坏。这两种设计的主要弱点在于,我在上面概述的第二个属性并不适用于这些结构。如果攻击者可以生成两个消息m和m',它们都散列到相同的值,则可以使用这些消息。 SHA-1和MD5也遭受邮件扩展攻击,如果你不小心,可能会致命地削弱你的应用程序。
像Whirpool这样更现代的哈希是更好的选择。它不受这些消息扩展攻击的影响,并使用与AES相同的数学来证明针对各种攻击的安全性。
希望有所帮助!
答案 6 :(得分:1)
你在这里说的是你想要一个使用具有抗冲击性的产品。尝试使用SHA-2。或者尝试在单向压缩功能中使用(好)分组密码(之前从未尝试过),例如Miyaguchi-Preenel模式中的AES。问题是你需要:
1)有一个IV。尝试使用Khinchin常数的小数部分的前256位或类似的东西。
2)有一个填充方案。简单。从像MD5或SHA-3(Keccak [发音为'ket-chak'])这样的哈希中删除它。
如果您不关心安全性(其他一些人说这个),请查看Bob Jenkins的FNV或lookup2(实际上我是第一个推荐lookup2的人)同时尝试MurmurHash,它很快(检查一下:.16 cpb )。
答案 7 :(得分:0)
一个好的哈希函数应该
要创建一个快速散列函数并很好地分配值,最好的选择是像使用PCG进行随机数生成一样,使用质量较差的快速置换来组成散列函数。
有用的排列方式包括:
按照此食谱,我们可以创建自己的hash function,也可以采用经过splitmix测试和接受的{3}}。
如果需要加密质量,我强烈建议使用经过严格测试和标准化的sha家族功能,但是出于教育目的,这就是您要使用的功能:
首先,您需要使用良好的非加密哈希函数,然后在单数字段上应用诸如幂运算的单向函数,或者{{1}时许多k
的应用程序在x {{1 }}是结果哈希中的位数。