从头到尾彻底解析Hash 表算法

高速设计注意事项总结——MDI篇 在以太网高速通信系统中,MDI(Medium Dependent Interface,介质相关接口)是连接物理层芯片(PHY)与传输介质(如网线)的关键接口,其设计直接影响信号完整性、传输速率和抗干扰能力。本文围绕 MDI 及 RJ45 接口电路、关键参数、设计与布线要点展开,为高速以太网设计提供参考。本文包含MDI 及 RJ45 接口电路组成、电路设计注意事项、布局布线要求等内容 阅读详情

作者:July、wuliming、pkuoliver  
出处:
http://blog.youkuaiyun.com/v_JULY_v。  
说明:本文分为三部分内容,
    第一部分为一道百度面试题Top K算法的详解;第二部分为关于Hash表算法的详细阐述;第三部分为打造一个最快的Hash表算法。
------------------------------------

第一部分:Top K 算法详解
问题描述
百度面试题:
    搜索引擎会通过日志文件把用户每次检索使用的所有检索串都记录下来,每个查询串的长度为1-255字节。
    假设目前有一千万个记录(这些查询串的重复度比较高,虽然总数是1千万,但如果除去重复后,不超过3百万个。一个查询串的重复度越高,说明查询它的用户越多,也就是越热门。),请你统计最热门的10个查询串,要求使用的内存不能超过1G。


必备知识:
    什么是哈希表?
    哈希表(Hash table,也叫散列表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。

    哈希表的做法其实很简单,就是把Key通过一个固定的算法函数既所谓的哈希函数转换成一个整型数字,然后就将该数字对数组长度进行取余,取余结果就当作数组的下标,将value存储在以该数字为下标的数组空间里。
    而当使用哈希表进行查询的时候,就是再次使用哈希函数将key转换为对应的数组下标,并定位到该空间获取value,如此一来,就可以充分利用到数组的定位性能进行数据定位(文章第二、三部分,会针对Hash表详细阐述)。


问题解析:
    要统计最热门查询,首先就是要统计每个Query出现的次数,然后根据统计结果,找出Top 10。所以我们可以基于这个思路分两步来设计该算法。
    即,此问题的解决分为以下俩个步骤:

第一步:Query统计
    Query统计有以下俩个方法,可供选择:
    1、直接排序法
    首先我们最先想到的的算法就是排序了,首先对这个日志里面的所有Query都进行排序,然后再遍历排好序的Query,统计每个Query出现的次数了。

    但是题目中有明确要求,那就是内存不能超过1G,一千万条记录,每条记录是255Byte,很显然要占据2.375G内存,这个条件就不满足要求了。

    让我们回忆一下数据结构课程上的内容,当数据量比较大而且内存无法装下的时候,我们可以采用外排序的方法来进行排序,这里我们可以采用归并排序,因为归并排序有一个比较好的时间复杂度O(NlgN)。

    排完序之后我们再对已经有序的Query文件进行遍历,统计每个Query出现的次数,再次写入文件中。

    综合分析一下,排序的时间复杂度是O(NlgN),而遍历的时间复杂度是O(N),因此该算法的总体时间复杂度就是O(N+NlgN)=O(NlgN)。

    2、Hash Table法
    在第1个方法中,我们采用了排序的办法来统计每个Query出现的次数,时间复杂度是NlgN,那么能不能有更好的方法来存储,而时间复杂度更低呢?

    题目中说明了,虽然有一千万个Query,但是由于重复度比较高,因此事实上只有300万的Query,每个Query255Byte,因此我们可以考虑把他们都放进内存中去,而现在只是需要一个合适的数据结构,在这里,Hash Table绝对是我们优先的选择,因为Hash Table的查询速度非常的快,几乎是O(1)的时间复杂度。

    那么,我们的算法就有了:维护一个Key为Query字串,Value为该Query出现次数的HashTable,每次读取一个Query,如果该字串不在Table中,那么加入该字串,并且将Value值设为1;如果该字串在Table中,那么将该字串的计数加一即可。最终我们在O(N)的时间复杂度内完成了对该海量数据的处理。

    本方法相比算法1:在时间复杂度上提高了一个数量级,为O(N),但不仅仅是时间复杂度上的优化,该方法只需要IO数据文件一次,而算法1的IO次数较多的,因此该算法2比算法1在工程上有更好的可操作性。


第二步:找出Top 10
    
算法一:普通排序
    我想对于排序算法大家都已经不陌生了,这里不在赘述,我们要注意的是排序算法的时间复杂度是NlgN,在本题目中,三百万条记录,用1G内存是可以存下的。

    算法二:部分排序
    题目要求是求出Top 10,因此我们没有必要对所有的Query都进行排序,我们只需要维护一个10个大小的数组,初始化放入10个Query,按照每个Query的统计次数由大到小排序,然后遍历这300万条记录,每读一条记录就和数组最后一个Query对比,如果小于这个Query,那么继续遍历,否则,将数组中最后一条数据淘汰,加入当前的Query。最后当所有的数据都遍历完毕之后,那么这个数组中的10个Query便是我们要找的Top10了。

    不难分析出,这样,算法的最坏时间复杂度是N*K, 其中K是指top多少。

    算法三:堆
    在算法二中,我们已经将时间复杂度由NlogN优化到NK,不得不说这是一个比较大的改进了,可是有没有更好的办法呢?

    分析一下,在算法二中,每次比较完成之后,需要的操作复杂度都是K,因为要把元素插入到一个线性表之中,而且采用的是顺序比较。这里我们注意一下,该数组是有序的,一次我们每次查找的时候可以采用二分的方法查找,这样操作的复杂度就降到了logK,可是,随之而来的问题就是数据移动,因为移动数据次数增多了。不过,这个算法还是比算法二有了改进。

    基于以上的分析,我们想想,有没有一种既能快速查找,又能快速移动元素的数据结构呢?回答是肯定的,那就是堆。
    借助堆结构,我们可以在log量级的时间内查找和调整/移动。因此到这里,我们的算法可以改进为这样,维护一个K(该题目中是10)大小的小根堆,然后遍历300万的Query,分别和根元素进行对比。

    思想与上述算法二一致,只是算法在算法三,我们采用了最小堆这种数据结构代替数组,把查找目标元素的时间复杂度有O(K)降到了O(logK)。
    那么这样,采用堆数据结构,算法三,最终的时间复杂度就降到了N‘logK,和算法二相比,又有了比较大的改进。

 

总结:
    至此,算法就完全结束了,经过上述第一步、先用Hash表统计每个Query出现的次数,O(N);然后第二步、采用堆数据结构找出Top 10,N*O(logK)。所以,我们最终的时间复杂度是:O(N) + N'*O(logK)。(N为1000万,N’为300万)。如果各位有什么更好的算法,欢迎留言评论。第一部分,完。

 

第二部分、Hash表 算法的详细解析

什么是Hash
     Hash,一般翻译做“散列”,也有直接音译为“哈希”的,就是把任意长度的输入(又叫做预映射, pre-image),通过散列算法,变换成固定长度的输出,该输出就是散列值。这种转换是一种压缩映射,也就是,散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,而不可能从散列值来唯一的确定输入值。简单的说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数。

    HASH主要用于信息安全领域中加密算法,它把一些不同长度的信息转化成杂乱的128位的编码,这些编码值叫做HASH值. 也可以说,hash就是找到一种数据内容和数据存放地址之间的映射关系。

    数组的特点是:寻址容易,插入和删除困难;而链表的特点是:寻址困难,插入和删除容易。那么我们能不能综合两者的特性,做出一种寻址容易,插入删除也容易的数据结构?答案是肯定的,这就是我们要提起的哈希表,哈希表有多种不同的实现方法,我接下来解释的是最常用的一种方法——拉链法,我们可以理解为“链表的数组”,如图:


    左边很明显是个数组,数组的每个成员包括一个指针,指向一个链表的头,当然这个链表可能为空,也可能元素很多。我们根据元素的一些特征把元素分配到不同的链表中去,也是根据这些特征,找到正确的链表,再从链表中找出这个元素。

    元素特征转变为数组下标的方法就是散列法。散列法当然不止一种,下面列出三种比较常用的:

1,除法散列法 
最直观的一种,上图使用的就是这种散列法,公式: 
      index = value % 16 
学过汇编的都知道,求模数其实是通过一个除法运算得到的,所以叫“除法散列法”。

2,平方散列法 
求index是非常频繁的操作,而乘法的运算要比除法来得省时(对现在的CPU来说,估计我们感觉不出来),所以我们考虑把除法换成乘法和一个位移操作。公式: 
      index = (value * value) >> 28   (右移,除以2^28。记法:左移变大,是乘。右移变小,是除。
)
如果数值分配比较均匀的话这种方法能得到不错的结果,但我上面画的那个图的各个元素的值算出来的index都是0——非常失败。也许你还有个问题,value如果很大,value * value不会溢出吗?答案是会的,但我们这个乘法不关心溢出,因为我们根本不是为了获取相乘结果,而是为了获取index。

3,斐波那契(Fibonacci)散列法

平方散列法的缺点是显而易见的,所以我们能不能找出一个理想的乘数,而不是拿value本身当作乘数呢?答案是肯定的。

1,对于16位整数而言,这个乘数是40503 
2,对于32位整数而言,这个乘数是2654435769 
3,对于64位整数而言,这个乘数是11400714819323198485

    这几个“理想乘数”是如何得出来的呢?这跟一个法则有关,叫黄金分割法则,而描述黄金分割法则的最经典表达式无疑就是著名的斐波那契数列,即如此形式的序列:0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946,…。另外,斐波那契数列的值和太阳系八大行星的轨道半径的比例出奇吻合。

    对我们常见的32位整数而言,公式: 
            index = (value * 2654435769) >> 28

    如果用这种斐波那契散列法的话,那上面的图就变成这样了:
很明显,用斐波那契散列法调整之后要比原来的取摸散列法好很多。 

适用范围
    快速查找,删除的基本数据结构,通常需要总数据量可以放入内存。

基本原理及要点
    hash函数选择,针对字符串,整数,排列,具体相应的hash方法。 
碰撞处理,一种是open hashing,也称为拉链法;另一种就是closed hashing,也称开地址法,opened addressing。

扩展 
    d-left hashing中的d是多个的意思,我们先简化这个问题,看一看2-left hashing。2-left hashing指的是将一个哈希表分成长度相等的两半,分别叫做T1和T2,给T1和T2分别配备一个哈希函数,h1和h2。在存储一个新的key时,同 时用两个哈希函数进行计算,得出两个地址h1[key]和h2[key]。这时需要检查T1中的h1[key]位置和T2中的h2[key]位置,哪一个 位置已经存储的(有碰撞的)key比较多,然后将新key存储在负载少的位置。如果两边一样多,比如两个位置都为空或者都存储了一个key,就把新key 存储在左边的T1子表中,2-left也由此而来。在查找一个key时,必须进行两次hash,同时查找两个位置。

问题实例(海量数据处理) 
    我们知道hash 表在海量数据处理中有着广泛的应用,下面,请看另一道百度面试题:
题目:海量日志数据,提取出某日访问百度次数最多的那个IP。
方案:IP的数目还是有限的,最多2^32个,所以可以考虑使用hash将ip直接存入内存,然后进行统计。

 

第三部分、最快的Hash表算法

    接下来,咱们来具体分析一下一个最快的Hasb表算法。
    我们由一个简单的问题逐步入手:有一个庞大的字符串数组,然后给你一个单独的字符串,让你从这个数组中查找是否有这个字符串并找到它,你会怎么做?有一个方法最简单,老老实实从头查到尾,一个一个比较,直到找到为止,我想只要学过程序设计的人都能把这样一个程序作出来,但要是有程序员把这样的程序交给用户,我只能用无语来评价,或许它真的能工作,但...也只能如此了。

    最合适的算法自然是使用HashTable(哈希表),先介绍介绍其中的基本知识,所谓Hash,一般是一个整数,通过某种算法,可以把一个字符串"压缩" 成一个整数。当然,无论如何,一个32位整数是无法对应回一个字符串的,但在程序中,两个字符串计算出的Hash值相等的可能非常小,下面看看在MPQ中的Hash算法:

函数一、以下的函数生成一个长度为0x500(合10进制数:1280)的cryptTable[0x500]

void prepareCryptTable()
{ 
    unsigned long seed = 0x00100001, index1 = 0, index2 = 0, i;
 
    for( index1 = 0; index1 < 0x100; index1++ )
    { 
        for( index2 = index1, i = 0; i < 5; i++, index2 += 0x100 )
        { 
            unsigned long temp1, temp2;
 
            seed = (seed * 125 + 3) % 0x2AAAAB;
            temp1 = (seed & 0xFFFF) << 0x10;
 
            seed = (seed * 125 + 3) % 0x2AAAAB;
            temp2 = (seed & 0xFFFF);
 
            cryptTable[index2] = ( temp1 | temp2 ); 
       } 
   } 
} 

函数二、以下函数计算lpszFileName 字符串的hash值,其中dwHashType 为hash的类型,在下面的函数三、GetHashTablePos函数中调用此函数二,其可以取的值为0、1、2;该函数返回lpszFileName 字符串的hash值: 

unsigned long HashString( char *lpszFileName, unsigned long dwHashType )
{ 
    unsigned char *key  = (unsigned char *)lpszFileName;
unsigned long seed1 = 0x7FED7FED;
unsigned long 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; 
}


    Blizzard的这个算法是非常高效的,被称为"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)。举个例子,字符串"unitneutralacritter.grp"通过这个算法得到的结果是0xA26067F3。

  是不是把第一个算法改进一下,改成逐个比较字符串的Hash值就可以了呢,答案是,远远不够,要想得到最快的算法,就不能进行逐个的比较,通常是构造一个哈希表(Hash Table)来解决问题,哈希表是一个大数组,这个数组的容量根据程序的要求来定义,例如1024,每一个Hash值通过取模运算 (mod) 对应到数组中的一个位置,这样,只要比较这个字符串的哈希值对应的位置有没有被占用,就可以得到最后的结果了,想想这是什么速度?是的,是最快的O(1),现在仔细看看这个算法吧:

typedef struct
{
    int nHashA;
    int nHashB;
    char bExists;
   ......
} SOMESTRUCTRUE;
一种可能的结构体定义?

函数三、下述函数为在Hash表中查找是否存在目标字符串,有则返回要查找字符串的Hash值,无则,return -1.

int GetHashTablePos( har *lpszString, SOMESTRUCTURE *lpTable ) 
//lpszString要在Hash表中查找的字符串,lpTable为存储字符串Hash值的Hash表。
{ 
    int nHash = HashString(lpszString);  //调用上述函数二,返回要查找字符串lpszString的Hash值。
    int nHashPos = nHash % nTableSize;
 
    if ( lpTable[nHashPos].bExists  &&  !strcmp( lpTable[nHashPos].pString, lpszString ) ) 
    {  //如果找到的Hash值在表中存在,且要查找的字符串与表中对应位置的字符串相同,
        return nHashPos;    //则返回上述调用函数二后,找到的Hash值
    } 
    else
    {
        return -1;  
    } 
}

 
    看到此,我想大家都在想一个很严重的问题:“如果两个字符串在哈希表中对应的位置相同怎么办?”,毕竟一个数组容量是有限的,这种可能性很大。解决该问题的方法很多,我首先想到的就是用“链表”,感谢大学里学的数据结构教会了这个百试百灵的法宝,我遇到的很多算法都可以转化成链表来解决,只要在哈希表的每个入口挂一个链表,保存所有对应的字符串就OK了。事情到此似乎有了完美的结局,如果是把问题独自交给我解决,此时我可能就要开始定义数据结构然后写代码了。

    然而Blizzard的程序员使用的方法则是更精妙的方法。基本原理就是:他们在哈希表中不是用一个哈希值而是用三个哈希值来校验字符串。

 

    MPQ使用文件名哈希表来跟踪内部的所有文件。但是这个表的格式与正常的哈希表有一些不同。首先,它没有使用哈希作为下标,把实际的文件名存储在表中用于验证,实际上它根本就没有存储文件名。而是使用了3种不同的哈希:一个用于哈希表的下标,两个用于验证。这两个验证哈希替代了实际文件名。
    当然了,这样仍然会出现2个不同的文件名哈希到3个同样的哈希。但是这种情况发生的概率平均是:1:18889465931478580854784,这个概率对于任何人来说应该都是足够小的。现在再回到数据结构上,Blizzard使用的哈希表没有使用链表,而采用"顺延"的方式来解决问题,看看这个算法:

函数四、lpszString 为要在hash表中查找的字符串;lpTable 为存储字符串hash值的hash表;nTableSize 为hash表的长度: 

int GetHashTablePos( char *lpszString, MPQHASHTABLE *lpTable, int nTableSize )
{
    const int  HASH_OFFSET = 0, HASH_A = 1, HASH_B = 2;
 
    int  nHash = HashString( lpszString, HASH_OFFSET );
    int  nHashA = HashString( lpszString, HASH_A );
    int  nHashB = HashString( lpszString, HASH_B );
    int  nHashStart = nHash % nTableSize;
    int  nHashPos = nHashStart;
 
    while ( lpTable[nHashPos].bExists )
   {
    
        if (   lpTable[nHashPos].nHashA == nHashA
        &&  lpTable[nHashPos].nHashB == nHashB )
       {
            return nHashPos;
       }
       else
       {
            nHashPos = (nHashPos + 1) % nTableSize;
       }
 
        if (nHashPos == nHashStart)
              break;
    }
     return -1;
}

上述程序解释:

1.计算出字符串的三个哈希值(一个用来确定位置,另外两个用来校验)
2. 察看哈希表中的这个位置
3. 哈希表中这个位置为空吗?如果为空,则肯定该字符串不存在,返回-1。
4. 如果存在,则检查其他两个哈希值是否也匹配,如果匹配,则表示找到了该字符串,返回其Hash值。
5. 移到下一个位置,如果已经移到了表的末尾,则反绕到表的开始位置起继续查询 
6. 看看是不是又回到了原来的位置,如果是,则返回没找到
7. 回到3

ok,这就是本文中所说的最快的Hash表算法。什么?不够快?:D。欢迎,各位批评指正。

--------------------------------------------
补充1、一个简单的hash函数:


unsigned long getHashIndex( const char *key, int nTableLength )
{
    unsigned long nHash = 0;
   
    while (*key)
    {
        nHash = (nHash<<5) + nHash + *key++;
    }
        
    return ( nHash % nTableLength );
}

 

补充2、一个完整测试程序:  
    哈希表的数组是定长的,如果太大,则浪费,如果太小,体现不出效率。合适的数组大小是哈希表的性能的关键。哈希表的尺寸最好是一个质数。当然,根据不同的数据量,会有不同的哈希表的大小。对于数据量时多时少的应用,最好的设计是使用动态可变尺寸的哈希表,那么如果你发现哈希表尺寸太小了,比如其中的元素是哈希表尺寸的2倍时,我们就需要扩大哈希表尺寸,一般是扩大一倍。


    下面是哈希表尺寸大小的可能取值:

     17,            37,          79,        163,          331,  
    673,           1361,        2729,       5471,         10949,        
   21911,          43853,      87719,      175447,      350899,
  701819,         1403641,    2807303,     5614657,     11229331,   
 22458671,       44917381,    89834777,    179669557,   359339171,  
718678369,      1437356741,  2147483647

 

以下为该程序的完整源码,已在linux下测试通过:

  1. #include <stdio.h>  
  2. #include <ctype.h>     //多谢citylove指正。  
  3. //crytTable[]里面保存的是HashString函数里面将会用到的一些数据,在prepareCryptTable  
  4. //函数里面初始化  
  5. unsigned long cryptTable[0x500];  
  6.   
  7. //以下的函数生成一个长度为0x500(合10进制数:1280)的cryptTable[0x500]  
  8. void prepareCryptTable()  
  9. {   
  10.     unsigned long seed = 0x00100001, index1 = 0, index2 = 0, i;  
  11.   
  12.     for( index1 = 0; index1 < 0x100; index1++ )  
  13.     {   
  14.         for( index2 = index1, i = 0; i < 5; i++, index2 += 0x100 )  
  15.         {   
  16.             unsigned long temp1, temp2;  
  17.   
  18.             seed = (seed * 125 + 3) % 0x2AAAAB;  
  19.             temp1 = (seed & 0xFFFF) << 0x10;  
  20.   
  21.             seed = (seed * 125 + 3) % 0x2AAAAB;  
  22.             temp2 = (seed & 0xFFFF);  
  23.   
  24.             cryptTable[index2] = ( temp1 | temp2 );   
  25.        }   
  26.    }   
  27. }  
  28.   
  29. //以下函数计算lpszFileName 字符串的hash值,其中dwHashType 为hash的类型,  
  30. //在下面GetHashTablePos函数里面调用本函数,其可以取的值为0、1、2;该函数  
  31. //返回lpszFileName 字符串的hash值;  
  32. unsigned long HashString( char *lpszFileName, unsigned long dwHashType )  
  33. {   
  34.     unsigned char *key  = (unsigned char *)lpszFileName;  
  35. unsigned long seed1 = 0x7FED7FED;  
  36. unsigned long seed2 = 0xEEEEEEEE;  
  37.     int ch;  
  38.   
  39.     while( *key != 0 )  
  40.     {   
  41.         ch = toupper(*key++);  
  42.   
  43.         seed1 = cryptTable[(dwHashType << 8) + ch] ^ (seed1 + seed2);  
  44.         seed2 = ch + seed1 + seed2 + (seed2 << 5) + 3;   
  45.     }  
  46.     return seed1;   
  47. }  
  48.   
  49. //在main中测试argv[1]的三个hash值:  
  50. //./hash  "arr\units.dat"  
  51. //./hash  "unit\neutral\acritter.grp"  
  52. int main( int argc, char **argv )  
  53. {  
  54.     unsigned long ulHashValue;  
  55.     int i = 0;  
  56.   
  57.     if ( argc != 2 )  
  58.     {  
  59.         printf("please input two arguments\n");  
  60.         return -1;  
  61.     }  
  62.   
  63.        
  64.      prepareCryptTable();  
  65.   
  66.        
  67.      for ( ; i < 0x500; i++ )  
  68.      {  
  69.          if ( i % 10 == 0 )  
  70.          {  
  71.              printf("\n");  
  72.          }  
  73.   
  74.          printf("%-12X", cryptTable[i] );  
  75.      }  
  76.   
  77.      ulHashValue = HashString( argv[1], 0 );  
  78.      printf("\n----%X ----\n", ulHashValue );  
  79.   
  80.      ulHashValue = HashString( argv[1], 1 );  
  81.      printf("----%X ----\n", ulHashValue );  
  82.   
  83.      ulHashValue = HashString( argv[1], 2 );  
  84.      printf("----%X ----\n", ulHashValue );  
  85.   
  86.      return 0;  
  87. }  
百万 Token 压力实测:1M Context 的真实能力边界在哪里? 本文揭示了长上下文模型的性能衰减规律:1M上下文存在"容量曲线"与"质量曲线"的分歧,512K是可靠拐点。通过MRCR测试发现,不同任务类型衰减差异显著——检索任务在512K后性能下降30.6%,而生成任务仅下降5%。研究提出三阶段补偿策略:三明治注入(首尾指令强化)、语义面包屑(位置标记)、引用检查(强制溯源),可将512K-1M区间的可用性提升20%。工程实践表明,中段区域(30-70%位置)是注意力薄弱区,需要特殊处理。最终建议:关键检索任务控制在512K内,关联推理任务可扩展至 阅读详情

相关推荐

Infinite-Canvas:一站式AI绘画工作流平台部署与实战指南

在 AI 绘画和内容创作领域,你是否经常遇到这样的困扰:灵感来了,却要在多个工具间来回切换——找素材、写提示词、调整参数、批量生成,整个过程繁琐且低效。创作一个系列作品时,管理不同版本的图片和提示词更是让人头疼。如果你渴望一个能整合所有流程,实现从素材管理、提示词工程到批量出图一站式完成的工作台,那么今天介绍的 Infinite-Canvas 正是为你量身打造的解决方案。 Infinite-Canvas 是一个开源的本地网页应用,它将 AI 绘画的完整工作流集成在一个界面中。无论你是想快速生成单张图片,还是

weixin_28707535的博客 337

8622 哈希查找

8622 哈希查找 时间限制:1000MS 代码长度限制:10KB 提交次数:2013 通过次数:1250 题型: 编程题 语言: G++;GCC Description 使用哈希函数:H(k)=3*k MOD length,并采用开放定址法处理冲突。试对输入的关键字序列构造哈希表,哈希表长度为length,求等概率情况下查找成功的平均查找长度,并设计构造哈希表的完整的算法。本题给出部分代码,请补全Hash函数和解决冲突的collison函数。 #include"malloc.h" /* malloc

Boucy的博客 1462

某手sig3-ios算法 Chomper黑盒调用

本文主要是介绍Chomper的算法模拟执行,Chomper目前已经已经是能比较完整的模拟ios可执行文件执行的模拟器,在这块有非常大的优势。以前安卓端有强势的unidbg,现在iOS也有Chomper了,后续等待作者持续更新,完善Chomper。强势推荐一波-> Chomper地址: https://github.com/sledgeh4w/chomper。我这块也已经使用Chomper完成雅迪系列、某手的算法调用。后续也会有更多扩展。

成小新的博客 2153

8622 哈希查找 SCAU 华南农业大学 数据结构实验(个人笔记 勿喷)

/*8622 哈希查找 时间限制:1000MS 代码长度限制:10KB 提交次数:2013 通过次数:1250 题型: 编程题 语言: G++;GCC Description 使用哈希函数:H(k)=3*k MOD length,并采用开放定址法处理冲突。试对输入的关键字序列构造哈希表,哈希表长度为length, 求等概率情况下查找成功的平均查找长度,并设计构造哈希表的完整的算法。本题给出部分代码,请补全Hash函数和解决冲突的collison函数。*/ #include"malloc.h" /*.

weixin_46809611的博客 969

手把手教你用Silvaco Atlas复现GaN蓝光LED仿真:从网格划分到光谱分析全流程避坑

本文详细介绍了如何使用Silvaco Atlas进行GaN蓝光LED仿真,从网格划分到光谱分析的全流程避坑指南。重点讲解了网格划分技巧、材料参数设置、极化效应处理以及结果分析方法,帮助用户快速掌握半导体器件仿真技术,提升研发效率。

weixin_42552716的博客 223

AI办公Agent技术:文档与表格智能处理实践

办公自动化技术正从规则驱动转向语义理解,大语言模型(LLM)为办公Agent赋予了智能决策能力。这类系统通过五层架构整合自然语言处理、工具API调用和任务规划引擎,能自动完成文档分类、表格分析等高重复性工作。关键技术包括基于TF-IDF和BERT的特征提取、Excel公式自动生成等,在财务报告、法律审查等场景可实现90%以上的效率提升。随着多模态能力和领域专业化发展,办公Agent正在重塑企业文档处理流程,成为数字化转型的核心基础设施。

weixin_30263277的博客 519

SCAU 8622 哈希查找(非填空写法

使用线性探测法,模拟一下,如果哈希后位置冲突就往后移,直到空位置(要取模) #include <iostream> #include <cstring> #include <cmath> #include <cstdio> #include <cstdlib> #include <algorithm> #include <map> #include <vector> #include <queue&..

m0_53688600的博客 563

Android P车机添加屏幕多指全局自定义手势操作

参照原生状态栏全局下拉手势拦截,实现屏幕全局多指手势响应操作,并对手势进行转换为KeyEvent,以便项目自定义手势行为或应用拦截手势行为。

shusuanly的博客 1250

实验五:8622 哈希查找

时间限制:1000MS 代码长度限制:10KB提交次数:2013 通过次数:1250题型: 编程题 语言: G++;GCC。

Y_Jiay的博客 179

[工作流节点10] 获取多条数据并循环执行 —— 明道云批处理自动化实战

本文系统介绍了明道云工作流中"获取多条数据+循环执行"的批处理功能。该功能通过筛选数据集合并逐条处理,适用于批量通知、数据更新等场景。文章详细解析了功能配置方法,包括数据筛选、循环设置和异常处理,并提供了合同到期提醒等典型应用案例。同时强调了数据量控制、分批处理、错误处理等优化策略,帮助用户实现高效批量自动化。这一功能组合能将业务从单条处理升级为批量驱动,显著提升工作效率。

每日出拳老爷子的博客 453

Unity-Mirror网络框架-从入门到精通之Mirror简介

在现代游戏开发中,网络功能日益成为提升游戏体验的关键组成部分。Mirror是一个用于Unity的开源网络框架,专为多人游戏开发设计。它使得开发者能够轻松实现网络连接、数据同步和游戏状态管理。本文将深入介绍Mirror的基本概念、如何与其他网络框架进行比较,以及如何从零开始创建一个使用Mirror的简单网络项目。Mirror是一个功能强大的Lite网络框架,旨在替代Unity的UNet网络解决方案。它提供了高效的多玩家架构、简单易用的API,以及良好的社区支持。

TxNet 3087

PHP中url参数转换Hash&nbsp;数组及逆生…

parse_str(string,array) 将xxx=1&xxx2=3 转换成数组 array{     xxx=>1     xxx2=>4 } http_build_query() 则以上数组拼回

蓝天的博客 845

若依框架二次开发——RuoYi-AI 本地部署流程

若依框架二次开发——RuoYi-AI 本地部署流程

bjzhang75的博客 1783

2022SCAU数据结构题库汇总

时间限制:1000MS 代码长度限制:10KB 提交次数:9027 通过次数:2456题型: 编程题 语言: G++;GCC 编写算法,创建初始化容量为LIST_INIT_SIZE的顺序表T,并实现插入、删除、遍历操作。本题目给出部分代码,请补全内容。....................................................................................

heimaoshei的博客 4418

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

编程艺术第二十三~四章&十一续:杨氏矩阵查找,倒排索引关键词Hash编码

  第二十三、四章:杨氏矩阵查找,倒排索引关键词Hash不重复编码实践作者:July、yansha。编程艺术室出品。出处:结构之法算法之道。前言    本文阐述两个问题,第二十三章是杨氏矩阵查找问题,第二十四章是有关倒排索引中关键词Hash编码的问题,主要要解决不重复以及追加的功能,同时也是经典算法研究系列十一、从头到尾彻底解析Hash表算法之续。    OK,有任何问题,也欢迎随时交流或批评指正...

weixin_34268310的博客 272

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

从头到尾彻底解析Hash 表算法

               十一、从头到尾彻底解析Hash 表算法 作者:July、wuliming、pkuoliver  出处:http://blog.youkuaiyun.com/v_JULY_v。   说明:本文分为三部分内容,     第一部分为一道百度面试题Top K算法的详解;第二部分为关于Hash表算法的详细阐述;第三部分为打造一个最快的Hash表算法。 --------

一座青山的专栏 1万+

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

HTML 的解析[转]

NekoHTML学习笔记   J. Andrew Clark用Java写了一系列的工具(Java APIs),NekoHTML是其中之一。    NekoHTML是一个简单地HTML扫描器和标签补偿器(tag balancer) ,使得程序能解析HTML文档并用标准的XML接口来访问其中的信息。这个解析器能投扫描HTML文件并“修正”许多作者(人或机器)在编写HTML文档 过程中常犯的错误...

ganzhijie163的博客 276

SCAU 8622 哈希查找

8622 哈希查找 时间限制:1000MS 代码长度限制:10KB 提交次数:2013 通过次数:1250 题型: 编程题 语言: G++;GCC Description 使用哈希函数:H(k)=3k MOD length,并采用开放定址法处理冲突。试对输入的关键字序列构造哈希表,哈希表长度为length,求等概率情况下查找成功的平均查找长度,并设计构造哈希表的完整的算法。本题给出部分代码,请补全Hash函数和解决冲突的collison函数。 #include"malloc.h" / malloc()

Maoxim的博客 2545

详细解说STL hash_map系列

0 为什么需要hash_map 用过map吧?map提供一个很常用的功能,那就是提供key-value的存储和查找功能。例如,我要记录一个人名和相应的存储,而且随时增加,要快速查找和修改: 岳不群-华山派掌门人,人称君子剑张三丰-武当掌门人,太极拳创始人东方不败-第一高手,葵花宝典...这些信息如果保存下来并不复杂,但是找起来比较麻烦。例如我要找"张三丰"的信息,最傻的方法就是取得所有的记录,然后

fisher_jiang的专栏 3211

【HDU 4334 Trouble 】哈希表

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4334 题目大意:输入5行,每行有n个数字,让你从这5行中每行抽取一个数让这5个数的和我0。如果存在输出Yes,否则输出No 解题思路: 用O(n^2)的时间求出前两行所有sum的值存入hash表中,注意存的时候存它的相反数,下面再解释。 然后再用O(n^3)的时间枚举后三行所...

baidui8622的博客 220

魔兽哈希算法封装和测试

摘自博客 http://blog.youkuaiyun.com/eaglewood2005/        近期由于需要,研究了魔兽文件打包管理器的相关算法,重点对其文件索引表的生成和查找进行了研究:采用哈希表进行,在冲突方面的处理方面,采用线性探测再散列。在添加和查找过程中进行了三次哈希,

wuzhekai的专栏 3245

SCAU 8621 二分查找

8621 二分查找 时间限制:1000MS 代码长度限制:10KB 提交次数:4655 通过次数:1446 题型: 编程题 语言: G++;GCC Description 编写Search_Bin函数,实现在一个递增有序数组ST中采用折半查找法确定元素位置的算法. 输入格式 第一行:元素个数n 第二行:依次输入n个元素的值(有序) 第三行:输入要查找的关键字key的值 输出格式 输出分两种情形: 1.如果key值存在,则输出其在表中的位置x(表位置从0开始),格式为The element positi

Maoxim的博客 2101

【SQL Server学习笔记】联接提示、查询提示、表提示

SQL Server的查询优化器在select查询执行的时候产生一个高效的查询执行计划。如果优化器不能选择最优的计划,那么就需要检查查询计划、统计信息、支持的索引等,而通过使用提示可以改变优化器选择查询计划的工程,使优化器生成一个更好的执行计划。 1、联接提示 <join_hint> ::= { LOOP | HASH | MERGE | REMOTE } LOOP |...

weixin_30555515的博客 283

散列表(hash)

Hash表也称散列表,也有直接译作哈希表,Hash表是一种特殊的数据结构,它同数组、链表以及二叉排序树等相比较有很明显的区别,它能够快速定位到想要查找的记录,而不是与表中存在的记录的关键字进行比较来进行查找。这个源于Hash表设计的特殊性,它采用了函数映射的思想将记录的存储位置与记录的关键字关联起来,从而能够很快速地进行查找。 1.Hash表的设计思想   对于一般的线性表,比如链表,如果要存...

River的博客 257
上一篇: 十道海量数据处理面试题-转
下一篇: Hadoop使用常见问题以及解决方法
nuoline
博客等级 码龄18年 345粉丝 247原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值