ELFhash - 优秀的字符串哈希算法
ELFhash - 優(yōu)秀的字符串哈希算法
?
1.字符串哈希:
我們先從字符串哈希說(shuō)起 在很多的情況下,我們有可能會(huì)獲得大量的字符串,每個(gè)字符串有可能重復(fù)也有可能不重復(fù) C不像Python有字典類型的數(shù)據(jù)結(jié)構(gòu),我們沒(méi)有辦法吧字符串當(dāng)做是鍵值來(lái)保存,所以說(shuō)我們需要一種hash函數(shù)將每個(gè)字符串都盡可能減少?zèng)_突的情況下去應(yīng)設(shè)一個(gè)唯一的整形數(shù)據(jù),方便我們的保存,這里我們就引入了字符串hash算法現(xiàn)在,有非常多的字符串hash算法都很優(yōu)秀,本文主要面對(duì)ELFhash算法來(lái)表述,相對(duì)來(lái)說(shuō)比較的清晰
2.ELFhash
首先我需要聲明,字符串hash算法ELFhash的算法的形成的三列的均勻性我不會(huì)證明 根據(jù)其他的大牛的描述,ELFhash算法對(duì)于長(zhǎng)字符串和短字符串都有優(yōu)良的效率,以下的數(shù)據(jù)援引劉愛(ài)貴大神的實(shí)驗(yàn)數(shù)據(jù):Hash應(yīng)用中,字符串是最為常見(jiàn)的關(guān)鍵字,應(yīng)用非常普通,現(xiàn)在的程序設(shè)計(jì)語(yǔ)言中基本上都提供了字符串hash表的支持。字符串hash函數(shù)非常多,常見(jiàn)的主要有Simple_hash, RS_hash, JS_hash, PJW_hash, ELF_hash, BKDR_hash, SDBM_hash, DJB_hash, AP_hash, CRC_hash等。它們的C語(yǔ)言實(shí)現(xiàn)見(jiàn)后面附錄代碼: hash.h, hash.c。那么這么些字符串hash函數(shù),誰(shuí)好熟非呢?評(píng)估hash函數(shù)優(yōu)劣的基準(zhǔn)主要有以下兩個(gè)指標(biāo):
(1)?散列分布性
即桶的使用率backet_usage = (已使用桶數(shù)) / (總的桶數(shù)),這個(gè)比例越高,說(shuō)明分布性良好,是好的hash設(shè)計(jì)。
(2) 平均桶長(zhǎng)
即avg_backet_len,所有已使用桶的平均長(zhǎng)度。理想狀態(tài)下這個(gè)值應(yīng)該=1,越小說(shuō)明沖突發(fā)生地越少,是好的hash設(shè)計(jì)。
hash函數(shù)計(jì)算一般都非常簡(jiǎn)潔,因此在耗費(fèi)計(jì)算時(shí)間復(fù)雜性方面判別甚微,這里不作對(duì)比。
?
評(píng)估方案設(shè)計(jì)是這樣的:
(1) 以200M的視頻文件作為輸入源,以4KB的塊為大小計(jì)算MD5值,并以此作為hash關(guān)鍵字;
(2) 分別應(yīng)用上面提到的各種字符串hash函數(shù),進(jìn)行hash散列模擬;
(3) 統(tǒng)計(jì)結(jié)果,用散列分布性和平均桶長(zhǎng)兩個(gè)指標(biāo)進(jìn)行評(píng)估分析。
?
測(cè)試程序見(jiàn)附錄代碼hashtest.c,測(cè)試結(jié)果如下表所示。從這個(gè)結(jié)果我們也可以看出,這些字符串hash函數(shù)真是不相仲伯,難以決出高低,所以實(shí)際應(yīng)用中可以根據(jù)喜好選擇。當(dāng)然,最好實(shí)際測(cè)試一下,畢竟應(yīng)用特點(diǎn)不大相同。其他幾組測(cè)試結(jié)果也類似,這里不再給出。
| Hash函數(shù) | 桶數(shù) | Hash調(diào)用總數(shù) | 最大桶長(zhǎng) | 平均桶長(zhǎng) | 桶使用率% |
| simple_hash | 10240 | 47198 | 16 | 4.63 | 99.00% |
| RS_hash | 10240 | 47198 | 16 | 4.63 | 98.91% |
| JS_hash | 10240 | 47198 | 15 | 4.64 | 98.87% |
| PJW_hash | 10240 | 47198 | 16 | 4.63 | 99.00% |
| ELF_hash | 10240 | 47198 | 16 | 4.63 | 99.00% |
| BKDR_hash | 10240 | 47198 | 16 | 4.63 | 99.00% |
| SDBM_hash | 10240 | 47198 | 16 | 4.63 | 98.90% |
| DJB_hash | 10240 | 47198 | 15 | 4.64 | 98.85% |
| AP_hash | 10240 | 47198 | 16 | 4.63 | 98.96% |
| CRC_hash | 10240 | 47198 | 16 | 4.64 | 98.77% |
3.原理:
首先,我們?cè)陂_(kāi)始之前需要明確幾點(diǎn) 1.unsigned int有4個(gè)字節(jié),32個(gè)比特位 2.異或操作中0是單位元,任何數(shù)與1異或相當(dāng)于取反 3.unsigned無(wú)符號(hào)類型的數(shù)據(jù)右移操作均是執(zhí)行邏輯右移(左高位自動(dòng)補(bǔ)0) 4.ELFhash算法的核心在于“影響“先附上代碼: [cpp]?view plain?copy ?
- unsigned?int?ELFhash(char?*str)??
- {??
- ????unsigned?int?hash=0;??
- ????unsigned?int?x=0;??
- ????while(*str)??
- ????{??
- ????????hash=(hash<<4)+*str;?????//1??
- ????????if((x=hash?&?0xf0000000)!=0)?????????//2??
- ????????{??
- ????????????hash^=(x>>24);???//影響5-8位,雜糅一次???3??
- ????????????hash&=~x;???//清空高四位????4??
- ????????}??
- ????????str++;???//5??
- ????}??
- ????return?(hash?&?0x7fffffff);????//6???
- }??
解釋: 首先我們的hash結(jié)果是一個(gè)unsigned int類型的數(shù)據(jù): 0000 0000 0000 0000 1.hash左移4位,將str插入(一個(gè)char有八位)這里我開(kāi)始也一直是懷疑的態(tài)度,那么第一個(gè)字節(jié)的高四位不就亂了嗎 實(shí)際上這也是我們的第一次雜糅,我們是故意這么做的,這里我們需要注意標(biāo)記一下,我們?cè)诘谝粋€(gè)字節(jié)的高四位做了第一次雜糅 2.x這里用0xf0000000獲取了hash的第四個(gè)字節(jié)的高四位,并用高四位作為掩碼做第二次雜糅 在這里我們首先聲明一下,因?yàn)槲覀兊腅LFhash強(qiáng)調(diào)的是每個(gè)字符都要對(duì)最后的結(jié)構(gòu)有影響,所以說(shuō)我們左移到一定程度是會(huì)吞掉最高的四位的,所以說(shuō)我們要將最高的四位先對(duì)串產(chǎn)生影響,再讓他被吞掉,之后的所有的影響都是疊加的,這就是多次的雜糅保證散列均勻,防止出現(xiàn)沖突的大量出現(xiàn) 3.x掩碼右移24位移動(dòng)到剛才的5-8位哪里在對(duì)5-8位進(jìn)行第二次雜糅 4.我們定時(shí)清空高四位,實(shí)際上這部操作我們完全沒(méi)有必要,但是算法要求,因?yàn)槲覀兿乱淮蔚淖笠茣?huì)自動(dòng)吞掉這四位//這里存疑,不會(huì)減少我們的hash的范圍? 5.str遞增,引入下一個(gè)字符進(jìn)行雜糅 6.返回一個(gè)缺失了最高符號(hào)位的無(wú)符號(hào)數(shù)(為了之后防止用到了有符號(hào)的時(shí)候造成的溢出)作為最后的hash值
4.Code:
[cpp]?view plain?copy ?- /*#include"iostream"?
- #include"cstdio"?
- #include"cstring"?
- ?
- using?namespace?std;?
- ?
- unsigned?int?a=0x80;?
- ?
- int?main()?
- {?
- ????printf("%d\n",a>>1);???//無(wú)符號(hào)數(shù)實(shí)行邏輯右移??
- ????return?0;?
- }?*/??
- ??
- #include"iostream"??
- #include"cstdio"??
- #include"cstring"??
- ??
- using?namespace?std;??
- ??
- unsigned?int?ELFhash(char?*str)??
- {??
- ????unsigned?int?hash=0;??
- ????unsigned?int?x=0;??
- ????while(*str)??
- ????{??
- ????????hash=(hash<<4)+*str;??
- ????????if((x=hash?&?0xf0000000)!=0)??
- ????????{??
- ????????????hash^=(x>>24);???//影響5-8位,雜糅一次???
- ????????????hash&=~x;???//清空高四位???
- ????????}??
- ????????str++;??
- ????}??
- ????return?(hash?&?0x7fffffff);???
- }??
- ??
- int?main()??
- {??
- ????char?data[100];??
- ????memset(data,0,sizeof(data));??
- ????scanf("%s",data);??
- ????printf("%d\n",ELFhash(data));??
- ????return?0;??
- }???
最后,按照我的思路來(lái)看的話,ELFhash最多可以散列的空間的大小是幾個(gè)億的數(shù)據(jù)?如果去掉hash&=~x這一句的話會(huì)不會(huì)擴(kuò)大我們hash的范圍,盡可能利用空間,我下星期問(wèn)問(wèn)數(shù)據(jù)結(jié)構(gòu)老師好了!
5.應(yīng)用:
我們?cè)趯?duì)內(nèi)存地址的進(jìn)行的操作的時(shí)候,可以將數(shù)據(jù)的內(nèi)存地址進(jìn)行哈希 因?yàn)槊總€(gè)數(shù)據(jù)的內(nèi)存地址都是唯一的,所以我們只需要一步獲取內(nèi)存地址的十六進(jìn)制的表示就可以了 語(yǔ)句是 [cpp]?view plain?copy ?- sprintf(data,"%0x",&now_data);??
利用這種思路,我們可以很清晰明了的對(duì)鏈表相交的問(wèn)題構(gòu)建一種新的解法,我們采用哈希我們的內(nèi)存空間就可以了,可以再O(n)中完成查找
總結(jié)
以上是生活随笔為你收集整理的ELFhash - 优秀的字符串哈希算法的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 上海欢乐谷残疾人优惠政策
- 下一篇: 字符串匹配shiftand算法