1-hash哈希介绍
hash函数
,把任意长度的输入
通过散列算法
变换成固定长度的输出
,该输出就是散列值1。一种常见的hash函数是
,
一般取素数。
设hash函数的定义域为
,值域为
,一般来说,
,这样hash函数容易出现碰撞,如下图,
,
在一条链上(碰撞):
对于hash函数,基本上都能找到一组输入,使得它们的hash值都相同,导致它们在一条链上,有时甚至会比线性查找的复杂度还要高,因为比线性查找多了hash的时间。
2-Universal hashing全域哈希法
思路:解决上述问题的一种方法就是随机。随机从一组hash函数(a family of hash functions)中选择一个。这样选的话,攻击者就没办法针对特定的hash函数构造一组输入,使得hash函数效率很低。
定义1: 是定义域, 是hash函数的集合,能够将 映射到 ,即 .
定义2:如果 满足 并且 ,则称 是全域(universal)的。
根据定义2,如果h是随机均匀地从
中选择(注意每个输入要重新选择一个hash函数), 那么
和
碰撞的概率是:
定理1:随机均匀地从
(
是全域的)选择
,如果我们现在已经把
个输入放入了hash表
中了,则再给一个输入
,有
其中
表示期望。
[定理1的重要性] 通过证明上述定理,我们就可以说,如果存在 是全域的,那么最终在hash表 中元素的分布(在平均意义上)是均匀的。
定理1的证明. 设
表示在hash表
中的随机元素和
碰撞的数量,设
那么,
例子 :如果 ,则
3-构造一个全域哈希
定理2: 按照如下四个步骤构造的 是全域的:
- (条件)令 等于一个素数;
- (初始准备)将输入 写成 个数字: ,其中 (等价于将 用 进制表示);
- (随机)随机选择一个 ,其中 ;
- (hash函数) .
证明见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])