hash算法的意義在於提供了一種快速存取數據的方法,它用一種算法建立鍵值與真實值之間的對應關系,(每一個真實值只能有一個鍵值,但是一個鍵值可以對應多個真實值),這樣可以快速在數組等條件中里面存取數據.
在網上看了不少HASH資料,所以對HASH的相關資料進行總結和收集。
//HashTable.h template class HashTable{ public : HashTable( int count ) ; void put( T* t ,int key ) ; T* get( int key ) ; private : T** tArray ; }
//HashTable.cpp template HashTable::HashTable( int count ){ tArray = new T*[count] ;} template void HashTable::put( T* t , int key ){ this->tArray[ key ] = t ;}template T* HashTable::get( int key ) { return this->tArray[ key ] ;}
這樣,我們只要知道key值,就可以快速存取T類型的數據,而不用像在鏈表等數據結構中查找一樣, 要找來找去的. 至於key值,一般都是用某種算法(所謂的Hash算法)算出來的.例如:字符串的Hash算法, char* value = "hello"; int key = (((((((27* (int)'h'+27)* (int)'e') + 27) * (int)'l') + 27) * (int)'l' +27) * 27 ) + (int)'o' ; Hash函數處理流程Hash,一般翻譯做"散列",也有直接音譯為"哈希"的,就是把任意長度的輸入(又叫做預映射, pre-image),通過散列算法,變換成固定長度的輸出,該輸出就是散列值。這種轉換是一種壓縮映射,也就是,散列值的空間通常遠小於輸入的空間,不同的輸入可能會散列成相同的輸出,而不可能從散列值來唯一的確定輸入值。簡單的說就是一種將任意內容的輸入轉換成相同長度輸出的加密方式.
我來做一個比喻吧。
我們有很多的小豬,每個的體重都不一樣,假設體重分布比較平均(我們考慮到公斤級別),我們按照體重來分,划分成100個小豬圈。
然后把每個小豬,按照體重趕進各自的豬圈里,記錄檔案。
好了,如果我們要找某個小豬怎么辦呢?我們需要每個豬圈,每個小豬的比對嗎?
當然不需要了。
我們先看看要找的這個小豬的體重,然后就找到了對應的豬圈了。
在這個豬圈里的小豬的數量就相對很少了。
我們在這個豬圈里就可以相對快的找到我們要找到的那個小豬了。
對應於hash算法。
就是按照hashcode分配不同的豬圈,將hashcode相同的豬放到一個豬圈里。
查找的時候,先找到hashcode對應的豬圈,然后在逐個比較里面的小豬。
所以問題的關鍵就是建造多少個豬圈比較合適。
如果每個小豬的體重全部不同(考慮到毫克級別),每個都建一個豬圈,那么我們可以最快速度的找到這頭豬。缺點就是,建造那么多豬圈的費用有點太高了。
如果我們按照10公斤級別進行划分,那么建造的豬圈只有幾個吧,那么每個圈里的小豬就很多了。我們雖然可以很快的找到豬圈,但從這個豬圈里逐個確定那頭小豬也是很累的。
所以,好的hashcode,可以根據實際情況,根據具體的需求,在時間成本(更多的豬圈,更快的速度)和空間本(更少的豬圈,更低的空間需求)之間平衡。
Hash算法有很多很多種類。具體的可以參考之前我寫的Hash算法的一些分析。本處給大家提供一個集合了很多使用的Hash算法的類,應該可以滿足不少人的需要的:
Java代碼
常用的字符串Hash函數還有ELFHash,APHash等等,都是十分簡單有效的方法。這些函數使用位運算使得每一個字符都對最后的函數值產生影響。另外還有以MD5和SHA1為代表的雜湊函數,這些函數幾乎不可能找到碰撞。
常用字符串哈希函數有BKDRHash,APHash,DJBHash,JSHash,RSHash,SDBMHash,PJWHash,ELFHash等等。對於以上幾種哈希函數,我對其進行了一個小小的評測。
Hash函數 數據1 數據2 數據3 數據4 數據1得分 數據2得分 數據3得分 數據4得分 平均分
BKDRHash 2 0 4774 481 96.55 100 90.95 82.05 92.64
APHash 2 3 4754 493 96.55 88.46 100 51.28 86.28
DJBHash 2 2 4975 474 96.55 92.31 0 100 83.43
JSHash 1 4 4761 506 100 84.62 96.83 17.95 81.94
RSHash 1 0 4861 505 100 100 51.58 20.51 75.96
SDBMHash 3 2 4849 504 93.1 92.31 57.01 23.08 72.41
PJWHash 30 26 4878 513 0 0 43.89 0 21.95
ELFHash 30 26 4878 513 0 0 43.89 0 21.95
其中數據1為100000個字母和數字組成的隨機串哈希沖突個數。數據2為100000個有意義的英文句子哈希沖突個數。數據3為數據1的哈希值與1000003(大素數)求模后存儲到線性表中沖突的個數。數據4為數據1的哈希值與10000019(更大素數)求模后存儲到線性表中沖突的個數。
經過比較,得出以上平均得分。平均數為平方平均數。可以發現,BKDRHash無論是在實際效果還是編碼實現中,效果都是最突出的。APHash也是較為優秀的算法。DJBHash,JSHash,RSHash與SDBMHash各有千秋。PJWHash與ELFHash效果最差,但得分相似,其算法本質是相似的。
#define M 249997 #define M1 1000001 #define M2 0xF0000000 // RS Hash Function unsigned int RSHash(char*str) { unsigned int b=378551 ; unsigned int a=63689 ; unsigned int hash=0 ; while(*str) { hash=hash*a+(*str++); a*=b ; } return(hash % M); } // JS Hash Function unsigned int JSHash(char*str) { unsigned int hash=1315423911 ; while(*str) { hash^=((hash<<5)+(*str++)+(hash>>2)); } return(hash % M); } // P. J. Weinberger Hash Function unsigned int PJWHash(char*str) { unsigned int BitsInUnignedInt=(unsigned int)(sizeof(unsigned int)*8); unsigned int ThreeQuarters=(unsigned int)((BitsInUnignedInt*3)/4); unsigned int OneEighth=(unsigned int)(BitsInUnignedInt/8); unsigned int HighBits=(unsigned int)(0xFFFFFFFF)<<(BitsInUnignedInt-OneEighth); unsigned int hash=0 ; unsigned int test=0 ; while(*str) { hash=(hash<<OneEighth)+(*str++); if((test=hash&HighBits)!=0) { hash=((hash^(test>>ThreeQuarters))&(~HighBits)); } } return(hash % M); } // ELF Hash Function unsigned int ELFHash(char*str) { unsigned int hash=0 ; unsigned int x=0 ; while(*str) { hash=(hash<<4)+(*str++); if((x=hash&0xF0000000L)!=0) { hash^=(x>>24); hash&=~x ; } } return(hash % M); } // BKDR Hash Function unsigned int BKDRHash(char*str) { unsigned int seed=131 ;// 31 131 1313 13131 131313 etc.. unsigned int hash=0 ; while(*str) { hash=hash*seed+(*str++); } return(hash % M); } // SDBM Hash Function unsigned int SDBMHash(char*str) { unsigned int hash=0 ; while(*str) { hash=(*str++)+(hash<<6)+(hash<<16)-hash ; } return(hash % M); } // DJB Hash Function unsigned int DJBHash(char*str) { unsigned int hash=5381 ; while(*str) { hash+=(hash<<5)+(*str++); } return(hash % M); } // AP Hash Function unsigned int APHash(char*str) { unsigned int hash=0 ; int i ; for(i=0;*str;i++) { if((i&1)==0) { hash^=((hash<<7)^(*str++)^(hash>>3)); } else { hash^=(~((hash<<11)^(*str++)^(hash>>5))); } } return(hash % M); }