Algorithm 测量哈希函数的质量(用于映射/关联数组)

Algorithm 测量哈希函数的质量(用于映射/关联数组),algorithm,hash,map,Algorithm,Hash,Map,我正在研究C语言中的关联数组库(我没有编写)。类似于C++中的映射或Python的DICT的< /P> 有一些非标准的散列函数,我不确定它们是否很好。(也许最初的开发人员只是抛出了一些神奇的数字、xor运算符,并希望得到最好的结果) 我编写了一个测试,测量给定一些示例输入时哈希函数的性能,以测量它如何将项均匀地分配到固定数量的bucket中(在本例中为模数组大小) 这样,如果有足够的输入,就会有某种方法来衡量散列函数的性能 对于任何编写关联数组的人来说,这似乎都是一个常见的问题 是否有一些惯例

我正在研究C语言中的关联数组库(我没有编写)。类似于C++中的映射或Python的DICT的< /P> 有一些非标准的散列函数,我不确定它们是否很好。(也许最初的开发人员只是抛出了一些神奇的数字、xor运算符,并希望得到最好的结果)

我编写了一个测试,测量给定一些示例输入时哈希函数的性能,以测量它如何将项均匀地分配到固定数量的bucket中(在本例中为模数组大小)

这样,如果有足够的输入,就会有某种方法来衡量散列函数的性能

对于任何编写关联数组的人来说,这似乎都是一个常见的问题


是否有一些惯例来衡量散列函数的性能?(就分销质量而不是速度而言)

其中,最坏的结果是每个输入的结果相同,而最好的结果是均匀分布(或尽可能接近)


注意,我不是在这里寻找加密强度。

一个有用的基准是可重复的随机性——我指的是将每个不同的密钥分配给随机选择的bucket的冲突倾向

例如,如果1000个bucket表中有500个值,那么实现此基准意味着插入新值时发生冲突的概率为0.5(无论与现有元素相比如何)

如果哈希表中有一些指令插入,以便知道插入值时发生了多少冲突,那么您可以(例如)插入元素,直到达到特定的
size():bucket
比率(如0.5),然后循环插入一个元素(并累积冲突计数器)然后删除另一个,直到你有一个好的样本大小。然后,您可以将冲突率与0.5进行对比,以了解哈希函数的质量。或者,您的指令插入可能会让您插入过多的值,然后对它们进行迭代,询问要从元素散列到实际元素的bucket中得到多少冲突元素必须跳过。您也可以在插入所有值时测量碰撞次数,但是您必须考虑哈希表的重排大小之间的<>代码> siz():桶的< /代码>比率。

< P> >龙骨书中有一页(中间页)。

我个人有一个经验法则:(假设线性链接)将N个项目插入N个槽->链,并计算获得所有N个元素所需的访问总数(链中第一个:=1个访问;第二个:=2个访问,等等)。(这等于所有链的总和(chainlen*(chainlen+1)/2)

给定随机输入数据,对于任何合理的hashfunction,该度量值应为1.5*N,或略低于该值


使用2543846个唯一标记/单词(及其统计信息)列表的典型运行示例 散列到2543846个槽/桶中:

plasser@pisbak:~/src/hash$ ./diskhash woorden.txt woorden.hsh
Ptr = 0x7fb5c264f000, Sz = 37362821
Array= 0x7fb5bff7e000 Cnt = 2543846
__________________
Histogram of seek lenghts:
len:    Count     Hops   Fraction (Cumulative)
  1:  1606429  1606429 0.63149617 (0.63149617)
  2:   672469  1344938 0.26435130 (0.89584747)
  3:   205046   615138 0.08060472 (0.97645219)
  4:    48604   194416 0.01910650 (0.99555869)
  5:     9477    47385 0.00372546 (0.99928415)
  6:     1581     9486 0.00062150 (0.99990565)
  7:      215     1505 0.00008452 (0.99999017)
  8:       24      192 0.00000943 (0.99999961)
  9:        1        9 0.00000039 (1.00000000)
Tot:  2543846  3819498           (1.50147)
Cnt  2543846 Empty   937417 (0.36850) Collisions 247 RedDragon 7638996/7631537=1.000977
__________________
  • 空插槽的分数为0.36850,约为应为(1/e)
  • 具有多个项目(链长度>1)的插槽部分也约为(1/e)
  • 正好有1个项目的插槽部分是剩余的::1-(2/e)
  • 冲突的数量似乎有点高,但在32位哈希值上有2.5 M项,这并不例外

根据@wildplasser的回答,以下是来自的方法,作为Python3.x中的测试函数,用于验证其是否按预期工作,结果如下:

从随机导入随机
def hash_在_范围内(bucket_tot):
#只需返回一个随机数即可模拟一个相当均匀的哈希函数:
#使用'random()`->`~1.0`得分
#将'random()'替换为'random()*0.5`->`~2.0`分数
#将'random()'替换为'random()*0.1`->`~10.0`分数
返回int(random()*bucket_tot)
def计数_质量(桶_项目):
总和=0
对于桶中物品的计数:
总和+=计数*(计数+1)
返回值(总和*len(存储单元项目)/(存储单元项目计数*(存储单元项目计数+2*len(存储单元项目)-1)))
def测试(桶总数、项目计数):
bucket_items=[0]*bucket_tot
对于范围内的i(项目数量):
bucket=hash_在_范围内(bucket_tot)
bucket_项目[bucket]+=1
打印(“测试:”,桶总计,项目计数,结束=“\t”)
分数=计数\质量(桶\项目,项目\计数)
打印(分数)
测试(10010000)
测试(1000100000)
测试(10000,1000000)
测试(100010000)
测试(1000100000)
测试(1001000000)
测试(10001000000)
测试(500100000)
测试(25100000)
示例C函数,其中bucket是一个单链表指针数组

双哈希计算质量(结构哈希*哈希)
{
uint64_t sum=0;
无符号整数i;
如果(散列->输入项==0)
回报率-1.0;
对于(i=0;inbuckets;i++){
uint64_t计数=0;
条目*e;
对于(e=hash->bucket[i];e;e=e->next){
计数+=1;
}
总和+=计数*(计数+1);
}
返回((双)和*(双)散列->nbuckets/
((双)散列->nentries*(散列->nentries+2*散列->nbuckets-1));
}

关于碰撞的可能性,仅测量碰撞不会检测到不平衡的结果(例如,如果一个桶得到许多物品)。到目前为止,我使用的方法是与最好的情况进行比较(例如,将30个项目放入10个桶中,每个桶中有3个项目),然后累积差异并报告。@ideasman42:如果您想建立碰撞深度的直方图,那么请自己动手。。。同样,一个有效的随机放置是您的基准,并且您可以通过使用加密散列然后比较您的直方图来轻松实现。另外,并非所有哈希表实现都允许每个bucket有多个项-这是一个实现问题。。。