zoukankan      html  css  js  c++  java
  • 字符串哈希函数

    参见: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;
    };
    

     感觉各种语言交叉着用,有时候真是会不知不觉掉进坑里

  • 相关阅读:
    反射实现Model修改前后的内容对比
    [C#] 将NLog输出到RichTextBox,并在运行时动态修改日志级别过滤
    C#远程调用技术WebService葵花宝典
    C# winform实现右下角弹出窗口结果的方法
    C# / VB.NET合并PDF指定页
    C# Word转PDF/HTML/XML/XPS/SVG/EMF/EPUB/TIFF
    C# 将PDF转为SVG的3种情况
    C# 如何将PDF转为多种图像文件格式(Png/Bmp/Emf/Tiff)
    C# 按指定范围拆分Excel工作表
    Powershell如何在Start-Job的Scriptblock里传参?
  • 原文地址:https://www.cnblogs.com/lailailai/p/4030151.html
Copyright © 2011-2022 走看看