Hash算法的讲解

转自:https://www.cnblogs.com/xiohao/p/4389672.html

散列表:哈希表,据关键码值key  value 直接访问的数线性表数据结构

      通过关键码值映射到表中的一个位置来访问记录,加快查找速度:散列函数

            函数计算结果是一个存储单位地址,每个存储单位称为“桶”,m个桶、值域[0,m-1]

      其中元素间可能存在空隙:负载因子

      基于快速存取的角度设计,空间换时间

基于 结果尽可能随机平均分布的固定函数H 为每个元素安排存储位置,避免遍历性质的线程搜索,但因为随机性、导致冲突(两个元素通过散列函数H 相同地址:同义词,哈希表不可避免冲突)

 

构造散列函数的方法:

散列函数:对一个数据序列的访问过程迅速有效,元素快速定位

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

 

冲突取决于:

散列函数、处理冲突的方法、负载因子大小(太大不一定好,浪费空间,因子与函数联动)

解决冲突:

1、线程探查法:冲突后、线性向前试探,找最近空位置;会堆积、存取时maybe非同义词的词也位于探查序列,影响效率

2、双散列函数法:在位置d冲突后,再次使用另一散列函数产生一个与散列表桶容量m互质的数c,依次试探(d+n*c)%m,使探查序列跳跃式分布;

      两个正整数只有一个公因数1时,它们的关系叫做互质;【

 

查找性能分析:

基本和造表过程相同,一些关键码通散列函数转换地址找到;一些码产生冲突,则按处理冲突方法查找;产生冲突后的查找仍然是给定值与关键码比较的过程;

效率量度:用平均查找长度衡量

查找过程中,关键码比较次数,取决于产生冲突的多少,影响因素

1、散列函数是否均匀;2、处理冲突的方法;3、散列表的装填因子

装填因子:α= 填入表中的元素个数 / 散列表的长度

     α是散列表装满程度的标志因子,由于表长是定值,α与“填入表中的元素个数”成正比,所以,α越大填入表的元素较多,产生冲突可能性越大;散列表的平均查找长度是装填因子α的函数,只是不同处理冲突的方法有不同的函数

 

hash算法

MD5 和 SHA-1 :目前应用最广泛的Hash算法,都是以 MD4 为基础设计的

1、MD4(RFC 1320) Message Digest ,适用在32位字长的处理器上用高速软件实现--基于 32 位操作数的位操作;

2、MD5(RFC 1321)对输入仍以512位分组,其输出是4个32位字的级联,与 MD4 相同。比MD4复杂,且速度慢一点,但更安全,在抗分析和抗差分方面表现更好;

3、SHA1 对长度小于264的输入,产生长度160bit的散列值,抗穷举(brute-force)性更好。 设计时基于MD4相同原理,且模仿了MD4;

 

哈希表:根据设定的哈希函数H(key)和所选中的处理冲突的方法,将一组关键字映象到一个有限的地址连续的地址集(区间)上并以关键字在地址集中的“象”作为相应记录 在表中的存储位置

哈希函数:以f(key)作为关键字 为key的记录在表中的位置,一个映像,将关键字的集合映射到某个地址的集合上,设置灵活、只有地址集合大小不超过容许范围即可;

 

Hash函数

       (也称杂凑函数或杂凑算法)就是把任意长的输入消息串变化成固定长的输出串的一种函数。这个输出串称为该消息的杂凑值。一般用于产生消息摘要,密钥加密等.【

        加法hash、位运算hash、乘法hash、除法hash、查表hash、混合hash

1)加法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);//返回索引i处的字符
 
    return (hash % prime);//prime是任意的质数,值域为[0,prime-1]
 
}

2)位运算:用各种位运算(常见的是移位和异或)来充分的混合输入元素

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<<5>>27)^key.charAt(i);
 
hash += key.charAt(i);
 
hash += (hash << 10);
 
hash ^= (hash >> 6);
 
 if((i&1) == 0) {
 
hash ^= (hash<<7>>3);
 
} else {
 
hash ^= ~((hash<<11>>5));
 
 } hash += (hash<<5> hash = key.charAt(i) + (hash<<6>>16) ? hash; hash ^= ((hash<<5>>2));

https://blog.csdn.net/jason5186/article/details/9037623

亦或https://zhidao.baidu.com/question/68565016.html

1:“按位与”运算符(&)用法是如果两个相应的二进制位都为1,则该位的结果值为1否则为0。0&0=0,1&0=0,1&1=1
2:“按位或”运算符(|)用法是如果两个相应的二进制位有一个为1,则该位的结果值为1否则为0。0&0=0,1&0=0,1&1=1
0,1&0=1,1&1=1
3:“异或”运算符(^)用法是如果两个相应的二进制位为同号,则该位的结果值为1否则为0。0&0=1,1&0=0,1&1=1

乘法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等等;

//  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);
}

 

除法hash

除法太慢,这种方式几乎找不到真正的应用;

在前面看到的hash的  结果除以一个prime的目的只是为了保证结果的范围;

 查表Hash

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

混合Hash

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

 

作用

1)文件校验

MD5 Hash的“数字指纹”:文件完整性校验和Checksum算法,抗数据篡改

     文件hash值:MD5Hash文件的数字文摘通过Hash函数计算得到,结果是固定长度的数字

     hash算法不可逆的单向函数,不同文件不太可能得到same结果,so一旦文件被修改,能检查出

2)数字签名

3)鉴权协议

挑战-认证模式:传输信道可被侦听、不可改

 

限制:

1、主原理:大范围映射到小范围,输入的实际值的个数须<=小范围,否则冲突more

2、不同应用对hash函数有不同要求,如加密hash考虑和单项函数的差距;查找hash考虑映射到小范围的冲突

3、不同应用对hash函数有不同要求,加密hash考虑它和单向函数的差距,查找hash考虑映射到小范围的冲突率

 

感谢分享:

https://blog.csdn.net/wfg18801733667/article/details/59108337

https://www.cnblogs.com/xiohao/p/4389672.html

感觉好多文章取自同一篇文章,连字符串都一模一样,无可厚非,多谢分享

发现csdn一个人性化的地方,新建博客的时候如果没有专门添加这个分类,在下面建的话会自动帮你建这个类,嗯~可能你们不知道我在说什么,可能也没有人会看到,写出来也不是为了……词穷了,也不知道写出来干啥,再说也不是我写的,复复制制、转载一篇,感才选的‘原创’,赶紧改过来,不废话了,晚上睡不着、诶~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值