散列函数之单散列算法解决冲突问题

1. 问题

问题同《简单散列函数算法

设有10个非负整数,用不多于20个的储存单元来存放,如何存放这10个数,使得搜索其中的某一个数时,在储存单元中查找的次数最少?

问题类似于,有10个带号码的球,放到编号为{0, 1, 2, …, 19}共20个盒子中,每个盒子最多放一个,问如何放,使能够用最少的次数打开盒子,知道任一个球所在的盒子编号?

2. 分析

简单散列函数算法》中,已经分析得出,只要能解决冲突问题,就能将查找时间降为常量范围内。

思路:当一个数发生冲突时,再找一个没有被占用的空盒子来放这个球

哈哈,思路相当简单,好像也很有道理的样子,关键问题是:如何知道这个没占用的空盒子和球号的对应关系?

这里使用《初等数论及其应用》中第5章所介绍的方法,该书中对方法的描述有的地方进行了简略,也没讲如何查找,我这里进行补齐,并写了份python的代码,便于理解和应用

3. 单散列函数解决冲突问题

3.1 方法的思路:

设盒子数量是m, 球的总数是n

当一个数发生冲突时,则看这个冲突的盒子(k)的下一个盒子(k+1)是否是空的,如果是,则放入,如果不是,则继续看下下个(k+2),一直加到m,大于m还没找到,则到{0, 1, …, k}即k前面的盒子中去找空盒子:

如假设有球{0, 1, 30},仍设m = 10,当0和1分别放入对应的0, 1号盒子,当要放入30时,f(30) = 30 % 10 = 0,0号盒子被占用,冲突,再看下一个盒子1号,发现1号也被占用,再看下一个2号盒子,发现是空的,放入即可

当又有一个球为40时呢,类似,会发现0, 1, 2号盒子都被占用,这时需要放入3号盒子

那么这时又来一个真正的3号球呢,我们会发现3号盒子已经被占用,所以只能放到4号盒子中去了

最后{0, 1, 30, 40, 3}放入的情况如下:

盒子1编号 0 1 2 3 4 5 6 7 8 9
球号 0 1 30 40 3          

注意:这里由于球号的排列顺序不同,放的位置并不一致,如球号如此排列{0, 1, 3, 30, 40},则3号球就会在3号位置了

∵ 而m >= n

∴ 对于任一个x,总可以找到一个空的盒子给其放球

3.2 数学表达式

设 h0(k) ≡ k (mod m), k = 球号, m = 盒子数量,这里”≡ ”表示同余,不是相等,h(k)即为除m的余数

hj(k) ≡ h0(k) + j,0<= j < m,  hj(k) 表示发生 j 次冲突后,球所放入的盒子编号

∴ hj+1(k) ≡ h0(k) + (j + 1) ≡ hj(k) + 1

即表示,当在hj(k)的位置发生冲突后,即再查看其下一个盒子是否为空

∵当k = m - 1时, k ≡ 0 (mod m),根据模的算法

∴其下一个位置 k + 1 = 0,即表示回到0号盒子开始查找空盒子

3.3 如何查找球k所在的盒子

方法和放球时一样的,先查找h0(k) ≡ k (mod m), 如果相等,则ok

如果不相等,则说明可能在下一个盒子中,按3.2的公式依次递归,最终会找到对应的盒子

3.4 最坏复杂度

假设有9个球已经占据了{0, 1, 2, …, 8}前8个盒子,最后一个球k9 ≡ k0 (mod m),则需要从第0个位置依次 +1 加到9位置,才能找到不冲突的盒子,也就是说最坏要打开10个盒子才能找到,最坏复杂度 = n,n为球的数量,

哈哈,看起来费了半天劲还不如《简单散列函数算法》中的方法2.2。

但好处也很明显,《简单散列函数算法》中的方法2.2需要先对在第2组盒子中的球号进行排序,如果新增加一个球,就要再排一次

3.5 Python code及测试结果

#mod = m, h(k) = n % m, hj(k) = (h(k) + j) % m
def SingleHash(numList):
    if len(numList) > m:
        print "num list len is :", len(numList), "is big than mod :", m;
        return None;

    hashList = [None] * m;
    for k in numList:
        for j in range(m):
            hj_k = (k + j) % m;
            if None == hashList[hj_k]:
                hashList[hj_k] = k;
                break;
        else:
            print "num list is too big, hash list is overflow";
            return None;

    return hashList;

def CheckSingleHash(numList, hashList):
    if None == hashList:
        return;

    for k in numList:
        for j in range(m):
            hj_k = (k + j) % m;
            #check the key is equal with the one in hash list, if not check the next one
            if hashList[hj_k] == k:
                if j > 0:
                    print k, "find", j+1, "times";
                break;
            else:
                print k, "conflict with", hashList[hj_k];
        else:
            print k, "is not find...";
            return False;

    return True;

测试时,设置了m = 19

测试数列为: numList = [0, 1, 2, 7, 9, 15, 19, 20, 77, 38],为了测试冲突,故意设置了一些冲突数,为了减少无用的输出,对于没有冲突的就不打出了,测试结果如下:

可以看出,38由于多次冲突,需要查找7次才能找到

4. 散列算法冲突问题

不论是《简单散列函数算法》中的散列算法还是单散列算法,如果没有冲突的情况下,都只要一次就能找到球所在的盒子,所以如果算法冲突的概率低,那么平均的时间复杂度也是很是越来越接近常量的。

4.1 简单散列算法冲突的概率

简单散列函数算法》中的散列算法只要一个球k模m(k%m)已经在盒子中了,就一定会产生冲突,设k已在第一组盒子中,则对所有的f(x) = k + im, (i ∈ {0, 1, 2, ...}),都会产生冲突,冲突的概率是很高的。

设最大的球号为s, 则共有(s/m)个满足f(x) = k + im的球号(当s很大时,可以忽略除不尽的部分)

则简单散列函数算法中第一个为满足f(x) = k + im的球的概率 : 

设共有n个球,恰有2个球满足f(x)时,第2个球的概率:,当s很大时,1可以忽略,则约为 (n-1)/m

故刚好取2个球满足f(x)时的概率 = (n2-1)/m2

类似的,恰有3个球满足f(x)时的概率 = n(n-1)(n-2) / m3

可以看出,当n和m接近时,冲突的概率越来越接近100%,所以一定要使m > n,且大得越多越好

回到我们问题中的设定,n = 10, m = 20, 则刚好2个球满足时,冲突的概率 = 25%

刚好有3个球满足时,冲突的概率 = 9%

总的冲突概率 > 34%,冲突概率是非常高的

4.2 单散列算法冲突概率

单散列算法中,由于第一次产生冲突时,设为hj(k),第2次产生冲突时则必须有一个hj(k) + 1 的球已经在盒子中,所以发生2次以上冲突的概率会有所降低,当然,这种情况下是需要查看2个盒子的。

由4.1知,同时取出2个都满足f(x)时的概率 = (n2-1)/m2,则第3个球必须是f(x)或f(x)+1才会有冲突

第3个球是f(x)的概率 = (n-2)/m

第3个球是f(x) + 1的概率 = (n-2)/m,

故发生冲突的概率 = 2n(n-1)(n-2) / m3

其他大于3个球冲突的概率就不予计算比较了,单用此和简单散列算法恰有2个球的冲突比较:

当n = 10, m = 20, 单散列算法冲突的概率 = 18%

可以看出,单散列算法在此情况下冲突概率上是优于简单散列算法的。

时间: 2024-09-29 23:33:31

散列函数之单散列算法解决冲突问题的相关文章

加密散列算法——MD5

引用wiki的定义,散列函数(或散列算法,英语:Hash Function)是一种从任何一种数据中创建小的数字"指纹"的方法.该函数将数据打乱混合,重新创建一个叫做散列值的指纹.散列值通常用来代表一个短的随机字母和数字组成的字符串.好的散列函数在输入域中很少出现散列冲突.在散列表和数据处理中,不抑制冲突来区别数据,会使得数据库记录更难找到.(具体专业术语请自行度娘) MD5是单向散列算法的一种,全称是Message-Digest Algorithm 5(信息-摘要算法),经MD2.MD

散列算法和哈希表结构

散列算法和哈希表结构 算法概述 Hash ,一般翻译做" 散列" ,也有直接音译为" 哈希" 的,就是把任意长度的输入(又叫做预映射, pre-image ),通过散列算法,变换成固定长度的输出,该输出就是散列值.这种转换是一种压缩映射,也就是,散列值的空间通常远小于输入的空间,不 同的输入可能会散列成相同的输出,而不可能从散列值来唯一的确定输入值.简单的说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数. 哈希表 数组的特点是:寻址容易,插入和删除困难:

Android数据加密之SHA安全散列算法

前言: 对于SHA安全散列算法,以前没怎么使用过,仅仅是停留在听说过的阶段,今天在看图片缓存框架Glide源码时发现其缓存的Key采用的不是MD5加密算法,而是SHA-256加密算法,这才勾起了我的好奇心,所以趁着晚上没啥事,来学习一下. 其他几种加密方式: Android数据加密之Rsa加密 Android数据加密之Aes加密 Android数据加密之Des加密 Android数据加密之MD5加密 Android数据加密之Base64编码算法 SHA加密算法 SHA(Secure Hash A

数字签名、数字证书、对称加密算法、非对称加密算法、单向加密(散列算法)

数字签名是什么? 1. 鲍勃有两把钥匙,一把是公钥,另一把是私钥. 2. 鲍勃把公钥送给他的朋友们----帕蒂.道格.苏珊----每人一把. 3. 苏珊给鲍勃写信,写完后用鲍勃的公钥加密,达到保密的效果. 4. 鲍勃收信后,用私钥解密,看到信件内容. 5. 鲍勃给苏珊回信,写完后用Hash函数,生成信件的摘要(digest). 6. 然后,鲍勃使用私钥,对这个摘要加密,生成"数字签名"(signature). 7. 鲍勃将这个签名,附在信件下面,一起发给苏珊. 8. 苏珊收信后,取下数

对称密码、非对称密码、散列算法与PKI

对称密码.非对称密码.散列算法与PKI 密码学要解决的问题:机密性.完整性.身份验证(抗抵赖性): 一.对称密码: 对称密码技术:发件人和收件人使用其共同拥有的单个密钥 ,这种密钥既用于加密,也用于解密,叫做机密密钥(也称为对称密钥或会话密钥). 能够提供信息机密性(没有密钥信息不能被解密).完整性(被改变的信息不能被解密)的服务. 对称式密码学又称:单钥密码学.秘密密钥密码学.会话密钥密码学.私钥密码学.共享秘钥密码学  常见的对称式加密技术: DES(数据加密标准):分组式加密,算法源于Lu

加解密算法一:散列算法、对称加解密

.Net中的加解密操作所涉及的对象都在命名空间System.Security.Cryptography下,所以应先在程序中添加using System.Security.Cryptography. 1.散列算法: 用来产生一些数据片段(例如消息或会话项)的散列值的算法.好的散列算法具有在输入数据中的更改可以更改结果散列值中每个比特的特性:因此,散列对于检测在诸如消息等大型信息对象中的任何变化很有用.此外,好的散列算法使得构造两个独立的有相同散列的输入不能通过计算方法实现. 典型的散列算法包括 M

MD5(单向散列算法)原理分析

注:本文章转载于网络. MD5(单向散列算法)的全称是Message-Digest Algorithm 5(信息-摘要算法),经MD2.MD3和MD4发展而来.MD5算法的使用不需要支付任何版权费用. MD5功能:    输入任意长度的信息,经过处理,输出为128位的信息(数字指纹):    不同的输入得到的不同的结果(唯一性):    根据128位的输出结果不可能反推出输入的信息(不可逆): MD5属不属于加密算法:    认为不属于的人是因为他们觉得不能从密文(散列值)反过来得到原文,即没有

Java哈希散列算法简介 - MD5 &amp; SHA-512

Java哈希散列算法简介 - MD5 & SHA-512 在日常的开发工作中,我们常常会碰到这样的一个场景:我们需要有一种可靠的行之有效的方法来检验跟判断数据在传输过程当中的完整性.最常见的一种情况就是当我们传输文件的时候,由于网络故障或者其他的一些因素,可能会出现我们下载下来的文件不完整,这给我们日常的开发和维护带了一些难题:另外的一个较为常用的场景就是:有没有一种行之有效的方法让我们可以很方便的判断服务器上的文件是不是有最新的数据更新,比如我们现在的移动Hybird App开发,我们经常会发

个人理解c#对称加密 非对称加密 散列算法的应用场景

c#类库默认实现了一系列加密算法在System.Security.Cryptography; 命名空间下 对称加密 通过同一密匙进行加密和解密.往往应用在内部数据传输情况下.比如公司a程序 和B程序 .a程序要给B程序发送数据 但是为了防止明文发送 数据被窃取.那么我就定了一个协议传输的数据的byte字节都统一+1  而接收数据的情况下将Byte字节统一-1 然后就能获得正确的 数据(当然这个是一个简单的加密) ,真正应用的加密肯定比这个复杂很多 非对称加密 传输数据的双方有各自的公钥和私钥