淺談解析庫XPath、bs4與pyquery

  全域哈希原理與實現

  1-hash哈希介紹

  2-Universal hashing全域哈希法

  3-構造一個全域哈希H\mathcal{H}H

  4-python實現

  1-hash哈希介紹

  hash函數y=h(k)y=h(k)y=h(k),把任意長度的輸入kkk通過散列算法hhh變換成固定長度的輸出yyy,該輸出就是散列值1。一種常見的hash函數是y=H(k)=(a⋅k+b)mod  my=H(k)=(a\cdot k+b) \mod my=H(k)=(a⋅k+b)modm,mmm一般取素數。

  設hash函數的定義域爲KKK,值域爲YYY,一般來說,∣K∣>∣Y∣|K|>|Y|∣K∣>∣Y∣,這樣hash函數容易出現碰撞,如下圖,h(k5)=h(k2)=h(k7)h(k_5)=h(k_2)=h(k_7)h(k5)=h(k2)=h(k7),k5,k2,k7k_5,k_2,k_7k5,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,...,m−1},即h:U→{0,1,...,m−1},h∈Hh:\mathcal{U}\rightarrow\{0, 1, ..., m-1\}, h\in \mathcal{H}h:U→{0,1,...,m−1},h∈H.

  定義2:如果∀x,y\forall x, y∀x,y滿足x≠yx\neq yx=y並且∣{h∈H:h(x)=h(y)}∣=∣H∣m|\{h\in \mathcal{H}:h(x)=h(y)\}|=\frac{|\mathcal{H}|}{m}∣{h∈H:h(x)=h(y)}∣=m∣H∣,則稱H\mathcal{H}H是全域(universal)的。

  根據定義2,如果h是隨機均勻地從H\mathcal{H}H中選擇(注意每個輸入要重新選擇一個hash函數), 那麼xxx和yyy碰撞的概率是:

  h(x)=h(y)的函數數量所有的函數=∣H∣m∣H∣=1m.\frac{h(x)=h(y)的函數數量}{所有的函數}

  =\frac{\frac{|\mathcal{H}|}{m}}{|\mathcal{H}|}=\frac{1}{m}.所有的函數h(x)=h(y)的函數數量=∣H∣m∣H∣=m1.

  定理1:隨機均勻地從H\mathcal{H}H(H\mathcal{H}H是全域的)選擇hhh,如果我們現在已經把nnn個輸入放入了hash表TTT中了,則再給一個輸入xxx,有

  E[hash表T中元素和x碰撞的數量]

  其中E[⋅]E[\cdot]E[⋅]表示期望。

  [定理1的重要性] 通過證明上述定理,我們就可以說,如果存在H\mathcal{H}H是全域的,那麼最終在hash表TTT中元素的分佈(在平均意義上)是均勻的。

  定理1的證明. 設CxC_{x}Cx表示在hash表TTT中的隨機元素和xxx碰撞的數量,設

  Cxy={1if h(x)=h(y)0if 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[Cx]=E[∑y∈T−xCxy]=∑y∈T−xE[Cxy]因爲期望的線性性質=∑y∈T−x1m=(n−1)1m

  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[∑y∈T−xCxy]=∑y∈T−xE[Cxy]=∑y∈T−xm1=(n−1)m1

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

  3-構造一個全域哈希H\mathcal{H}H

  定理2: 按照如下四個步驟構造的H\mathcal{H}H是全域的:

  (條件)令mmm等於一個素數;

  (初始準備)將輸入kkk寫成r+1r+1r+1個數字:k=k=k=,其中ki∈{0,1,...,m−1}k_i\in\{0, 1, ..., m-1\}ki∈{0,1,...,m−1}(等價於將kkk用mmm進製表示);

  (隨機)隨機選擇一個a=a=a=,其中ai∈0,1,...,m−1a_i\in{0, 1,..., m-1}ai∈0,1,...,m−1;

  (hash函數)ha(k)=(∑i=0i=rai×ki)mod  mh_a(k)=(\sum_{i=0}^{i=r}a_i\times k_i) \mod mha(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鄭州人流手術多少錢 http://mobile.sgyy029.com/

  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])


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章