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左移一位再叫上字符值。使用这个算法 ,"arr\units.dat" 的哈希值是0x5A858026,字符串"unit\neutral\acritter.grp" 的哈希值是0x694CD020。但是这个算法没有什么使用价值,因为它产生的哈希值是可以预测的,可能使不同的字符产生相同的哈希值,从而产生碰撞。

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

扫描二维码关注公众号,回复: 5645276 查看本文章

该算法产生的哈希值完全无法预测,非常高效,被称为"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的偶次幂)进行存储;当你要看看这个字符串是否存在于哈希表中,为了获取这个字符串在哈希表中的位置,你首先计算字符串的哈希值,然后哈希表的长度取模。这样如果你像上一节那样使用简单的哈希算法,字符串"arr\units.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回顾会议。  

猜你喜欢

转载自www.cnblogs.com/tsinghuama/p/10591341.html