這個過濾器自己是一篇論文中提出的過濾器的簡化版本,去掉了計數功能,我以爲簡化版本應用的可能也很廣,專門寫一篇簡化版本的RSQF。RSQF全稱是rank-and-select based filter,一會咱們會專門來說這個rank-and-select是什麼。
原論文能夠到這裏下載:http://www3.cs.stonybrook.edu/~ppandey/files/SIGMOD17_Talk_CQF_long.pdfpython
過濾器分下面幾部分來說git
這部分不屬於原論文,這部分假設有一個比較好的Hash函數,能夠將原數據hash成n位數據,設n = q + r,這個r實際上控制着過濾器的假陽率和空間大小的平衡。假陽率是\(2^{-r}\),固然若是r越大,空間消耗也越大github
結構部分以下圖所示
算法
咱們分別來介紹這個結構中的各部分,其中occupieds這一行是1bit數組,元素只能表示1或0;runends也是1bit數組;remainders內每一個元素佔用r個bit,就是前一部分說的r。這個塊總共有\(2^q\)列,q也是前文說的q。數組
咱們設某一列的下標爲\(i\),則當有元素hash後的前q位對應到這一列的時候,\(occupieds[i] = 1\);咱們把這個元素放到對應位置上,那麼若是有多個元素對應這個位置,咱們要求,把具備相同前q位的元素放到相鄰位置。那麼何時表明結束呢?就是這些前q位相同的元素的最後一個元素對應位置\(runends[i] = 1\)。在圖中,相同顏色的元素其前q位是相同的,也就是說若是不考慮重複,他們都應該存在相同位置上的。函數
def MAY_CONTAIN(Q,x) """ 查找算法,Q表明存儲結構,x表明要查找的元素 """ b = h0(x) #計算hash值,取其前q位 if Q.occupieds[b] = 0: return 0 t = RANK(Q.occupieds,b) k = SELECT(Q.runends,t) v = h(x) #計算hash值取其後r位 while k >= b and Q.runends[k] = 0: if Q.remainders[k] = v: return 1 k -= 1 return false
RANK(Q,i)是找到在位置i以前,數組Q中有多少個1出現過
SELECT(Q,i)是找到數組Q中第i次出現1的位置spa
先經過找到在某個位置以前在occupieds上有多少個1,就表明有多少組相同前q位hash值的元素組,而後經過runends的第i個1,找到目前這個位置對應的組,這樣就能夠精準定位。3d
def find_first_unused_slot(Q,x): """ 找到第一個空位置,以便於在插入元素時向後移動元素 """ r = rank(Q.occupieds,x) s = select(Q.runends) while x < s: x = s + 1 r = RANK(Q.occupieds,x) s = SELECT(Q.runends,s) return x
def insert(Q,x): """ 插入一個元素 實質是先找到一個空位,而後把要插入位置以後的全部元素依次右移,直到把要插入位置空出來爲止 """ r = RANK(Q.occupieds,h0(x)) s = SELECT(Q.runends,r) if h0(x) > s: Q.remainders[h0(x)] = h1(x) Q.runends[h0(x)] = 1 else: s += 1 n = find_first_unused_slot(Q,s) while n > s: Q.remainders[n] = Q.remainers[n-1] Q.runends[n] = Q.remainers[n-1] n -= 1 Q.remainers[s] = h1(x) if Q.occupieds[h0(x)] == 1: Q.runends[s-1] = 0 Q.runends[s] = 1 Q.occupieds[h0(x)] = 1 return
原論文中其實還有一個在塊內加速的一個索引方式,在這裏沒有講到,想要詳細瞭解的能夠看原論文。若是想要看帶計數功能版本的過濾器也請看原論文,github上也有公開代碼。https://github.com/splatlab/cqfcode