• ARTS打卡第一周


    ARTS第一周

    A:本周因为公司代码需要,主要参考研究了暴雪的hash算法

    你有一个非常大的字符串数组A,现在又有一个字符串B,需要你去检测B是否存在于A中。最简单粗暴的方法是遍历整个A,但是这个方法投入到实际应用时的运行速度是难以接受的。在没有与其他所有字符串比较前怎么知道该字符串是否存在呢?

    解决方法是使用哈希表,即用较小的数据类型来代表较大的数据类型,例如:用数字来代表字符串。你可以存储哈希值与字符串一一对应,当需要检测一个字符串时,就用哈希算法计算其哈希值,然后与存储的哈希值比较级可以得出结果,使用这一方法根据数组的大小和字符串长度提升速度大约100倍。

     1 unsigned long HashString(char *lpszString)
     2 {   
     3     unsigned long ulHash = 0xf1e2d3c4;        
     4     while (*lpszString != 0)    
     5     {        
     6         ulHash <<= 1;       
     7         ulHash += *lpszString++;      
     8     }   
     9     return ulHash;
    10 }
    View Code

    上述代码演示了一个简单的哈希算法。该函数在遍历整个字符串时,将ulHash左移一位再叫上字符值。使用这个算法 ,"arrunits.dat" 的哈希值是0x5A858026,字符串"unit eutralacritter.grp" 的哈希值是0x694CD020。但是这个算法没有什么使用价值,因为它产生的哈希值是可以预测的,可能使不同的字符产生相同的哈希值,从而产生碰撞。

    要解决这一问题方法,网上流传最神的是MPQ,源自于暴雪公司的文件打包管理,用于Blizzard游戏的数据文件,包括图形,声音,等级等数据,该算法能够压缩,解密,文件分割等功能。详情见维基:http://en.wikipedia.org/wiki/MPQ

    该算法产生的哈希值完全无法预测,非常高效,被称为"One-Way Hash"( A one-way hash is a an algorithm that is constructed in such a way that deriving the original string (set of strings, actually) is virtually impossible)。

    unsigned long HashString(char *lpszFileName, unsigned long dwHashType)
    {   
        unsigned char *key = (unsigned char *)lpszFileName;   
        unsigned long seed1 = 0x7FED7FED, seed2 = 0xEEEEEEEE;   
        int ch;
    
        while(*key != 0)       
        {      
            ch = toupper(*key++);   
            seed1 = cryptTable[(dwHashType << 8) + ch] ^ (seed1 + seed2);       
            seed2 = ch + seed1 + seed2 + (seed2 << 5) + 3;       
        }   
        return seed1;  
    }
    View Code

    尝试在前面的示例中使用相同索引,您的程序一定会有中断现象发生,而且不够快。如果想让它更快,您能做的只有让程序不去查询数组中的所有散列值。或者您可以只做一次对比就可以得出在列表中是否存在字符串。听起来不错,真的么?不可能的啦

        一个哈希表就是以字符串的哈希值作为下标的一类数组。我的意思是,哈希表使用一个固定长度的字符串数组(比如10242的偶次幂)进行存储;当你要看看这个字符串是否存在于哈希表中,为了获取这个字符串在哈希表中的位置,你首先计算字符串的哈希值,然后哈希表的长度取模。这样如果你像上一节那样使用简单的哈希算法,字符串"arrunits.dat" 的哈希值是0x5A858026,偏移量0x260x5A858026 除于0x400等于0x16A160,模0x400等于0x26)。因此,这个位置的字符串将与新加入的字符串进行比较。如果0X26处的字符串不匹配或不存在,那么表示新增的字符串在数组中不存在。下面是示意的代码:

    1 int GetHashTablePos(char *lpszString, SOMESTRUCTURE *lpTable, int nTableSize)
    2 {   
    3     int nHash = HashString(lpszString), nHashPos = nHash % nTableSize;       
    4     if (lpTable[nHashPos].bExists && !strcmp(lpTable[nHashPos].pString, lpszString))       
    5         return nHashPos;   
    6     else        
    7         return -1; //Error value   
    8 }
    View Code

    上面的说明中存在一个缺陷。当有冲突(两个不同的字符串有相同的哈希值)发生的时候怎么办?显而易见的,它们不能占据哈希表中的同一个位置。通常的解决办法是为每一个哈希值指向一个链表,用于存放所有哈希冲突的值;

    MPQs使用一个存放文件名的哈希表来跟踪文件内部,但是表的格式与通常方法有点不同,首先不像通常的做法使用哈希值作为偏移量,存储实际的文件名。MPQs 根本不存储文件名,而是使用了三个不同的哈希值:一个用做哈希表偏移量,两个用作核对。这两个核对的哈希值用于替代文件名。当然从理论上说存在两个不同的文件名得到相同的三个哈希值,但是这种情况发送的几率是:1:18889465931478580854784,这应该足够安全了。

    MPQ's的哈希表的实现与传统实现的另一个不同的地方是,相对与传统做法(为每个节点使用一个链表,当冲突发生的时候,遍历链表进行比较),看一下下面的示范代码,在MPQ中定位一个文件进行读操作:

     1 int GetHashTablePos(char *lpszString, MPQHASHTABLE *lpTable, int nTableSize)
     2 {   
     3     const int HASH_OFFSET = 0, HASH_A = 1, HASH_B = 2;    
     4     int nHash = HashString(lpszString, HASH_OFFSET),nHashA = HashString(lpszString, HASH_A),nHashB = HashString(lpszString, HASH_B), nHashStart = nHash % nTableSize,nHashPos = nHashStart;
     5     while (lpTable[nHashPos].bExists)
     6     {
     7         if (lpTable[nHashPos].nHashA == nHashA && lpTable[nHashPos].nHashB == nHashB)
     8             return nHashPos;
     9 
    10         else
    11             nHashPos = (nHashPos + 1) % nTableSize;
    12             if (nHashPos == nHashStart)
    13                 break;
    14     }
    15     return -1; //Error value
    16 
    17 }
    View Code

    无论代码看上去有多么复杂,其背后的理论并不难。读一个文件的时候基本遵循下面这样一个过程:

    1、计算三个哈希值(一个哈希偏移量和两个验证值)并保存到变量中;

    2、移动到哈希偏移量对应的值;

    3、对应的位置是否尚未使用?如果是,则停止搜寻,并返回"文件不存在";

    4、这两个验证值是否与我们要找的字符串验证值匹配,如果是,停止搜寻,并返回当前的节点;

    5、移动到下一个节点,如果到了最后一个节点则返回开始;

    6、如果移动到了相同的偏移值(遍历了整个哈希表),则停止搜寻,并返回"文件不存在";

    7、回到第3步;

    如果你注意的话,可能已经从我们的解释和示例代码注意到,MPQ的哈希表已经将所有的文件入口放入MPQ中;那么当哈希表的每个项都被填充的时候,会发生什么呢?答案可能会让你惊讶:你不能添加任何文件。有些人可能会问我为什么文件数量上有这样的限制(文件限制),是否有办法绕过这个限制?就此而言,如果不重新创建MPQ 的项,甚至无法调整哈希表的大小。这是因为每个项在哈希表中的位置会因为跳闸尺寸而改变,而我们无法得到新的位置,因为这些位置值是文件名的哈希值,而我们根本不知道文件名是什么。

    如果想要深入了解MPQ入此坑: http://sfsrealm.hopto.org/inside_mopaq/index.htm

    R:  阅读部分kafaka的英文文档,另外由于公司是外企,公司的相关技术文档也是英文的

    T:  本周在学习公司技术文档的时候,发现流程图很多细节自己不清楚,然后就去学习了设计模式里的UML设计图,

       UML(Unified Modeling Language)是一种统一建模语言,为面向对象开发系统的产品进行说明、可视化、和编制文档的一种标准语言。

       由于自己目前还未整理好相关内容,这里贴一下原文地址:https://www.cnblogs.com/jiangds/p/6596595.html

     

    S:  基于现在很多公司都是敏捷项目管理,学习了敏捷的相关流程。

      1.相关介绍
      Scrum (英式橄榄球争球队), 软件开发模型是敏捷开发的一种,在最近的一两年内逐渐流行起来。
      Scrum的基本假设是:
      开发软件就像开发新产品,无法一开始就能定义软件产品最终的规程,过程中需要研发、创意、尝试错误,所以没有一种固定的流程可以保证专案成功。Scrum 将软件开发团队比拟成橄榄球队,有明确的最高目标,熟悉开发流程  中所需具备的最佳典范与技术,具有高度自主权,紧密地沟通合作,以高度弹性解决各种挑战,确保每天、每个阶段都朝向目标有明确的推进。

      Scrum 开发流程通常以 30 天(或者更短的一段时间)为一个阶段,由客户提供新产品的需求规格开始,开发团队与客户于每一个阶段开始时挑选该完成的规格部分,开发团队必须尽力于 30 天后交付成  果,团队每天用 15 分钟开会检查每个成员的进度与计划,了解所遭遇的困难并设法排除。

      2.Scrum较传统开发模型的优点

      Scrum模型的一个显著特点就是响应变化,它能够尽快地响应变化。下面的图片使用传统的软件开发模型(瀑布模型、螺旋模型或迭代模型)。随着系统因素(内部和外部因素)的复杂度增加,项目成功  的可能性就迅速降低。

      

      3.Scrum的过程简单介绍
      (1) 将整个产品的backlog分解成Sprint Backlog,这个Sprint Backlog是按照目前的人力物力条件可以完成的。
      (2)召开sprint planning meeting,划分,确定这个Sprint内需要完成的任务,标注任务的优先级并分配给每个成员。注意这里的任务是以小时计算的,并不是按人天计算。
      (3) 进入sprint开发周期,在这个周期内,每天需要召开Daily Scrum meeting。
      (4)整个sprint周期结束,召开Sprint review meeting,将成果演示给Product Owner.
      (5)团队成员最后召开Sprint retrospective meeting,总结问题和经验。
      (6)这样周而复始,按照同样的步骤进行下一次Sprint.

      4.敏捷强调的重点

      (1)敏捷强调,客户们需要在开发过程中自始至终都和项目紧密配合。拥抱变更,且非常欢迎客户的反馈。在项目所有的“检核和适应”这一环节上,都期望客户能够参与并提供宝贵意见,降低风险,为客户和利益相关者提供选择  空间。它强调当项目的需求发生了变化,团队能够迅速适应。主要靠频繁地小规模发布软件,也就是短的迭代完成的,敏捷方法在几周或者几个月的时间内就完成相对较小的功能,尽早将尽量小的可用的功能交付使用,并在整个  项目周期中持续改善和增强。

      (2)敏捷强调,面对面的交流,而不是用多而复杂的文档。“客户需要的是支持,而不是文档。很多时候我们有一个错误的理解,文档等于支持,但事实上,文档并不等于支持。”

      (3)敏捷强调,计划会议上,客户应该和开发负责人一起定义User Story,并在计划会议上给出详细说明。

      (4)敏捷强调,客户应该和开发负责人一起为Backlog定出优先级。

      (5)  敏捷强调,客户和利益相关者要参与Sprint尾声的产品演示。根据客户和产品负责人的关系,客户甚至可以参加Sprint回顾会议。  

  • 相关阅读:
    Java三大框架
    单例模式和工厂模式(百度文库)
    使用java代码编辑oracle数据库
    extends 与implements的区别和用法
    介绍MVC编程架构模式
    接口具体是什么东西
    Servlet和JSP的本质和区别
    用户注册,登录,留言系统
    页面跳转的五种方法
    cookie的长度和限制数量
  • 原文地址:https://www.cnblogs.com/tsinghuama/p/10591341.html
Copyright © 2020-2023  润新知