完美雜湊(perfect hashing)
通用雜湊讓碰撞平均罕見,但罕見不是零——有時你想把一個靜態的鍵集(比如一本固定的詞典)存起來,使得每一次查詢都保證恰好一次探測、完全沒有碰撞。完美雜湊為一個已知、固定的集合達成這點:量身打造一個雜湊函數,使那些鍵中沒有任何兩個落在同一個槽。
精確地說:給定一個固定的 n 個鍵的集合 S,完美雜湊函數把 S 的鍵無碰撞地映入一個表,於是查詢是真正的最壞情況 O(1)。著名的 FKS 建構(Fredman, Komlos, Szemeredi)用通用雜湊以兩層方式建造它。頂層用一個通用函數把 n 個鍵雜湊進 n 個桶;有些桶會拿到好幾個鍵。對每個裝 b 個鍵的桶,建一個大小為 b^2 的小型第二層表,並(藉由試幾個隨機通用函數)挑一個把那 b 個鍵零碰撞雜湊的函數——這之所以可行,是因為以大小 b^2 的表,一個隨機通用函數無碰撞的機率至少二分之一,所以試幾次就成功。巧妙的計數事實是:儘管每個第二層表是其桶大小的平方,所有 b^2 值之總和的期望卻只有 O(n),因此整個結構使用線性空間。
完美雜湊在你有一個固定鍵集、且同時需要保證的常數時間查詢與線性空間時很重要——編譯器的關鍵字表、唯讀詞典、路由器。它示範了如何在建構時花一次隨機性,去買到查詢時確定性的最壞情況保證(一種「拉斯維加斯建造」餵給確定性查詢)。誠實的提醒:經典完美雜湊假設鍵集是靜態的;插入新鍵可能逼出代價高昂的重建。動態變體(布穀雜湊、動態完美雜湊)以更多機制及攤還或期望界限為代價,恢復了更新能力。
存 4 個固定鍵:頂層雜湊把兩個鍵送到桶 0、各一個到桶 1 與桶 3。桶 0 的兩個鍵得到一個大小 4(= 2^2)的第二層表,並挑一個讓它們不碰撞的通用函數;空桶與單元桶幾乎不需要什麼。總空間保持 O(n),每次查詢恰好兩次探測。
兩層通用雜湊為固定鍵集帶來 O(n) 空間中保證的 O(1) 查詢。
完美雜湊給出最壞情況 O(1) 的查詢,但僅限於靜態集合;隨機性住在建造函數的過程中,事後加鍵可能需要重建。空間之所以保持線性,是因為各桶大小平方之和在期望上是 O(n),而非儘管每桶是平方仍然如此。