Hash算法的使用
在对语料文本进行2,3元切分时,需要借助hash表来获得切分内容在内存中的位置,以便能够记录语料库中出现的次数。以前知道有很多hash算法,但没认真研究过,今天才知道hash算法差距还是很明显的。
首先我选择的是暴雪在魔兽里的hash算法,这个算法很高级,是time33类型的一个变种(有关time33的介绍,可以参考:http://www.cnblogs.com/napoleon_liu/articles/1911571.html),而且很巧妙的借助3次hash避免字符串比较这种费时的操作,并且不用链表来存储冲突,3次hash冲突值能相等的可能行只有1/10的23次方,应该说不可能冲突了。具体算法请参考:http://blog.csdn.net/eaglewood2005/article/details/4394583。
但很遗憾,这种算法对于一个文本有数百万条记录的情况处理不够理想。首先,预先分配的Table数量是有限的,我分配0x3ffffff大小,再大就很容易分配失败了。其次,正是由于他不用链表处理冲突,当越来越多的加入进来时,处理冲突地方就需要不断往下轮询空余的Table下标,当积累到一定的时候,大概是200万条记录的时候,效率就开始急剧下降,降将到我无法接受,进度条很久都不会动,最后没耐心等下去了。
第二个选择的算法是一个很常见的OpenSSL里的算法
unsigned long lh_strhash(char *str)
{
int i,l;
unsigned long ret=0;
unsigned short *s;
if (str == NULL)
return(0);
l=(strlen(str)+1)/2;
s=(unsigned short *)str;
for (i=0; i<l; i++)
ret^=(s[i]<<(i&0x0f));
return(ret);
}
很简单吧,不过这个算法也仅在一个数量范围内比较好用,对于大记录的hash效果不好,我对328万条记录的结果,bucket利用率仅仅只有 2.14%,最大的冲突长度高达147。 其实这个算法也让程序在20分钟内统计完成。
不过感觉2.14%的利用率实在太低,有必要选择一个更好的算法。于是找到了php的hash算法,这个zend公司的算法也是time33的一种,而且有独到的地方,比如他的hash值不是从0开始,而是从5381开始,5381这个数字有什么意义呢?
Magic Constant 5381:
1. odd number
2. prime number
3. deficient number
4. 001/010/100/000/101 b
算法我就不列了,大家可以很容易找到。这个算法非常有效,程序运行速度翻倍,只要10分钟就可以统计好一个4GB的语料库,而且更加惊人的是在相同的测试条件下,他的bucket利用率高达33.16%,最大冲突长度只有14。
最后借用一个前辈的说法“算法的力量很强”。
阅读全文类别:默认分类 查看评论