Universal Hashing全域哈希原理与python实现,减少hash冲突/碰撞!

18 篇文章 1 订阅
2 篇文章 0 订阅

1-hash哈希介绍

hash函数 y = h ( k ) y=h(k) y=h(k),把任意长度的输入 k k k通过散列算法 h h h变换成固定长度的输出 y y y,该输出就是散列值1。一种常见的hash函数是 y = H ( k ) = ( a ⋅ k + b ) m o d    m y=H(k)=(a\cdot k+b) \mod m y=H(k)=(ak+b)modm m m m一般取素数。
设hash函数的定义域为 K K K,值域为 Y Y Y,一般来说, ∣ K ∣ > ∣ Y ∣ |K|>|Y| K>Y,这样hash函数容易出现碰撞,如下图, h ( k 5 ) = h ( k 2 ) = h ( k 7 ) h(k_5)=h(k_2)=h(k_7) h(k5)=h(k2)=h(k7) k 5 , k 2 , k 7 k_5,k_2,k_7 k5,k2,k7在一条链上(碰撞):

在这里插入图片描述
对于hash函数,基本上都能找到一组输入,使得它们的hash值都相同,导致它们在一条链上,有时甚至会比线性查找的复杂度还要高,因为比线性查找多了hash的时间。

2-Universal hashing全域哈希法

思路:解决上述问题的一种方法就是随机。随机从一组hash函数(a family of hash functions)中选择一个。这样选的话,攻击者就没办法针对特定的hash函数构造一组输入,使得hash函数效率很低。

定义1 U \mathcal{U} U是定义域, H \mathcal{H} H是hash函数的集合,能够将 U \mathcal{U} U映射到 { 0 , 1 , . . . , m − 1 } \{0, 1, ..., m-1\} {0,1,...,m1},即 h : U → { 0 , 1 , . . . , m − 1 } , h ∈ H h:\mathcal{U}\rightarrow\{0, 1, ..., m-1\}, h\in \mathcal{H} h:U{0,1,...,m1},hH.

定义2:如果 ∀ x , y \forall x, y x,y满足 x ≠ y x\neq y x=y并且 ∣ { h ∈ H : h ( x ) = h ( y ) } ∣ = ∣ H ∣ m |\{h\in \mathcal{H}:h(x)=h(y)\}|=\frac{|\mathcal{H}|}{m} {hH:h(x)=h(y)}=mH,则称 H \mathcal{H} H是全域(universal)的。

根据定义2,如果h是随机均匀地从 H \mathcal{H} H中选择(注意每个输入要重新选择一个hash函数), 那么 x x x y y y碰撞的概率是:
h ( x ) = h ( y ) 的 函 数 数 量 所 有 的 函 数 = ∣ H ∣ m ∣ H ∣ = 1 m . \frac{h(x)=h(y)的函数数量}{所有的函数} =\frac{\frac{|\mathcal{H}|}{m}}{|\mathcal{H}|}=\frac{1}{m}. h(x)=h(y)=HmH=m1.

定理1:随机均匀地从 H \mathcal{H} H H \mathcal{H} H是全域的)选择 h h h,如果我们现在已经把 n n n个输入放入了hash表 T T T中了,则再给一个输入 x x x,有
E [ h a s h 表 T 中 元 素 和 x 碰 撞 的 数 量 ] < n m , E[hash表T中元素和x碰撞的数量]<\frac{n}{m}, E[hashTx]<mn,
其中 E [ ⋅ ] E[\cdot] E[]表示期望。

[定理1的重要性] 通过证明上述定理,我们就可以说,如果存在 H \mathcal{H} H是全域的,那么最终在hash表 T T T中元素的分布(在平均意义上)是均匀的。

定理1的证明. C x C_{x} Cx表示在hash表 T T T中的随机元素和 x x x碰撞的数量,设
C x y = { 1 i f   h ( x ) = h ( y ) 0 i f   h ( x ) ≠ h ( y ) C_{xy}=\left\{\begin{array}{cr} 1 & if\ h(x)=h(y) \\ 0 & if\ h(x)\neq h(y) \end{array}\right. Cxy={10if h(x)=h(y)if h(x)=h(y)
那么,
E [ C x ] = E [ ∑ y ∈ T − x C x y ] = ∑ y ∈ T − x E [ C x y ] 因 为 期 望 的 线 性 性 质 = ∑ y ∈ T − x 1 m = ( n − 1 ) 1 m < n m . \begin{array}{lll} E[C_x]&=E[\sum_{y\in T-x}C_{xy}] \\ &=\sum_{y\in T-x}E[C_{xy}] & 因为期望的线性性质\\ &=\sum_{y\in T-x}\frac{1}{m} \\ &=(n-1)\frac{1}{m} \\ &<\frac{n}{m}. \end{array} E[Cx]=E[yTxCxy]=yTxE[Cxy]=yTxm1=(n1)m1<mn.线

例子 :如果 n = 1 , m = 2 n=1,m=2 n=1,m=2,则 E [ C x ] < 1 2 . E[C_x]<\frac{1}{2}. E[Cx]<21.

3-构造一个全域哈希 H \mathcal{H} H

定理2: 按照如下四个步骤构造的 H \mathcal{H} H是全域的:

  1. (条件)令 m m m等于一个素数;
  2. (初始准备)将输入 k k k写成 r + 1 r+1 r+1个数字: k = < k 0 , k 1 , . . . , k r > k=<k_0,k_1,...,k_r> k=<k0,k1,...,kr>,其中 k i ∈ { 0 , 1 , . . . , m − 1 } k_i\in\{0, 1, ..., m-1\} ki{0,1,...,m1}(等价于将 k k k m m m进制表示);
  3. (随机)随机选择一个 a = < a 0 , a 1 , . . . , a r > a=<a_0, a_1,...,a_r> a=<a0,a1,...,ar>,其中 a i ∈ 0 , 1 , . . . , m − 1 a_i\in{0, 1,..., m-1} ai0,1,...,m1
  4. (hash函数) h a ( k ) = ( ∑ i = 0 i = r a i × k i ) m o d    m h_a(k)=(\sum_{i=0}^{i=r}a_i\times k_i) \mod m ha(k)=(i=0i=rai×ki)modm.

证明见2

4-python实现

自己写的代码,如有错误望指正。代码链接:https://github.com/VFVrPQ/LDP/blob/master/Components/UniversalHashing.py,另有完整代码如下:

import math
import random
class UniversalHashing:
    '''
        g: a prime
        d: domain, [0, 1, ..., d-1]
        len: The maximum number of digits in g Base
        v: an input value in [0, 1, ..., d-1] 
        hash function: H_a(k) = (a(0)*k(0)+a(1)*k(1)+...+a(len-1)*k(len-1)) % g
    '''
    def __init__(self, g, d):
        self.__g = g
        assert g>=2, 'g is less than 2'
        assert self.__isPrime(g), 'g is not a prime'

        self.__d = d
        self.__len = math.ceil( math.log(d) / math.log(g)) # g进制下,最大的位数
        self.__a = self.__len*[0] # initial length
    
    # v is an input value in [0, 1, ..., d-1] 
    def hash(self, v):
        self.__randomness() # regenerate a, select H
        out = self.calc(self.__a, v)
        return self.__a, out

    # calc H_a(k) = (a(0)*k(0)+a(1)*k(1)+...+a(len-1)*k(len-1)) % g
    def calc(self, a, v):
        assert len(a)==self.__len, 'len(a)!=self.__len'
        k = self.__toBitList(v)
        out = 0
        for i in range(self.__len):
            out = (out + a[i]*k[i]) % self.__g
        return out

    def __randomness(self):
        # generate a
        for i in range(self.__len):
            self.__a[i] = random.randint(0, self.__g-1)

    def __toBitList(self, v):
        assert v>=0, 'v<0'
        if v == 0:
            return self.__len * [0]
        bitList = self.__len * [0]
        for i in range(self.__len):
            bitList[i] = v%self.__g
            v = int(v/self.__g)
        return bitList
    
    def __isPrime(self, v):
        if v<=1:
            return False
        for i in range(2, int(math.sqrt(v))+1, 1):
            if v%i==0:
                return False
        return True

# for test
if __name__ == "__main__":
    TIMES = 10
    g = 29 # prime
    d = 16 # domain
    uhash = UniversalHashing(g, d)
    H = g * [0]
    for i in range(TIMES): # random TIMES to verify
        x = random.randint(0, d-1)
        _, out = uhash.hash(x)
        H[out] += 1
    for i in range(g):
        print(i, H[i])

  1. https://baike.baidu.com/item/Hash/390310?fr=aladdin ↩︎

  2. http://cs-www.bu.edu/faculty/homer/537/talks/SarahAdelBargal_UniversalHashingnotes.pdf 或者https://download.csdn.net/download/MustImproved/12275636 ↩︎

  • 13
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值