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

    1. 问题

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

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

    2. 分析

    2.1 最简单的情况

    设10个球的号码分别是 : {1, 2, 3, …, 10}

    那么我们只要10个盒子,按顺序依次将球放入{1, 2, 3, …, 10}中,那么任一个球的所在的盒子就是球的号码,打开对应编号的盒子,一次即可找到这个球

    当然,我们还可以这样放:

    设球的号码 = n, 盒子的编号 = k (k ∈ {0, 1, 2, …., 9})

    球放入盒子的方式 f(n) = (n +  x) % m = (n + x) % 10

    即将每个球偏移x个位置,当x = 1时,则如:1号球放到2号盒子,2号球放到3号盒子,依次类推,最后10号球将在0号盒子

    这就是最简单的散列函数了,当处理球号为{4, 5, 6, …, 13}, {22, 23, 24, …, 31}这样起点不同的球号组合时,可以通杀。

    进一步,如果10个球中,所有的球号除以盒子数20,所得的余数都不相同,即没有冲突,仍可以用该散列函数处理,

    如{0, 1, 3, 5, 6, 7, 9, 13, 14, 17, 39}

    除20后所得余数为{0, 1, 3, 5, 6, 7, 9, 13, 14, 17, 19},都没有冲突,依然可以用该散列函数处理

    很类似于古代西方国家流行的一种加密方式,将每个字母都往后顺移x个位,如good,都顺移一个位,则变成hppe了,即使密报被截获,不懂的人根本不知道是个啥意思,接收方的人只要把每个字母往后回移一位即可得原文。

    这种方法简单,好用,但对于10个数不连续或者没有规律的情况下,且不满足模m(盒子总数)无冲突的条件时,就无法处理了,如{0, 1, 2, 7, 9, 15, 19, 20, 77, 38},因为最大的数出现了77,除非有77个盒子,才能用上述方法,当有球号码是10000时,就得10000个盒子来放10个球,严重浪费空间

    2.2 改进的方案

    因为有20个盒子,我们可以用2.1的方法先放一部分球到编号为{0, 1, 2, …, 9}的盒子中,为了简单,这里设x = 0:

    f(n) = n % 10

    则{0, 1, 2, 7, 9, 15, 19, 20, 77, 38}分别可以放到对应编号为如下的盒子中:

      {0, 1, 2, 7, 9, 5,   9,   0,  7,   8}

    我们看到0号球和20号球都需要放入0号盒子,这就产生了一个冲突(因为一个盒子最多只能放一个球,类似一个内存地址只能放一个数),为了解决这种冲突,我们把有冲突的球放到第2组编号为{10, 11, 12, …, 19}的10个盒子中,则这10个存放的情况如下:

    盒子1编号 0 1 2 3 4 5 6 7 8 9
    球号 0 1 2     15     38 9

    剩下的冲突的球{19, 20, 77}从小到大依次放入第2组盒子:

    盒子2编号 10 11 12 13 14 15 16 17 18 19
    球号 19 20 77              

    当要查找某一个球x时,先计算f(x) = (x % 10),如果f(x)对应的盒子中就是这个球,则只要1次就能找到,和2.1的方法一样

    但当球x不在f(x)盒子中时,则往第2组盒子中查找,因为第2组盒子是有序的,我们用二分查找,可在log2(n)次内找到其对应的球(n = 第二组盒子中球的数量)

    那么这种算法的最坏情况是什么呢?

    我们可以看出,只要不能在第一组盒子中找到该球,则其需要log2(n)次才能找到,当n最大时,即n = 10(因为最多只有10个球),表示10个球都在第2组盒子中

    此时,需要的次数 = log2(10) = 3,加上在第一组盒子中找的一次,共需要4次

    3. 有没有更好的方法?

    从2.2的算法中,我们可以看到,其最终还是用到了二分查找,最坏的查找次数为log2(n),当球的数量增加时,则需打开盒子的次数也要增加,这就不是散列函数了,如用来做STL中的map容器,查找某个key对应的值也将不是常量时间,那么有没有更好的算法使其时间复杂度降为常量呢?方法2.2的问题主要是由于有冲突,从而会导致需要二分查找,如果能有一种方法能解决掉该冲突问题,那么即可将时间复杂度降为常量范围。

  • 相关阅读:
    css 旋转
    html 旋转
    链表和数组的区别
    hashmap
    【java开发系列】—— 自定义注解
    java不确定参数个数方法例子
    mysql 删除
    linux下常用命令
    php的几种算法(转载)
    数据容器
  • 原文地址:https://www.cnblogs.com/organic/p/6262240.html
Copyright © 2011-2022 走看看