参见:http://www.cnblogs.com/uvsjoh/archive/2012/03/27/2420120.html
函数太多了,如上述文章中所述,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; };
感觉各种语言交叉着用,有时候真是会不知不觉掉进坑里