經典的hash演算法 常見hash 雜湊演算法

2021-07-13 06:41:18 字數 2957 閱讀 4906

計算理論中,沒有hash函式的說法,只有單向函式的說法。所謂的單向函式,是乙個複雜的定義,大家可以去看計算理論或者密碼學方面的資料。用「人 類」的語言描述單向函式就是:如果某個函式在給定輸入的時候,很容易計算出其結果來;而當給定結果的時候,很難計算出輸入來,這就是單項函式。各種加密函 數都可以被認為是單向函式的逼近。hash函式(或者成為雜湊函式)也可以看成是單向函式的乙個逼近。即它接近於滿足單向函式的定義。

hash函式還有另外的含義。實際中的hash函式是指把乙個大範圍對映到乙個小範圍。把大範圍對映到乙個小範圍的目的往往是為了節省空間,使得資料容易儲存。除此以外,hash函式往往應用於查詢上。所以,在考慮使用hash函式之前,需要明白它的幾個限制:

1. hash的主要原理就是把大範圍對映到小範圍;所以,你輸入的實際值的個數必須和小範圍相當或者比它更小。不然衝突就會很多。

2. 由於hash逼近單向函式;所以,你可以用它來對資料進行加密。

3. 不同的應用對hash函式有著不同的要求;比如,用於加密的hash函式主要考慮它和單項函式的差距,而用於查詢的hash函式主要考慮它對映到小範圍的衝突率。

應用於加密的hash函式已經**過太多了,在作者的部落格裡面有更詳細的介紹。所以,本文只**用於查詢的hash函式。

hash函式應用的主要物件是陣列(比如,字串),而其目標一般是乙個int型別。以下我們都按照這種方式來說明。

一般的說,hash函式可以簡單的劃分為如下幾類:

1. 加法hash;

2. 位運算hash;

3. 乘法hash;

4. 除法hash;

5. 查表hash;

6. 混合hash;

下面詳細的介紹以上各種方式在實際中的運用。

一 加法hash

所謂的加法hash就是把輸入元素乙個乙個的加起來構成最後的結果。標準的加法hash的構造如下:

static int additivehash(string key, int prime)

這裡的prime是任意的質數,看得出,結果的值域為[0,prime-1]。

二 位運算hash

這型別hash函式通過利用各種位運算(常見的是移位和異或)來充分的混合輸入元素。比如,標準的旋轉hash的構造如下:

static int rotatinghash(string key, int prime)

先移位,然後再進行各種位運算是這種型別hash函式的主要特點。比如,以上的那段計算hash的**還可以有如下幾種變形:

1.     hash = (hash<<5)^(hash>>27)^key.charat(i);

2.     hash += key.charat(i);

hash += (hash << 10);

hash ^= (hash >> 6);

3.     if((i&1) == 0)

else

4.     hash += (hash<<5) + key.charat(i);

5.     hash = key.charat(i) + (hash<<6) + (hash>>16) – hash;

6.     hash ^= ((hash<<5) + key.charat(i) + (hash>>2));

三 乘法hash

這種型別的hash函式利用了乘法的不相關性(乘法的這種性質,最有名的莫過於平方取頭尾的隨機數生成演算法,雖然這種演算法效果並不好)。比如,

static int bernstein(string key)

以及改進的fnv演算法:

public static int fnvhash1(string data)

除了乘以乙個固定的數,常見的還有乘以乙個不斷改變的數,比如:

static int rshash(string str)

return (hash & 0x7fffffff);

}雖然adler32演算法的應用沒有crc32廣泛,不過,它可能是乘法hash裡面最有名的乙個了。關於它的介紹,大家可以去看rfc 1950規範。

四 除法hash

除法和乘法一樣,同樣具有表面上看起來的不相關性。不過,因為除法太慢,這種方式幾乎找不到真正的應用。需要注意的是,我們在前面看到的hash的 結果除以乙個prime的目的只是為了保證結果的範圍。如果你不需要它限制乙個範圍的話,可以使用如下的**替代」hash%prime」: hash = hash ^ (hash>>10) ^ (hash>>20)。

五 查表hash

查表hash最有名的例子莫過於crc系列演算法。雖然crc系列演算法本身並不是查表,但是,查表是它的一種最快的實現方式。查表hash中有名的例子有:universal hashing和zobrist hashing。他們的**都是隨機生成的。

六 混合hash

混合hash演算法利用了以上各種方式。各種常見的hash演算法,比如md5、tiger都屬於這個範圍。它們一般很少在面向查詢的hash函式裡面使用。

七 對hash演算法的評價

這個頁面提供了對幾種流行hash演算法的評價。我們對hash函式的建議如下:

1. 字串的hash。最簡單可以使用基本的乘法hash,當乘數為33時,對於英文單詞有很好的雜湊效果(小於6個的小寫形式可以保證沒有衝突)。複雜一點可以使用fnv演算法(及其改進形式),它對於比較長的字串,在速度和效果上都不錯。

2. 長陣列的hash。可以使用這種演算法,它一次運算多個位元組,速度還算不錯。

八 後記

本文簡略的介紹了一番實際應用中的用於查詢的hash演算法。hash演算法除了應用於這個方面以外,另外乙個著名的應用是巨型字串匹配(這時的 hash演算法叫做:rolling hash,因為它必須可以滾動的計算)。設計乙個真正好的hash演算法並不是一件容易的事情。做為應用來說,選擇乙個適合的演算法是最重要的。

九 陣列hash

inline int hashcode(const int *v)

注:雖說以上的hash能極大程度地避免衝突,但是衝突是在所難免的。所以無論用哪種hash函式,都要加上處理衝突的方法。

幾種經典的hash演算法

計算理論中,沒有hash函式的說法,只有單向函式的說法。所謂的單向函式,是乙個複雜的定義,大家可以去看計算理論或者密碼學方面的資料。用 人 類 的語言描述單向函式就是 如果某個函式在給定輸入的時候,很容易計算出其結果來 而當給定結果的時候,很難計算出輸入來,這就是單項函式。各種加密函 數都可以被認為...

幾種經典的hash演算法

計算理論中,沒有hash函式的說法,只有單向函式的說法。所謂的單向函式,是乙個複雜的定義,大家可以去看計算理論或者密碼學方面的資料。用 人 類 的語言描述單向函式就是 如果某個函式在給定輸入的時候,很容易計算出其結果來 而當給定結果的時候,很難計算出輸入來,這就是單項函式。各種加密函 數都可以被認為...

幾種經典的hash演算法

計算理論中,沒有hash函式的說法,只有單向函式的說法。所謂的單向函式,是乙個複雜的定義,大家可以去看計算理論或者密碼學方面的資料。用 人 類 的語言描述單向函式就是 如果某個函式在給定輸入的時候,很容易計算出其結果來 而當給定結果的時候,很難計算出輸入來,這就是單項函式。各種加密函 數都可以被認為...