Hash哈希(一)
哈希是大家比較常見一個詞語,在編程中也經常用到,但是大多數人都是知其然而不知其所以然,再加上這幾天想寫一個一致性哈希算法,突然想想對哈希也不是很清楚,所以,抽點時間總結下Hash知識。本文參考了很多博文,感謝大家的無私分享。
基本概念
Hash,一般翻譯做“散列”,也有直接音譯為“哈希”的。那么哈希函數的是什么樣的?大概就是 value = hash(key),我們希望key和value之間是唯一的映射關系。
大家使用的最多的就是哈希表(Hash table,也叫散列表),是根據關鍵碼值(Key value)而直接進行訪問的數據結構,通過把關鍵碼值映射到表中一個位置來訪問記錄,以加快查找的速度,這個映射函數叫做哈希函數或散列函數。
實際中的Hash主要有兩種應用:加密和壓縮。在加密方面,Hash哈希是把一些不同長度的信息轉化成雜亂的128位的編碼,這些編碼值叫做HASH值,最廣泛應用的Hash算法有MD4、MD5、SHA1。在壓縮方面,Hash哈希是指把一個大范圍映射到一個小范圍,往往是為了節省空間,使得數據容易保存。
Hash的特點
主要原理就是把大范圍映射到小范圍,因此輸入范圍必須和小范圍相當或者比它更小,否則增加沖突。
Hash函數逼近單向函數,所以可以用來對數據進行加密。(單項函數:如果某個函數在給定輸入的時候,很容易計算出其結果來;而當給定結果的時候,很難計算出輸入來)
不同的應用對Hash函數有着不同的要求:用於加密的Hash函數主要考慮它和單項函數的差距,而用於查找的Hash函數主要考慮它映射到小范圍的沖突率。
Hash算法
Hash的產生方式大體可以分為三種基本方法:加法、乘法和移位。
加法哈希是通過遍歷數據中的元素然后每次對某個初始值進行加操作,其中加的值和這個數據的一個元素相關。
/******************************** *加法哈希 *@key 輸入字符串 *@prime 素數 ********************************/ int additiveHash(string key, int prime) { int hash, i; for(hash = key.length(), i = 0; i < key.length(); ++i) { hash += int(key.at(i)); } return (hash%prime); }
乘法哈希是通過遍歷數據中的元素然后每次對初始值進行乘法操作,其中乘的值無需和數據有關系。
/******************************** *乘法哈希 *@key 輸入字符串 *@prime 素數 ********************************/ int bernstein(string key) { int hash, i; for(hash = 0, i = 0; i < key.length(); ++i) { hash = 33*hash + int(key.at(i)); } return hash; } /******************************** *32位FNV算法(乘法) *@key 輸入字符串 *@prime 素數 ********************************/ int M_SHIFT = 0; int M_MASK = 0x8765fed1; int FNVHash(string key) { int hash = (int)2166136261L; for(int i = 0; i < key.length(); ++i) { hash = (hash * 16777619)^int(key.at(i)); } if(M_SHIFT == 0) return hash; return (hash ^ (hash >> M_SHIFT)) & M_MASK; }
在JAVA中,哈希函數使用的就是乘法哈希:
/** * JAVA自己帶的算法 */ public static int java(String str) { int h = 0; int off = 0; int len = str.length(); for (int i = 0; i < len; i++) { h = 31 * h + str.charAt(off++); } return h; }
移位哈希是通過遍歷數據中的元素然后每次對初始值進行移位操作。
/******************************** *旋轉哈希(移位) *@key 輸入字符串 *@prime 素數 ********************************/ int rotatingHash(string key, int prime) { int hash, i; for(hash = key.length(), i = 0; i < key.length(); ++i) { hash = (hash << 4)^(hash >> 28)^int(key.at(i)); } return (hash%prime); }
實際情況下,很多哈希函數都是包含加法、乘法和移位操作來實現的,例如MD5。
問題
為什么prime的取值是素數? 有科學依據么? 有待驗證
有人是這樣說的取素數,可以降低碰撞的概率。但是並沒有很好的說明原因,如果哪位有想法希望能留下您的想法,分享給大家。