HASH 與 隨機數

本文主要介紹一下hash與多值hash,然後在討論一下支撐hash的僞隨機數生成器。不感興趣者可以走了……


一:hash 與 多值hash:

野史

第一次見到hash,是在算法導論裏看到的,其中的桶排序就是使用了此類思想,當然也有專門介紹hash的章節。感覺卻是高明。後來在sqlseveral中也經常接觸,畢竟作爲join的一種優化方法經常都很靠譜。而多值hash則是在《數學之美》中接觸到的,其中用作垃圾郵件過濾器。兩個字:神奇。後來在一些分類統計中經常使用,感觸更深。hash的本質就是用一個函數根據內容本身計算出內容的存儲位置然後存儲之,取的時候自然也就是看到內容立刻就能計算出(而不是找出)目標的位置。

碰撞

這個計算位置的函數先放一下,假設已經有了,而且內容無規律,我們選擇了隨機函數;先討論碰撞問題。假設在一個100萬的序列中已經有1萬個位置被佔滿,那麼再放一個元素時他會落到別人的位置上的概率是1%。這就是碰撞的概率了。當然如果內容存在一定規律,我們不使用隨機函數作爲hash函數可能會完全不碰撞並且位置使用率很高,例如:如果要計算一篇文章的所有單字的字頻統計,就可以使用漢字的編碼所代表的數字做一個線性平移。但如果統計詞組的頻率呢?四字成語所代表數字可不小(2的64次方)。回到之前的問題,在一個100萬的序列中存儲一個元素時碰撞概率爲1%的問題。如果我們把每個元素存放在2個隨機的位置會發生什麼呢?明顯需要兩個位置同時碰撞纔會使信息真正遺失,而此時概率爲0.01%。方法很簡單,但有效。

最優多值

這個多值是不是月多越好呢?明顯不是,當一個元素需要1千萬個位置時恐怕那個百萬序列存不了幾個數字。在假設隨機函數完全隨機的情況下,我們要在一個M的序列中存放N個元素,每個元素佔據K個位置。最優K一個近似的偏小的估計值是M/N/e(也就是那個接近2.7的無理數)。後來我找到了一個更精確的計算方法,但結果不那麼容易記,在此也不說了。如果在實驗時發現上面的最優值與實際測試出入較大,在懷疑它之前,先懷疑自己的隨見函數是否真的是足夠隨機。這便是下面要講的——構建一個好的隨機數發生器。


二:隨機數發生器

再看導論之前,用過隨機數發生器,看過導論之後寫過。至於那些七大類八大綱就不扯了,說一下我用的第一個hash函數和最讓我滿意的一個。

乘法

乘法是指用一個無理數(比如sqrt(6))與需要存儲的內容所對應的唯一值(如ASCII碼代表的值)進行乘法後取小數部分,然後再用這個小數乘以散列長度。但我在使用的時候發現其碰撞概率比我的估計值要大了幾個數量級,最後看了幾種不同隨機數產生方法,把目標放在md5上。隨機數算法要儘可能簡單,下面是個邏輯過程,沒有照抄md5,但很類似。散列長度2**24.【就是一些移位與異或,符號採取的是Python中的】

1. a = ASCII(string); a1 = 0

2. if a <2**64,a = a*138647902547865326901【隨意一個大數,別取那些2^n的】

3.while (a>0):

4.       a1 = a1^(a&(2**24-1));  a = a>>24

5.return(a1)

這個方法還是不錯的,在我的實驗中,得到的碰撞概率比完全隨機假設時的理論結果還要低。這個原因是因爲多值hash時,多個隨機數發生器對應同一個種子時更傾向於產生不同的位置,相互之間碰撞概率低於隨機情況。至少目前我是這麼解釋。

檢驗

怎麼說明隨機函數是否隨機呢?看分佈圖等很有說服力,但是不夠嚴謹。在一過程中我之所以能夠區分出孰優孰劣也是因爲下面介紹的一個方法。

假設一個長度爲M的散列的散列函數完全隨機,存儲了N個不同元素後,其中有K個位置被佔據。忽略高階小量(三個或更多元素存在同一個位置),其中某一個元素髮生碰撞的概率爲(N-K)/N。而另一方面在在這個元素沒有放進去之前,(K-1)位置已經存儲元素(小概率被忽略),總長度M,碰撞概率(k-1)/M。於是根據我們存儲的唯一元素個數N,佔據位置數量K,和散列長度M,我們可以估計出兩個概率,而它們的含義相同【(N-K)/N=(k-1)/M】。即 這兩者也接近 函數越接近隨機。當N已經較大時(比如大於M的0.01倍,太大時高階小量就不能忽略了),在這個殘酷的檢驗中MD5的方法比乘法以及另外一個求餘法要好了不少。

結語

上週就說要寫的,今天終於寫完了,下次談談Hadoop的吧,哎,被老闆拉去做些裝機的苦力活,不知道會遇到些什麼困難,又會學些什麼東西。

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