zoukankan      html  css  js  c++  java
  • 散列函数之单散列算法

    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],为了测试冲突,故意设置了一些冲突数,为了减少无用的输出,对于没有冲突的就不打出了,测试结果如下:

    image

    可以看出,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的球的概率 :  image

    设共有n个球,恰有2个球满足f(x)时,第2个球的概率:image,当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%

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

  • 相关阅读:
    安装lnmp 时如何修改数据库数据存储地址及默认访问地址
    ubuntu 设置root用户密码并实现root用户登录
    解决ubuntu 远程连接问题
    linux 搭建FTP服务器
    PHP 根据ip获取对应的实际地址
    如何发布自己的composer包
    使用composer安装composer包报Your requirements could not be resolved to an installable set of packages
    laravel 框架配置404等异常页面
    使用Xshell登录linux服务器报WARNING! The remote SSH server rejected X11 forwarding request
    IoTSharp 已支持国产松果时序数据库PinusDB
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/6262835.html
Copyright © 2011-2022 走看看