精华内容
下载资源
问答
  • Hash函数

    2020-05-18 22:29:51
    Hash函数按照其安全性可分为(弱)无碰撞的Hash函数和强无碰撞的Hash函数。通常说的无碰撞指的是强无碰撞。 Hash函数HASH概念:2.SHA5123.WHIRLPOOL 算法 HASH概念: 长消息生成固定长度的短字符串。 设 H 表示一个...

    Hash函数按照其安全性可分为(弱)无碰撞的Hash函数和强无碰撞的Hash函数。通常说的无碰撞指的是强无碰撞。

    HASH概念:

    长消息生成固定长度的短字符串。
    设 H 表示一个散列函数, Γ (大写伽马)是输入长度的上界, γ (小写伽马)是比Γ小得多的固定的输出长度

    • 强无碰撞:很难找到两个消息x和y具有相同的散列,即H(x) = H(y)
    • 无碰撞:给定一个消息 x,满足|x| ≤ Γ, 找到另一个不同的消息y (|y| ≤ Γ)与x具有相同的散列值,即H(x) = H(y),是困难的
      注:不满足强无碰撞性,并不意味着不满足无碰撞性。也就是说,强无碰撞性最容易被攻破。

    比如,MD5 (γ =128bit)已被证明不能满足强无碰撞性,
    MD5(M1)=MD5(M2),即给定消息M1,能够计算获取M2,使得M2产生的散列值与M1产生的散列值相同。
    但还未被证明不满足无碰撞性。


    当前主流散列函数SHA-512(γ =512bit)、WHIRLPOOL(γ =512bit).它们的共性特定是输入2个值输出一个值,输出的值是最后一轮运算的hash值。

    2.SHA512

    输入512bitIV和切分成1024bit明文块的明文,输出512bit。

    其中,SHA-512算法如下:
    设 X = X1X2…Xk, Y = Y1Y2…Yk 是二进制串, 其中 Xi,Yi 是 l-bit 字符串. 定义如下l-bit的异或运算:
    在这里插入图片描述

    SHA512(M)=HN SHA-512(M)=H_{N}

    在这里插入图片描述
    i =1,2,…,N
    其中Mi长度1024bit, Hi长度512bit, F(压缩)函数输出512bit, N 为M 分块的个数

    3.WHIRLPOOL 算法

    输入:切分成 512-bit 明文块的明文 和 512-bit密钥K
    输出: 512-bit 输出

    WHIRLPOOL的压缩函数定义为:
    在这里插入图片描述
    在这里插入图片描述
    i =1,2,…,N。其中,各种块的长度均为512bit。

    展开全文
  • hash函数

    2016-04-17 11:25:53
    1、hash函数广泛用于密码学中,比如银行客户的密码通过函数函数转换得到一个加密过的新密码,把这个信息密码保存在数据库中,每次客户登录时,都会将密码经过hash算法转换后,再次与数据库中的密码进行比较。...

    1、hash函数广泛用于密码学中,比如银行客户的密码通过函数函数转换得到一个加密过的新密码,把这个信息密码保存在数据库中,每次客户登录时,都会将密码经过hash算法转换后,再次与数据库中的密码进行比较。由于hash算法是不可逆的,所以可以保证即使得到了数据库中的密码,也无法破解得到原先的密码。

    2、hash还可以用来搜索,即用生成的hash值来表示所存储内容的存储器中的位置,这样可以将搜索的时间复杂度降低到O(1)。

    下面这些内容均为转载:

    所有内容均来自于:http://www.partow.net/programming/hashfunctions/

    以下总共给出了10种字符串hash函数,在上面的链接中可以找到各个Hash函数的描述,以及一些其它的关于hash函数的资料下载。

    这些hash函数各自的优缺点不详。其中,ELFHash函数是在unix系统中被广泛使用的,也是《算法艺术与信息学竞赛》中作者推荐的一个字符串hash函数。DEKHash函数是Knuth在其《The Art of Computer Programming》第三卷中引入的。

    [cpp] view plain copy
    1. unsigned int RSHash(char* str, unsigned int len)  
    2. {  
    3.    unsigned int b    = 378551;  
    4.    unsigned int a    = 63689;  
    5.    unsigned int hash = 0;  
    6.    unsigned int i    = 0;  
    7.    for(i = 0; i < len; str++, i++)  
    8.    {  
    9.       hash = hash * a + (*str);  
    10.       a    = a * b;  
    11.    }  
    12.    return hash;  
    13. }  
    14. /* End Of RS Hash Function */  
    15.   
    16. unsigned int JSHash(char* str, unsigned int len)  
    17. {  
    18.    unsigned int hash = 1315423911;  
    19.    unsigned int i    = 0;  
    20.    for(i = 0; i < len; str++, i++)  
    21.    {  
    22.       hash ^= ((hash << 5) + (*str) + (hash >> 2));  
    23.    }  
    24.    return hash;  
    25. }  
    26. /* End Of JS Hash Function */  
    27.   
    28. unsigned int PJWHash(char* str, unsigned int len)  
    29. {  
    30.    const unsigned int BitsInUnsignedInt = (unsigned int)(sizeof(unsigned int) * 8);  
    31.    const unsigned int ThreeQuarters     = (unsigned int)((BitsInUnsignedInt  * 3) / 4);  
    32.    const unsigned int OneEighth         = (unsigned int)(BitsInUnsignedInt / 8);  
    33.    const unsigned int HighBits          = (unsigned int)(0xFFFFFFFF) << (BitsInUnsignedInt - OneEighth);  
    34.    unsigned int hash              = 0;  
    35.    unsigned int test              = 0;  
    36.    unsigned int i                 = 0;  
    37.    for(i = 0; i < len; str++, i++)  
    38.    {  
    39.       hash = (hash << OneEighth) + (*str);  
    40.       if((test = hash & HighBits)  != 0)  
    41.       {  
    42.          hash = (( hash ^ (test >> ThreeQuarters)) & (~HighBits));  
    43.       }  
    44.    }  
    45.    return hash;  
    46. }  
    47. /* End Of  P. J. Weinberger Hash Function */  
    48.   
    49. unsigned int ELFHash(char* str, unsigned int len)  
    50. {  
    51.    unsigned int hash = 0;  
    52.    unsigned int x    = 0;  
    53.    unsigned int i    = 0;  
    54.    for(i = 0; i < len; str++, i++)  
    55.    {  
    56.       hash = (hash << 4) + (*str);  
    57.       if((x = hash & 0xF0000000L) != 0)  
    58.       {  
    59.          hash ^= (x >> 24);  
    60.       }  
    61.       hash &= ~x;  
    62.    }  
    63.    return hash;  
    64. }  
    65. /* End Of ELF Hash Function */  
    66.   
    67. unsigned int BKDRHash(char* str, unsigned int len)  
    68. {  
    69.    unsigned int seed = 131; /* 31 131 1313 13131 131313 etc.. */  
    70.    unsigned int hash = 0;  
    71.    unsigned int i    = 0;  
    72.    for(i = 0; i < len; str++, i++)  
    73.    {  
    74.       hash = (hash * seed) + (*str);  
    75.    }  
    76.    return hash;  
    77. }  
    78. /* End Of BKDR Hash Function */  
    79.   
    80. unsigned int SDBMHash(char* str, unsigned int len)  
    81. {  
    82.    unsigned int hash = 0;  
    83.    unsigned int i    = 0;  
    84.    for(i = 0; i < len; str++, i++)  
    85.    {  
    86.       hash = (*str) + (hash << 6) + (hash << 16) - hash;  
    87.    }  
    88.    return hash;  
    89. }  
    90. /* End Of SDBM Hash Function */  
    91.   
    92. unsigned int DJBHash(char* str, unsigned int len)  
    93. {  
    94.    unsigned int hash = 5381;  
    95.    unsigned int i    = 0;  
    96.    for(i = 0; i < len; str++, i++)  
    97.    {  
    98.       hash = ((hash << 5) + hash) + (*str);  
    99.    }  
    100.    return hash;  
    101. }  
    102. /* End Of DJB Hash Function */  
    103.   
    104. unsigned int DEKHash(char* str, unsigned int len)  
    105. {  
    106.    unsigned int hash = len;  
    107.    unsigned int i    = 0;  
    108.    for(i = 0; i < len; str++, i++)  
    109.    {  
    110.       hash = ((hash << 5) ^ (hash >> 27)) ^ (*str);  
    111.    }  
    112.    return hash;  
    113. }  
    114. /* End Of DEK Hash Function */  
    115.   
    116. unsigned int BPHash(char* str, unsigned int len)  
    117. {  
    118.    unsigned int hash = 0;  
    119.    unsigned int i    = 0;  
    120.    for(i = 0; i < len; str++, i++)  
    121.    {  
    122.       hash = hash << 7 ^ (*str);  
    123.    }  
    124.    return hash;  
    125. }  
    126. /* End Of BP Hash Function */  
    127.   
    128. unsigned int FNVHash(char* str, unsigned int len)  
    129. {  
    130.    const unsigned int fnv_prime = 0x811C9DC5;  
    131.    unsigned int hash      = 0;  
    132.    unsigned int i         = 0;  
    133.    for(i = 0; i < len; str++, i++)  
    134.    {  
    135.       hash *= fnv_prime;  
    136.       hash ^= (*str);  
    137.    }  
    138.    return hash;  
    139. }  
    140. /* End Of FNV Hash Function */  
    141.   
    142. unsigned int APHash(char* str, unsigned int len)  
    143. {  
    144.    unsigned int hash = 0xAAAAAAAA;  
    145.    unsigned int i    = 0;  
    146.    for(i = 0; i < len; str++, i++)  
    147.    {  
    148.       hash ^= ((i & 1) == 0) ? (  (hash <<  7) ^ (*str) * (hash >> 3)) :  
    149.                                (~((hash << 11) + (*str) ^ (hash >> 5)));  
    150.    }  
    151.    return hash;  
    152. }  
    153. /* End Of AP Hash Function */  

    展开全文
  • HASH函数

    2013-12-17 10:10:47
    HASH函数 计算理论中,没有Hash函数的说法,只有单向函数的说法。所谓的单向函数,是一个复杂的定义,大家可以去看计算理论或者密码学方面的数据。用“人类”的语言描述单向函数就是:如果某个函数在给定输入...
    HASH函数



    计算理论中,没有Hash函数的说法,只有单向函数的说法。所谓的单向函数,是一个复杂的定义,大家可以去看计算理论或者密码学方面的数据。用“人类”的语言描述单向函数就是:如果某个函数在给定输入的时候,很容易计算出其结果来;而当给定结果的时候,很难计算出输入来,这就是单项函数。各种加密函数都可以被认为是单向函数的逼近。Hash函数(或者成为散列函数)也可以看成是单向函数的一个逼近。即它接近于满足单向函数的定义。

    Hash函数还有另外的含义。实际中的Hash函数是指把一个大范围映射到一个小范围。把大范围映射到一个小范围的目的往往是为了节省空间,使得数据容易保存。除此以外,Hash函数往往应用于查找上。所以,在考虑使用Hash函数之前,需要明白它的几个限制:

    1. Hash的主要原理就是把大范围映射到小范围;所以,你输入的实际值的个数必须和小范围相当或者比它更小。不然冲突就会很多。
    2. 由于Hash逼近单向函数;所以,你可以用它来对数据进行加密。
    3. 不同的应用对Hash函数有着不同的要求;比如,用于加密的Hash函数主要考虑它和单项函数的差距,而用于查找的Hash函数主要考虑它映射到小范围的冲突率。

    应用于加密的Hash函数已经探讨过太多了,在作者的博客里面有更详细的介绍。所以,本文只探讨用于查找的Hash函数。

    Hash函数应用的主要对象是数组(比如,字符串),而其目标一般是一个int类型。以下我们都按照这种方式来说明。

    一般的说,Hash函数可以简单的划分为如下几类:
    1. 加法Hash;
    2. 位运算Hash;
    3. 乘法Hash;
    4. 除法Hash;
    5. 查表Hash;
    6. 混合Hash;
    下面详细的介绍以上各种方式在实际中的运用。


    一 加法Hash


    所谓的加法Hash就是把输入元素一个一个的加起来构成最后的结果。标准的加法Hash的构造如下:

    static int additiveHash(String key, int prime)
    {
    int hash, i;
    for (hash = key.length(), i = 0; i < key.length(); i++)
    hash += key.charAt(i);
    return (hash % prime);
    }
    这里的prime是任意的质数,看得出,结果的值域为[0,prime-1]。


    二 位运算Hash


    这类型Hash函数通过利用各种位运算(常见的是移位和异或)来充分的混合输入元素。比如,标准的旋转Hash的构造如下:

    static int rotatingHash(String key, int prime)
    {
    int hash, i;
    for (hash=key.length(), i=0; i<key.length(); ++i)
    hash = (hash<<4)^(hash>>28)^key.charAt(i);
    return (hash % prime);
    }

    先移位,然后再进行各种位运算是这种类型Hash函数的主要特点。比如,以上的那段计算hash的代码还可以有如下几种变形:
    1. hash = (hash<<5)^(hash>>27)^key.charAt(i);
    2. hash += key.charAt(i);
    hash += (hash << 10);
    hash ^= (hash >> 6);
    3. if((i&1) == 0)
    {
    hash ^= (hash<<7) ^ key.charAt(i) ^ (hash>>3);
    }
    else
    {
    hash ^= ~((hash<<11) ^ key.charAt(i) ^ (hash >>5));
    }
    4. hash += (hash<<5) + key.charAt(i);
    5. hash = key.charAt(i) + (hash<<6) + (hash>>16) – hash;
    6. hash ^= ((hash<<5) + key.charAt(i) + (hash>>2));


    三 乘法Hash


    这种类型的Hash函数利用了乘法的不相关性(乘法的这种性质,最有名的莫过于平方取头尾的随机数生成算法,虽然这种算法效果并不好)。比如,

    static int bernstein(String key)
    {
    int hash = 0;
    int i;
    for (i=0; i<key.length(); ++i) hash = 33*hash + key.charAt(i);
    return hash;
    }

    jdk5.0里面的String类的hashCode()方法也使用乘法Hash。不过,它使用的乘数是31。推荐的乘数还有:131, 1313, 13131, 131313等等。

    使用这种方式的著名Hash函数还有:
    // 32位FNV算法
    int M_SHIFT = 0;
    public int FNVHash(byte[] data)
    {
    int hash = (int)2166136261L;
    for(byte b : data)
    hash = (hash * 16777619) ^ b;
    if (M_SHIFT == 0)
    return hash;
    return (hash ^ (hash >> M_SHIFT)) & M_MASK;
    }

    以及改进的FNV算法:
    public static int FNVHash1(String data)
    {
    final int p = 16777619;
    int hash = (int)2166136261L;
    for(int i=0;i<data.length();i++)
    hash = (hash ^ data.charAt(i)) * p;
    hash += hash << 13;
    hash ^= hash >> 7;
    hash += hash << 3;
    hash ^= hash >> 17;
    hash += hash << 5;
    return hash;
    }

    除了乘以一个固定的数,常见的还有乘以一个不断改变的数,比如:
    static int RSHash(String str)
    {
    int b = 378551;
    int a = 63689;
    int hash = 0;

    for(int i = 0; i < str.length(); i++)
    {
    hash = hash * a + str.charAt(i);
    a = a * b;
    }
    return (hash & 0x7FFFFFFF);
    }

    虽然Adler32算法的应用没有CRC32广泛,不过,它可能是乘法Hash里面最有名的一个了。关于它的介绍,大家可以去看RFC 1950规范。


    四 除法Hash


    除法和乘法一样,同样具有表面上看起来的不相关性。不过,因为除法太慢,这种方式几乎找不到真正的应用。需要注意的是,我们在前面看到的hash的结果除以一个prime的目的只是为了保证结果的范围。如果你不需要它限制一个范围的话,可以使用如下的代码替代”hash%prime”: hash = hash ^ (hash>>10) ^ (hash>>20)。


    五 查表Hash


    查表Hash最有名的例子莫过于CRC系列算法。虽然CRC系列算法本身并不是查表,但是,查表是它的一种最快的实现方式。下面是CRC32的实现:

    static int crctab[256] = {
    0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
    0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
    };
    int crc32(String key, int hash)
    {
    int i;
    for (hash=key.length(), i=0; i<key.length(); ++i)
    hash = (hash >> 8) ^ crctab[(hash & 0xff) ^ k.charAt(i)];
    return hash;
    }

    查表Hash中有名的例子有:Universal Hashing和Zobrist Hashing。他们的表格都是随机生成的。


    六 混合Hash


    混合Hash算法利用了以上各种方式。各种常见的Hash算法,比如MD5、Tiger都属于这个范围。它们一般很少在面向查找的Hash函数里面使用。


    七 对Hash算法的评价


    http://www.burtleburtle.net/bob/hash/doobs.html 这个页面提供了对几种流行Hash算法的评价。我们对Hash函数的建议如下:

    1. 字符串的Hash。最简单可以使用基本的乘法Hash,当乘数为33时,对于英文单词有很好的散列效果(小于6个的小写形式可以保证没有冲突)。复杂一点可以使用FNV算法(及其改进形式),它对于比较长的字符串,在速度和效果上都不错。

    2. 长数组的Hash。可以使用http://burtleburtle.net/bob/c/lookup3.c这种算法,它一次运算多个字节,速度还算不错。


    八 后记


    本文简略的介绍了一番实际应用中的用于查找的Hash算法。Hash算法除了应用于这个方面以外,另外一个著名的应用是巨型字符串匹配(这时的Hash算法叫做:rolling hash,因为它必须可以滚动的计算)。设计一个真正好的Hash算法并不是一件容易的事情。做为应用来说,选择一个适合的算法是最重要的。
    展开全文
  • Hash 函数

    2015-05-04 14:58:45
    散列表,它时给予快速存取的角度设计的,也是一种典型的“空间换时间”的做法。顾名思义,该数据可以理解为一个线性表,但是其中的元素...这个影色好函数叫做散列函数,存放纪录的数组叫做散列表。 比如我们存储7
    散列表,它时给予快速存取的角度设计的,也是一种典型的“空间换时间”的做法。顾名思义,该数据可以理解为一个线性表,但是其中的元素不是紧密排列的,而是可能存在空隙。

    散列表(Hash table,也叫哈希表),是根据关键码值儿直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置访问记录,以加快查找的速度。这个影色好函数叫做散列函数,存放纪录的数组叫做散列表。

    比如我们存储70个元素,但我们可能为70元素申请了100个元素的空间。70/100=0.7,这个数字称为负载因子。
    注释:我们之所以这样做,也是为了“快速存取”的目的。

    我们基于一种结果尽可能随机平均分布的固定函数H为每个元素安排存储位置,这样可以避免便利性质的先行搜索,以达到快速存取。但是由于此随机性,也必然会导致一个问题就是冲突。所谓冲突,即两个元素通过散裂函数H得到的地址形同,那么这两个元素称为“同义词”。这类似于70个人去一个又100个椅子的饭店吃饭。散裂函数的计算结果是一个存储单位地址,每个存储单位称为“桶”。设一个散裂表有m个桶,则散列表函数的值域应为[0,m-1]。

    解决冲突时一个复杂问题。冲突主要取决于:

    1)散裂函数,一个好的散列函数的值应尽可能平均分布
    2)处理冲突方法
    3)负载因子的大小。太大浪费空间严重,负载因子和散列函数是联动的


    解决冲突的方法:


    1)线性探查法:冲突后,线性向前试探,找到最近的一个空位置。缺点时回出现堆积现象。存取时,可能不是同义词的词也位于探查序列,影响效率。
    2)双散列函数:在位置d冲突后,再次使用另一个散列函数产生一个散列桶容量互质的数c,一次试探(d+n*c),时探查序列跳跃式分布。
    崇勇的构造散列函数的方法

    散列函数能使对一个数据序列的访问过程更加迅速有效,通过散列函数,数据元素将被更快的定位:

    1.直接寻址法:取关键字或关键字的某个线性函数值为散列地址。即H(key)=key或H(key)=a?key+b,其中a和b常数(这种散列函数叫做自身函数)

    2.数字分析法:分析一组数据,比如一组员工的出生年月,这时我们发现出生年月日的前几位数字答题相同,这样的话,出现冲突的几率就会很大,但是我们发现年月日的后继伟表示月份和具体日期的数字拆憋很大,如果用后面的数字来构成散列地址,则冲突的几率会明显降低。因此数字分析法就是找出数字的规律,尽可能利用这些数据来构造冲突纪律较低的散列地址。

    3.平方取中法:取关键字平方后的中间纪委作为散列地址。

    4.折叠法:选择一随机函数,去关键字的随机值作为散列地址,通常用于关键字长度不同的场合。

    5.随机数法:选择一随机函数,取关键字的随机值作为散列地址,通常用于关键字长度不同的场合。

    6.除留余数法:取关键字被某个不大于散列表表长m的数P除后所得的余数为散列地址。即H(key) = key mod p,p<=m.不仅可以对关键字直接取模,也可在折叠、平方取中等运算之后取模。对p的酸则很重要,一般取素数或m,若p选的不好,容易产生同义词。

    散列表的查找过程基本上和造表过程相同。一些关键码可通过散列函数转换的地址直接找到,另一些关键吗在散列函数得到的地址上产生了冲突,需要按处理冲突的方法进行查找。在介绍的三种处理宠的方法中,产生冲突后的查找仍然是给定值与关键码进行比较的过程。所以,对散列表查找效率的量度,依然用平均查找长度来衡量。


    查找过程中,关键码的比较次数,取决于产生冲突的多少,产生的冲突少,查找效率就高,产生的冲突多,超着效率就低。因此影响产生冲突多少的因素,也就是影响查找效率的因素。影响产生冲突多少有以下三个因素:

    1.散列函数是否均匀;

    2处理冲突的方法;

    3散列表的田庄因子。

    散列表的装填因子定义为:a=填入表中的元素歌数/散列的长度

    a是散列表装满程度的标志因子。由于表长是定值,a与“填入表中的元素个数”成正比,所以,a越大,填入表中的元素越多,产生冲突的可能性就越大;a越小,填入表中的元素较少,产生冲突的可能性就越小。

    注:a和表中元素数量小可以减少冲突

    实际上,散列表的平均查找长度是装填因子a的函数,只是不同处理冲突的方法有不同的函数。

    了解了hash基本定义,就不能不提到一些著名的hash算法,MD5和SHA-1可以说是目前应用足迹广泛的hash算法,而他们都是以MD4为基础设计的。那么他们是什么意思?

    eg:
    1)MD4(RFC 1320)是MIT 的Ronald L.Rivest 在1990年设计的,MD是Message digest的缩写。它适合在32位字长的处理器上用高速软件实现--它是机遇32位操作来实现的。

    2)MD5(RFC 1321)是Rivest 于1991年对MD4的改进版本。它对输入仍以512位分组,其输出是4个32位字的级联,与MD4相同。MD5比MD4来的复杂,并且速度较之要慢一点,但更安全,在抗分析和抗差分方面表现更好。

    3)SHA-1及其他
    SHA1是由NIST NSA 设计为同DSA一起使用的,他对长度小于264的输入,产生长度为160bit的散列值,因此抗穷举(brute-force)性更好。SHA-1设计时基于喝MD4相同原理,并且模仿了该算法。

    哈希表不可避免冲突(collision)现象:对不同的关键字可能得到同意哈希地址,即key1!=key2,而hash(key1)=hash(key2)。因此,在建造哈希表时不仅要设定一个好的哈希函数,而且要设定一种处理冲突的方法。可如下描述哈希表:根据设定的哈希函数H(key)和所选中的处理冲突的方法,将一组关键字映像到一个有限的、地址连续的地址集(区间)上并以关键字在地址集中的”象“作为响应纪录在表中的存储位置,这种表被称为哈希表。

    对于动态查找表而言,

    1)表长不确定;

    2)在设计查找表时,只知道关键字所属范围,而不知道确切的关键字。因此,一般情况需建立一个函数关系,以f(key)作为关键字为key的录在表中的位置,通常称这个函数f(key)为哈希函数。(注意:这个函数并不一定是数学函数)

    哈希函数是一个映像,即:将关键字的结合影响到某个地址结合上,它的设置很灵活,只要这个地址集合的大小不超过允许范围即可。
    现实中哈希函数是需要构造的,并且构造的好才能使用的好。


    那么这些hash算法到底有什么用呢?
    hash算法在信息安全方面的应用主要体现在以下的3个方面


    1)文件校验

    我们比较熟悉的娇艳算法有奇偶校验喝CRC娇艳,这两种娇艳并没有抗数据转改能力,他们一定程度上能检测病纠正数据传输中的信道误码,但却不能防止对数据的恶意破坏。

    MD5 Hash算法的“数字指纹”特性,使它成为目前应用广泛的一种文件完整性娇艳喝(Checksum)算法,不少Unix系统由提供计算md5 checksum命令。

    2)数字签名

    Hash算法也是现代密码体系中的一个重要组成部分。由于非对称算法的运行速度较慢,所以在数字签名协议中,单向散列函数扮演了一个重要的角色。对Hash值,又称“数字摘要”进行数字签名,在同济上可以认为与对文件本身进行数字签名是等效的。而且这样的协议还有其他的优点。

    3)鉴权协议

    如下的鉴权协议又称作挑战--认证模式:在传输信道是可被真听,但不可以被修改的情况下,这是一种简单而安全的方法。

    文件hash值

    MD5-hash-文件的数字文摘通过Hash函数计算得到。不管文件长度如何,它的Hash函数计算结果是一个固定长度的数字。与机密算法不同,这个Hash算法是一个不可逆的单向函数。采用安全性高的Hash算法,如MD5、 SHA时,两个不同的文件几乎不可能得到相同的Hash结果。因此一旦文件被修改,就可检测出来。

    Hash函数还有另外的含义。实际中的Hash函数是指把一个大范围映射到一个小范围。把大范围映射到一个小范围的目的往往是为了节省空间,是的数据容易保存。除此之外,Hash函数往往应用于查找上。所以,在考虑使用Hash函数之前需要明白几个限制:

    1.Hash的主要原理就是把范围映射到小范围;所以,你输入的实际值的个数必须喝小范围相当或者比它更小。不然冲突就会很多;

    2.由于Hash逼近单向函数;所以,你可以用它来对数据进行加密;

    3.不同的应用对Hash函数有着不同的要求。

    eg:用于加密的Hash函数主要考虑它和单向函数的差距,而用于查找的Hash函数主要考虑它映射到小范围的冲突率。


    Hash函数应用的主要对象是数组,而其目标一般是个int类型。以下我们都按照这种方式来说明。
    一般说,Hash函数可以简单的划分为如下几类:

    1.加法Hash;
    2.位运算Hash;
    3.乘法Hash;
    4.除法Hash;
    5.查表Hash;
    6.混合Hash;


    下面详细介绍以上各种方式在实际中的运用。


    一 加法Hash 所谓的加法Hash就是把输入元素一个一个的加起来构成最后的结果。标准的加法Hash的构造如下:

    static int additiveHash(String key, int prime) {
        int hash, i;
        for (hash = key.length(), i = 0; i < key.length(); i++)
            hash += key.charAt(i); return (hash % prime);
    } 这里的prime是任意的质数,看得出,结果的值域为[0,prime-1]。

    二 位运算Hash 这类型Hash函数通过利用各种位运算(常见的是移位和异或)来充分的混合输入元素。比如,标准的旋转Hash的构造如下:
    static int rotatingHash(String key, int prime) {
        int hash, i;
        for (hash=key.length(), i=0; i<key.length(); ++i)
            hash = (hash<<4)^(hash>>28)^key.charAt(i); return (hash % prime);
    }
    先移位,然后再进行各种位运算是这种类型Hash函数的主要特点。比如,以上的那段计算hash的代码还可以有如下几种变形:
    1. hash = (hash<<5)^(hash>>27)^key.charAt(i);
    2. hash += key.charAt(i);
    hash += (hash << 10);
    hash ^= (hash >> 6);
    3. if((i&1) == 0)
    {
        hash ^= (hash<<7) ^ key.charAt(i) ^ (hash>>3);
    } else {
        hash ^= ~((hash<<11) ^ key.charAt(i) ^ (hash >>5)); }
    4. hash += (hash<<5) + key.charAt(i);
    5. hash = key.charAt(i) + (hash<<6) + (hash>>16) – hash;
    6. hash ^= ((hash<<5) + key.charAt(i) + (hash>>2));


    三 乘法Hash


    这种类型的Hash函数利用了乘法的不相关性(乘法的这种性质,最有名的莫过于平方取头尾的随机数生成算法,虽然这种算法效 果并不好)。比如,

    static int bernstein(String key) {
        int hash = 0;
        int i;
        for (i=0; i<key.length(); ++i) hash = 33*hash + key.charAt(i); return hash;
    }
    jdk5.0里面的String类的hashCode()方法也使用乘法Hash。不过,它使用的乘数是31。推荐的乘数还有:131, 1313, 13131, 131313等等。
    使用这种方式的著名Hash函数还有: // 32位FNV算法
    int M_SHIFT = 0;
    public int FNVHash(byte[] data) {
        int hash = (int)2166136261L; for(byte b : data)
            hash = (hash * 16777619) ^ b; if (M_SHIFT == 0)
                return hash;
        return (hash ^ (hash >> M_SHIFT)) & M_MASK;
    }
    以及改进的FNV算法:
    public static int FNVHash1(String data) {
        final int p = 16777619;
        int hash = (int)2166136261L; for(int i=0;i<data.length();i++)
            hash = (hash ^ data.charAt(i)) * p; hash += hash << 13;
        hash ^= hash >> 7;
        hash += hash << 3;
        hash ^= hash >> 17; hash += hash << 5; return hash;
    }
    除了乘以一个固定的数,常见的还有乘以一个不断改变的数,比如: static int RSHash(String str)
    {
        int b = 378551; int a = 63689; int hash = 0;
        for(int i = 0; i < str.length(); i++) {
            hash = hash * a + str.charAt(i);
            a =a*b; }
        return (hash & 0x7FFFFFFF);
    }
    虽然Adler32算法的应用没有CRC32广泛,不过,它可能是乘法Hash里面最有名的一个了。关于它的介绍,大家可以去看RFC 1950规范。


    四 除法Hash

    除法和乘法一样,同样具有表面上看起来的不相关性。不过,因为除法太慢,这种方式几乎找不到真正的应用。需要注意的是, 我们在前面看到的hash的 结果除以一个prime的目的只是为了保证结果的范围。如果你不需要它限制一个范围的话,可以使用如 下的代码替代”hash%prime”: hash = hash ^ (hash>>10) ^ (hash>>20)。


    五 查表Hash

    查表Hash最有名的例子莫过于CRC系列算法。虽然CRC系列算法本身并不是查表,但是,查表是它的一种最快的实现方式。查表 Hash中有名的例子有:Universal Hashing和Zobrist Hashing。他们的表格都是随机生成的。


    六 混合Hash
    混合Hash算法利用了以上各种方式。各种常见的Hash算法,比如MD5、Tiger都属于这个范围。它们一般很少在面向查找的 Hash函数里面使用。


    七 对Hash算法的评价
    http://www.burtleburtle.net/bob/hash/doobs.html 这个页面提供了对几种流行Hash算法的评价。我们对Hash函数的建议如 下:

    1. 字符串的Hash。最简单可以使用基本的乘法Hash,当乘数为33时,对于英文单词有很好的散列效果(小于6个的小写形式可 以保证没有冲突)。复杂一点可以使用FNV算法(及其改进形式),它对于比较长的字符串,在速度和效果上都不错。

    2. 长数组的Hash。可以使用http://burtleburtle.net/bob/c/lookup3.c这种算法,它一次运算多个字节,速度还算不错。

    八 后记
    本文简略的介绍了一番实际应用中的用于查找的Hash算法。Hash算法除了应用于这个方面以外,另外一个著名的应用是巨型字 符串匹配(这时的 Hash算法叫做:rolling hash,因为它必须可以滚动的计算)。设计一个真正好的Hash算法并不是一件容易 的事情。做为应用来说,选择一个适合的算法是最重要的。
    九 数组hash
    inline int hashcode(const int *v) {
        int s = 0;
        for(int i=0; i<k; i++) s=((s<<2)+(v[i]>>4))^(v[i]<<10); s = s % M;
        s = s < 0 ? s + M : s; return s;
    }
    注:虽说以上的hash能极大程度地避免冲突,但是冲突是在所难免的。所以无论用哪种hash函数,都要加上处理冲突的方法
























    展开全文
  • HASH函数 应用Hash函数

    2015-08-14 14:05:06
    HASH函数 应用Hash函数  作者:冲处宇宙 时间:2007.1.25 计算理论中,没有Hash函数的说法,只有单向函数的说法。所谓的单向函数,是一个复杂的定义,大家可以去看计算理论或者...
  • php自定义hash函数实例

    2020-10-24 07:12:57
    主要介绍了php自定义hash函数,实例分析了hash函数的实现技巧,可实现简单的加密功能,具有一定参考借鉴价值,需要的朋友可以参考下
  • hash函数和hash表

    2020-03-17 18:17:44
    首先说hash函数,hash其实就是一个映射函数y=Hash(x)y=Hash(x)y=Hash(x),具体做的事情就是把一个值y(数字,字符串等)映射成一个数组hash_arr的下标index(数字),然后把这个值存到这个下标对应的位置。...
  • hash函数的引入 在介绍hash函数之前,先说个实际的例子。我是个比较乱的男生,袜子啊,书籍什么的都乱扔。那么哪天如果要找某件东西,在最坏的情况下,你需要找遍你房间的所有角落。但是,如果你是个爱收拾的男生,...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 15,099
精华内容 6,039
关键字:

hash函数