簡介
哈稀函數按照定義能夠實現一個僞隨機數生成器(PRNG),從這個角度能夠獲得一個公認的結論:哈希函數之間性能的比較能夠經過比較其在僞隨機生成方面的比較來衡量。
一些經常使用的分析技術,例如
泊松
分佈可
用於分析不一樣的哈希函數對不一樣的數據的碰撞率(collision rate)
。通常來講,對任意一類的數據存在一個理論上完美的哈希函數
。
這個
完美的
哈希函數
定義是
沒有
發生任何
碰撞,這
意味着沒有
出現
重複
的散列
值
。
在現實中
它
很難
找到一個完美的
哈希
散列
函數,並且這種完美函數的趨近變種在
實際應用中的做用
是至關
有限的。
在實踐中
人們廣泛
認識到,
一個完美
哈希函數
的哈希
函數
,就是在
一個
特定的數據
集上產生的
的碰撞
最少哈希的函數。
如今的問題是有各類類型的數據,有一些是高度隨機的,有一些有包含高緯度的圖形結構,這些都使得找到一個通用的哈希函數變得十分困難,即便是某一特定類型的數據,找到一個比較好的哈希函數也不是意見容易的事。咱們所能作的就是經過試錯方法來找到知足咱們要求的哈希函數。能夠從下面兩個角度來選擇哈希函數:
1.數據分佈
一個衡量的措施是考慮一個哈希函數是否能將一組數據的哈希值進行很好的分佈。要進行這種分析,須要知道碰撞的哈希值的個數,若是用鏈表來處理碰撞,則能夠分析鏈表的平均長度,也能夠分析散列值的分組數目。
2.哈希函數的效率
另個一個衡量的標準是哈希函數獲得哈希值的效率。一般,包含哈希函數的算法的算法複雜度都假設爲O(1),這就是爲何在哈希表中搜索數據的時間複雜度會被認爲是"平均爲O(1)的複雜度",而在另一些經常使用的數據結構,好比圖(一般被實現爲紅黑樹),則被認爲是O(logn)的複雜度。
一個好的哈希函數必修在理論上很是的快、穩定而且是可肯定的。一般哈希函數不可能達到O(1)的複雜度,可是哈希函數在字符串哈希的線性的搜索中確實是很是快的,而且一般哈希函數的對象是較小的主鍵標識符,這樣整個過程應該是很是快的,而且在某種程度上是穩定的。
在這篇文章中介紹
的哈希
函數
被稱爲
簡單的哈希
函數。
它們一般
用於
散列(
哈希
字符串
)數據
。
它們被用來
產生一種
在諸如
哈希
表的
關聯容器
使用的key
。
這些
哈希
函數不是
密碼安全的
,
很容易經過
顛倒
和組合不一樣數據的
方式
產生
徹底相同的哈希
值。
哈希函數一般是由他們產生哈希值的方法來定義的,有兩種主要的方法:
1.基於加法和乘法的散列
這種方式是經過遍歷數據中的元素而後每次對某個初始值進行加操做,其中加的值和這個數據的一個元素相關。一般這對某個元素值的計算要乘以一個素數。
2.基於移位的散列 html
和加法散列相似,基於移位的散列也要利用字符串數據中的每一個元素,可是和加法不一樣的是,後者更多的而是進行位的移位操做。一般是結合了左移和右移,移的位數的也是一個素數。每一個移位過程的結果只是增長了一些積累計算,最後移位的結果做爲最終結果。 java
哈希函數和素數 算法
沒有人能夠證實素數和僞隨機數生成器之間的關係,可是目前來講最好的結果使用了素數。僞隨機數生成器如今是一個統計學上的東西,不是一個肯定的實體,因此對其的分析只能對整個的結果有一些認識,而不能知道這些結果是怎麼產生的。若是能進行更具體的研究,也許咱們能更好的理解哪些數值比較有效,爲何素數比其餘數更有效,爲何有些素數就不行,若是能用可再現的證實來回答這些問題,那麼咱們就能設計出更好的僞隨機數生成器,也可能獲得更好的哈希函數。 編程
圍繞着哈希函數中的素數的使用的基本的概念是,利用一個素質來改變處理的哈希函數的狀態值,而不是使用其餘類型的數。處理這個詞的意思就是對哈希值進行一些簡單的操做,好比乘法和加法。這樣獲得的一個新的哈希值必定要在統計學上具備更高的熵,也就是說不能有爲偏向。簡單的說,當你用一個素數去乘一堆隨機數的時候,獲得的數在bit這個層次上是1的機率應該接近0.5。沒有具體的證實這種不便向的現象只出如今使用素數的狀況下,這看上去只是一個自我宣稱的直覺上的理論,並被一些業內人士所遵循。 緩存
決定什麼是正確的,甚至更好的方法和對散列素數的使用最好的組合仍然是一個頗有黑色藝術。沒有單一的方法能夠宣稱本身是最終的通用散列函數。最好的一所能作的就是經過試錯演進和得到適當的散列算法,以知足其須要的統計分析方法。 安全
位偏向 服務器
位序列發生器是純粹隨機的或者說在某種程度上肯定性的,能夠按照必定的機率產生某種狀態或相反狀態的比特,這個機率就是位偏向。在純粹隨機的狀況下,產生高位或者低位的位偏向應該是50%。 網絡
而後在僞隨機產生器中,算法將決定在產生器在最小輸出模塊的位偏向。 數據結構
假設一個PRNG的產生8位做爲其輸出塊。出於某種緣由,MSB始終是設置爲高,MSB的位偏向將是100%的機率被置高。這一結論是,即便有256個本PRNG的產生可能的值,值小於128將永遠不會產生。爲簡單起見,假設其餘位正在生成純粹是隨機的,那麼有平等的機會,128和255之間的任何值將產生,可是在同一時間,有0%的機會,一個小於128的值會產生。 函數
全部PRNGs,不管是雜湊函數,密碼,msequences或其餘任何產生比特流的產生器都會有這樣一個位偏向。大多數PRNGs他們將嘗試收斂位偏向到一個肯定值,流密碼就是一個例子,而其餘產生器在不肯定的位偏向下效果更好。
混合或位序列加擾是一種產生在一個共同的平等流位偏向的方法。雖然咱們必須要當心,以確保他們不會混合至發散位偏向。密碼學中的一個混合使用的形式被稱爲雪崩,這就是一個位塊使用用另外一個塊來替換或置換混合在一塊兒,而另外一塊產生與其餘快混合的輸出。
正以下圖中顯示的,雪崩過程始於一個或多個二進制數據塊。對數據中的某些位操做(一般是一些輸入敏感位入減小位邏輯)生產的第i層片數據。而後重複這個過程是在第i層數據,以生成一個i+1個層數據,是當前層的位數將小於或等於前層的位數。
這一反覆的過程將致使一個依靠以前數據全部位的位。應該指出的是,下圖是一個單純的歸納,雪崩過程不必定是這一進程的惟一形式。
各類形式的哈希
哈希是一個在現實世界中將數據映射到一個標識符的工具,下面是哈希函數的一些經常使用領域:
1.字符串哈希
在數據存儲領域,主要是數據的索引和對容器的結構化支持,好比哈希表。
2.加密哈希
用於數據/用戶覈查和驗證。一個強大的加密哈希函數很難從結果再獲得原始數據。加密哈希函數用於哈希用戶的密碼,用來代替密碼自己存在某個服務器撒很難過。加密哈希函數也被視爲不可逆的壓縮功能,可以表明一個信號標識的大量數據,能夠很是有用的判斷當前的數據是否已經被篡改(好比MD5),也能夠做爲一個數據標誌使用,以證實了經過其餘手段加密文件的真實性。
3.幾何哈希
這個哈希表用於在計算機視覺領域,爲在任意場景分類物體的探測。
最初選擇的過程涉及一個地區或感興趣的對象。從那裏使用,如Harris角檢測器(HCD的),尺度不變特徵變換(SIFT)或速成式的強大功能(衝浪),一組功能的仿射提取這被視爲表明仿射不變特徵檢測算法表示對象或地區。這一套有時被稱爲宏觀功能或功能的星座。對發現的功能的性質和類型的對象或地區被列爲它可能仍然是可能的匹配兩個星座的特色,即便可能有輕微的差別(如丟失或異常特徵)兩集。星座,而後說是功能分類設置。
哈希值是計算從星座的特性。這一般是由最初定義一個地方的哈希值是爲了居住空間中完成- 在這種狀況下,散列值是一個多層面的價值,定義的空間正常化。再加上計算的哈希值另外一個進程,決定了兩個哈希值之間的距離是必要的過程-一個距離測量是必需的,而不是一個肯定性的平等經營者因爲對星座的哈希值計算到了可能的差距問題。也由於簡單的歐氏距離度量的本質上是無效的,其結果是自動肯定特定空間的距離度量已成爲學術界研究的活躍領域處理這類空間的非線性性質。
幾何散列包括各類汽車分類的從新檢測中任意場景的目的,典型的例子。檢測水平能夠多種多樣,從剛檢測是不是車輛,到特定型號的車輛,在特定的某個車輛。
4.布隆過濾器
布隆過濾器
容許
一個很是大範圍內的值被一個小不少的內存鎖表明
。
在計算機科學
,這是
衆所周知
的關聯
查詢
,並在
關聯容器
的核心理念。
Bloom Filter的
實現
經過
多種不一樣的
hash函數
使用,也可
經過容許
一個
特定
值的存在
有
必定
的偏差
機率
會員
查詢結果
的。
布隆過濾器
的
保證
提供的是
,對於任何
會員國
的查詢
就永遠不會再
有
假陰性
,但有
多是假
陽性。
假陽性
的機率能夠
經過改變
控制
爲
布隆過濾器
,並
經過不一樣的
hash函數的數量
所使用的
表的大小。
隨後的研究
工做集中在
的
散列函數和哈希表以及
Mitzenmacher的布隆過濾器
等
領域。
建議
對這種
結構,
在數據
被
散列
熵
最實用的用法
有助於
哈希
函數
熵,
這
是
理論成果
上
締結一項
最佳的
布隆過濾器
(一個
提供
給定
一個
最低的
進一步
致使
假陽性
的可能性
表的大小
或
反之亦然
)提供
假陽性
的機率
定義
用戶能夠
建造
最多
也做爲
兩種大相徑庭的
兩兩獨立
的哈希
散列函數
已知功能
,大大提升了
查詢
效率
的成員
。
布隆過濾器
一般存在於
諸如
拼寫
檢查器
,字符串匹配
算法,
網絡數據包
分析工具和
網絡/ Internet
緩存
的應用程序。
通用的哈希函數庫有下面這些混合了加法和一位操做的字符串哈希算法。下面的這些算法在用法和功能方面各有不一樣,可是均可以做爲學習哈希算法的實現的例子。(其餘版本代碼實現見下載)
1.RS
從Robert
Sedgwicks的
Algorithms in C一書中獲得了。
我(原文做者)已經
添加了一些
簡單的優化
的
算法,以
加快
其
散列
過程。
- public long RSHash(String str)
- {
- int b = 378551;
- int a = 63689;
- long hash = 0;
- for(int i = 0; i < str.length(); i++)
- {
- hash = hash * a + str.charAt(i);
- a = a * b;
- }
- return hash;
- }
2.JS
Justin Sobel寫的一個位操做的哈希函數。
- public long JSHash(String str)
- {
- long hash = 1315423911;
- for(int i = 0; i < str.length(); i++)
- {
- hash ^= ((hash << 5) + str.charAt(i) + (hash >> 2));
- }
- return hash;
- }
3.PJW
該散列
算法是基於貝爾實驗室的
彼得J
溫伯格
的的研究
。在Compilers一書中
(原則,
技術
和工具)
,
建議
採用這個算法的
散列
函數
的哈希
方法。
- public long PJWHash(String str)
- {
- long BitsInUnsignedInt = (long)(4 * 8);
- long ThreeQuarters = (long)((BitsInUnsignedInt * 3) / 4);
- long OneEighth = (long)(BitsInUnsignedInt / 8);
- long HighBits = (long)(0xFFFFFFFF) << (BitsInUnsignedInt - OneEighth);
- long hash = 0;
- long test = 0;
- for(int i = 0; i < str.length(); i++)
- {
- hash = (hash << OneEighth) + str.charAt(i);
- if((test = hash & HighBits) != 0)
- {
- hash = (( hash ^ (test >> ThreeQuarters)) & (~HighBits));
- }
- }
- return hash;
- }
4.ELF
和PJW很類似,在Unix系統中使用的較多。
- public long ELFHash(String str)
- {
- long hash = 0;
- long x = 0;
- for(int i = 0; i < str.length(); i++)
- {
- hash = (hash << 4) + str.charAt(i);
- if((x = hash & 0xF0000000L) != 0)
- {
- hash ^= (x >> 24);
- }
- hash &= ~x;
- }
- return hash;
- }
5.BKDR
這個算法來自Brian Kernighan 和 Dennis Ritchie的 The C Programming Language。這是一個很簡單的哈希算法,使用了一系列奇怪的數字,形式如31,3131,31...31,看上去和DJB算法很類似。(參照我以前一篇博客,這個就是Java的字符串哈希函數)
- public long BKDRHash(String str)
- {
- long seed = 131; // 31 131 1313 13131 131313 etc..
- long hash = 0;
- for(int i = 0; i < str.length(); i++)
- {
- hash = (hash * seed) + str.charAt(i);
- }
- return hash;
- }
6.SDBM
這個算法在開源的SDBM中使用,彷佛對不少不一樣類型的數據都能獲得不錯的分佈。
- public long SDBMHash(String str)
- {
- long hash = 0;
- for(int i = 0; i < str.length(); i++)
- {
- hash = str.charAt(i) + (hash << 6) + (hash << 16) - hash;
- }
- return hash;
- }
7.DJB
這個算法是Daniel J.Bernstein 教授發明的,是目前公佈的最有效的哈希函數。
- public long DJBHash(String str)
- {
- long hash = 5381;
- for(int i = 0; i < str.length(); i++)
- {
- hash = ((hash << 5) + hash) + str.charAt(i);
- }
- return hash;
- }
8.DEK
由偉大的Knuth在《編程的藝術 第三卷》的第六章排序和搜索中給出。
- public long DEKHash(String str)
- {
- long hash = str.length();
- for(int i = 0; i < str.length(); i++)
- {
- hash = ((hash << 5) ^ (hash >> 27)) ^ str.charAt(i);
- }
- return hash;
- }
9.AP
這是本文做者Arash Partow貢獻的一個哈希函數,繼承了上面以旋轉覺得和加操做。代數描述:
- public long APHash(String str)
- {
- long hash = 0xAAAAAAAA;
- for(int i = 0; i < str.length(); i++)
- {
- if ((i & 1) == 0)
- {
- hash ^= ((hash << 7) ^ str.charAt(i) * (hash >> 3));
- }
- else
- {
- hash ^= (~((hash << 11) + str.charAt(i) ^ (hash >> 5)));
- }
- }
- return hash;
- }
這裏有一個關於這些算法的評測,能夠稍微看看,本身也能夠簡單測試下,我在VSM試驗中的測試,這些算法沒有太大的性能差別,多是數據量較小的緣故。
各版本哈希代碼下載