什么是好的哈希函数?

时间:2008-08-29 16:15:37

标签: algorithm language-agnostic hash

什么是好的哈希函数?我在大学的数据结构课程中看到了很多哈希函数和应用程序,但我大多认为很难创建一个好的哈希函数。作为避免碰撞的经验法则,我的教授说:

function Hash(key)
  return key mod PrimeNumber
end

(mod是C和类似语言中的%运算符)

使用素数作为哈希表的大小。我觉得这是一个很好的功能,以避免碰撞和快速,但我怎么能做一个更好的?对于数字键,是否有更好的字符串键哈希函数?

8 个答案:

答案 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)

散列函数有两个主要目的:

  • 将数据点均匀地分散为n位。
  • 以安全地识别输入数据。

如果不知道你使用的是什么,就不可能推荐哈希。

如果您只是在程序中创建哈希表,那么您不必担心算法的可逆性或可破解性...... 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;
}

编辑:

  • Landon Curt Noll推荐使用his site FVN-1A算法优于原始FVN-1算法:改进算法更好地分散哈希中的最后一个字节。我相应地调整了算法。

答案 4 :(得分:3)

我会说主要的经验法则不是自己动手。尝试使用经过全面测试的东西,例如SHA-1或其他类似的东西。

答案 5 :(得分:1)

好的哈希函数具有以下属性:

  1. 鉴于消息的散列,攻击者在计算上无法找到另一条消息,使得它们的哈希值相同。

  2. 给定一对消息m'和m,找到两个这样的h(m)= h(m')

  3. 在计算上是不可行的。

    这两种情况相同。在第一种情况下,有一个预先存在的哈希,你正试图找到一个冲突。在第二种情况下,您试图找到任何两条冲突的消息。由于生日“悖论”,第二项任务变得非常容易。

    如果性能不是很大问题,则应始终使用安全散列函数。可以通过强制哈希中的冲突来执行非常聪明的攻击。如果你从一开始就使用强大的东西,你就可以抵御这些。

    不要在新设计中使用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)

一个好的哈希函数应该

  1. 两心一意,尽可能不丢失信息,并且冲突最少
  2. 尽可能均匀地级联,即每个输入位应以0.5的概率翻转每个输出位,并且没有明显的模式。
  3. 如果用于加密上下文中,则不应存在一种有效的方式来对其进行反转。
质数模量不满足这些点中的任何一个。这根本不够。它通常总比没有强,但它甚至不快。与一个无符号整数相乘并取2的幂可以很好地分配这些值,这一点也不好,但是只有大约2 cpu的周期,它比15至40的质数模数要快得多(是的,整数除法真的很慢)。

要创建一个快速散列函数并很好地分配值,最好的选择是像使用PCG进行随机数生成一样,使用质量较差的快速置换来组成散列函数。

有用的排列方式包括:

  • 不规则整数的乘法
  • 二进制旋转
  • xorshift

按照此食谱,我们可以创建自己的hash function,也可以采用经过splitmix测试和接受的{

如果需要加密质量,我强烈建议使用经过严格测试和标准化的sha家族功能,但是出于教育目的,这就是您要使用的功能:

首先,您需要使用良好的非加密哈希函数,然后在单数字段上应用诸如幂运算的单向函数,或者{{1}时许多k的应用程序在x {{1 }}是结果哈希中的位数。