函数太多了,如上述文章中所述,BKDRHash算法在实现方便且表现出色,摘录其C的实现
1 unsigned int BKDRHash(char *str)
2 {
3 unsigned int seed = 131; // 31 131 1313 13131 131313 etc..
4 unsigned int hash = 0;
5
6 while (*str)
7 {
8 hash = hash * seed + (*str++);
9 }
10
11 return (hash & 0x7FFFFFFF);
12 }
其中第8行需要注意,这里在计算过程中也许会产生溢出,因为使用了unsigned int类型,相当于做了一次取模操作(hash % (0xFFFFFFFF + 1)=2^32,或者说是hash & 0xFFFFFFFF),在返回时又对hash进行了一次取模,这次采用的是(0x7FFFFFFFF + 1)=2^31。虽然两次取模使用的参数不同,但是因为前置是后者的倍数关系,中间的溢出对最后结果没有影响。
在同一个程序中全部使用这样一个函数是没有问题的,但是当涉及到不用语言之间的交互时情况就有所不同了。下面给出一个python版本的上述代码翻译实现:
def BKDRHash(string):
seed = 131
hash = 0
for ch in string:
hash = hash * seed + ord(ch)
return hash & 0x7FFFFFFF
由于python中整数类型可以很长很长(是不是无限?)所以在中间步骤计算hash值时其值不像C版本中的那样会溢出,而是会原样存储,因此以上python程序和c版本的是等价的(最好还是对每次hash结果进行一次hash & 0x7FFF FFFF)。
之所以要给出python版本的程序是因为今天在实现一小游戏功能:服务器给出一个答案和待选字符,用户在指定时间内输入该答案(从待选字符中)
比如服务器给出ans:hello, word: a b c d h e l o x y z,唯有当用户拼出hello时才算正确,这其实和网页验证码有些类似。我们不想给客户端明文答案,而是一个答案的hash值即hash(ans),通过比较hash(用户输入)和hash(ans)就可以判断对错,同时避免了给出明文答案。于是用python实现了服务端的bkdr hash函数,同时在客户端也实现了javascript版本的brdr hash函数如下:
// bug exists!
var string_hash = function(str, seed) {
var seed = !!seed ? seed : 131;
var hash = 0;
var i = 0;
while (true) {
var ch = str.charCodeAt(i++);
if (!(ch > 0 || ch < 0)) {
break;
}
hash = seed * hash + ch;
}
return hash & 0x7FFFFFFF;
};
结果测试发现同个字符串python和javascript实现给出的hash值不一致,原来javascript中的数字内部都是以64位浮点形式存储,最多只能保留52位左右的整数精度,因此当javascript版本在计算hash值时,hash值会出现精度丢失,这个和C语言中unsigned int类型溢出不同,它仍然是一个浮点表示的数,但不会出现折回,也就没有取模的效果了,导致最后结果错误。既然最后要取模,那么在中间阶段就开始取模,可以避免出现数值过大时的精度丢失。如下:
var string_hash = function(str, seed) {
var seed = !!seed ? seed : 131;
var hash = 0;
var i = 0;
while (true) {
var ch = str.charCodeAt(i++);
if (!(ch > 0 || ch < 0)) {
break;
}
hash = (seed * hash + ch) & 0x7FFFFFFF;
}
return hash;
};