最近在对Heritrix 进行线程策略优化的时候(原来是根据Hostname来开线程的,现改为根据hash函数算出来的key值开线程),需要用到ELFHash算法,上网找了找资料,自己总结下。
它对于长字符串和短字符串都很有效,字符串中每个字符都有同样的作用,它巧妙地对字符的ASCII编码值进行计算,ELFhash函数对于能够比较均匀地把字符串分布在散列表中。这些函数使用位运算使得每一个字符都对最后的函数值产生影响。
java 版ELFHash算法:
private long ELFHash(String str){
long hash=0;
long x=0;
for(int i=0;i<str.length();i++){
hash = (hash << 4 )+str.charAt(i);<span style="color: rgb(0, 130, 0); font-family: Consolas, 'Courier New', Courier, mono, serif; line-height: 18px;">//hash左移4位,把当前字符ASCII存入hash低四位。 </span>
if((x= hash & 0xF000000L) != 0){
<ol start="1" class="dp-cpp" style="padding: 0px; border: none; list-style-position: initial; list-style-image: initial; color: rgb(92, 92, 92); font-family: Consolas, 'Courier New', Courier, mono, serif; line-height: 26px; margin: 0px 0px 1px 45px !important;"><li style="border-style: none none none solid; border-left-width: 3px; border-left-color: rgb(108, 226, 108); list-style: decimal-leading-zero outside; background-color: rgb(248, 248, 248); line-height: 18px; margin: 0px !important; padding: 0px 3px 0px 10px !important;"><span style="margin: 0px; padding: 0px; border: none; color: black; background-color: inherit;"> <span class="comment" style="margin: 0px; padding: 0px; border: none; color: rgb(0, 130, 0); background-color: inherit;">//如果最高的四位不为0,则说明字符多余7个,现在正在存第7个字符,如果不处理,再加下一个字符时,第一个字符会被移出,因此要有如下处理。</span><span style="margin: 0px; padding: 0px; border: none; background-color: inherit;"> </span></span></li><li class="alt" style="border-style: none none none solid; border-left-width: 3px; border-left-color: rgb(108, 226, 108); list-style: decimal-leading-zero outside; color: inherit; line-height: 18px; margin: 0px !important; padding: 0px 3px 0px 10px !important;"><span style="margin: 0px; padding: 0px; border: none; color: black; background-color: inherit;"> <span class="comment" style="margin: 0px; padding: 0px; border: none; color: rgb(0, 130, 0); background-color: inherit;">//该处理,如果最高位为0,就会仅仅影响5-8位,否则会影响5-31位,因为C语言使用的算数移位</span><span style="margin: 0px; padding: 0px; border: none; background-color: inherit;"> </span></span></li><li style="border-style: none none none solid; border-left-width: 3px; border-left-color: rgb(108, 226, 108); list-style: decimal-leading-zero outside; background-color: rgb(248, 248, 248); line-height: 18px; margin: 0px !important; padding: 0px 3px 0px 10px !important;"><span style="margin: 0px; padding: 0px; border: none; color: black; background-color: inherit;"> <span class="comment" style="margin: 0px; padding: 0px; border: none; color: rgb(0, 130, 0); background-color: inherit;">//因为1-4位刚刚存储了新加入到字符,所以不能>>28</span><span style="margin: 0px; padding: 0px; border: none; background-color: inherit;"> </span></span></li></ol>
hash ^= x>>24;
<span style="font-family: Consolas, 'Courier New', Courier, mono, serif; line-height: 18px; background-color: rgb(248, 248, 248);"> </span><span class="comment" style="margin: 0px; padding: 0px; border: none; color: rgb(0, 130, 0); background-color: rgb(248, 248, 248); font-family: Consolas, 'Courier New', Courier, mono, serif; line-height: 18px;">//上面这行代码并不会对X有影响,本身X和hash的高4位相同,下面这行代码&~即对28-31(高4位)位清零。</span>
hash &= ~x;
}
}
<span style="color: rgb(0, 130, 0); font-family: Consolas, 'Courier New', Courier, mono, serif; line-height: 18px; background-color: rgb(248, 248, 248);">//返回一个符号位为0的数,即丢弃最高位,以免函数外产生影响。(我们可以考虑,如果只有字符,符号位不可能为负)</span>
return hash & 0x7FFFFFFFL;
}
应用场景:通常用来按规律地存放字符串并能快速查找。例如,我存放一堆字符串,但是要求能快速对查找出来。那么首先定义一个大数组存放,存放的index值根据ELF值算出来,如果算出的结果相同,那么用链表链接起来:
c版:
#include <iostream>
#include <fstream>
#include <string.h>
#define N 100001
#define strSize 15
using namespace std;
struct hash{
bool used;
char fn[strSize],en[strSize];
hash* next; //用于冲突时构造链表
hash(){used=false; next=NULL;}
hash(char *f,char *e)
{
strcpy(fn,f);
strcpy(en,e);
used=false;
next=NULL;
}
}h[N];
int ELFhash(char *key){
unsigned long h=0;
unsigned long x=0;
while(*key)
{
h=(h<<4)+(*key++); //h左移4位,当前字符ASCII存入h的低四位
if( (x=h & 0xF0000000L)!=0)
{ //如果最高位不为0,则说明字符多余7个,如果不处理,再加第九个字符时,第一个字符会被移出
//因此要有如下处理
h^=(x>>24);
//清空28~31位
h&=~x;
}
}
return h % N;
}
int main()
{
freopen("acm.txt","r",stdin);
char str[30],en[strSize],fn[strSize];
hash* p;
int sign=1,key;
while(gets(str))
{
if(str[0]=='\0')
{
sign=0;
continue;
}
if(sign) //输入字典
{
sscanf(str,"%s %s",&en,&fn);
key=ELFhash(fn); //获取hash值
if(!h[key].used) //对应到hash表中
{
h[key].used=true;
strcpy(h[key].en,en);
strcpy(h[key].fn,fn);
}
else //处理冲突
{
p=&h[key];
while(p->next != NULL) p=p->next;
p->next=new hash(fn,en);
}
}
else //输入外文
{
key=ELFhash(str);
if(!h[key].used) printf("eh\n");
else
{
p=&h[key];
while(p!=NULL)
{
if(!strcmp(str,p->fn))
{
printf("%s\n",p->en);
break;
}
else
{
p=p->next;
}
}
if(p==NULL) printf("eh\n"); //不匹配的情况,不能少
}
}
}
return 0;
}