完美的哈希函数

时间:2010-11-09 06:04:26

标签: hash hashtable perfect-hash

我正在尝试散列值

10, 100, 32, 45, 58, 126, 3, 29, 200, 400, 0

我需要一个函数将它们映射到一个大小为13的数组而不会引起任何冲突。

我花了几个小时思考这个并用谷歌搜索而无法解决这个问题。我还没有接近可行的解决方案。

我如何找到这种哈希函数?我玩过gperf,但我真的不明白,我无法得到我想要的结果。

7 个答案:

答案 0 :(得分:24)

如果您知道确切的密钥,那么生成完美的哈希函数是微不足道的 -

int hash (int n) {
  switch (n) {
    case 10:   return 0;
    case 100:  return 1;
    case 32:   return 2;
    // ...
    default:   return -1;
  }
}

答案 1 :(得分:11)

找到一个

我尝试了一些东西,并半手动找到了一个:

(n ^ 28) % 13

半手动部分是以下ruby脚本,我用它来测试具有一系列参数的候选函数:

t = [10, 100, 32, 45, 58, 126, 3, 29, 200, 400, 0]
(1..200).each do |i|
  t2 = t.map { |e| (e ^ i) % 13 }
  puts i if t2.uniq.length == t.length
end

答案 2 :(得分:5)

在某些平台(例如嵌入式)上,模运算很昂贵,因此可以更好地避免使用% 13。但是低阶位的AND操作很便宜,相当于2的幂的模数。

我尝试编写一个简单的程序(在Python中),使用((x << a) ^ (x << b)) & 0xF等简单形式搜索11个数据点的完美哈希值(其中& 0xF等同于% 16 ,给出0..15范围内的结果,例如)。我能够找到以下无冲突的哈希,它给出一个0..15范围内的索引(表示为C宏):

#define HASH(x)    ((((x) << 2) ^ ((x) >> 2)) & 0xF)

这是我使用的Python程序:

data = [ 10, 100, 32, 45, 58, 126, 3, 29, 200, 400, 0 ]

def shift_right(value, shift_value):
    """Shift right that allows for negative values, which shift left
    (Python shift operator doesn't allow negative shift values)"""
    if shift_value == None:
        return 0
    if shift_value < 0:
        return value << (-shift_value)
    else:
        return value >> shift_value

def find_hash():
    def hashf(val, i, j = None, k = None):
        return (shift_right(val, i) ^ shift_right(val, j) ^ shift_right(val, k)) & 0xF

    for i in xrange(-7, 8):
        for j in xrange(i, 8):
            #for k in xrange(j, 8):
                #j = None
                k = None
                outputs = set()
                for val in data:
                    hash_val = hashf(val, i, j, k)
                    if hash_val >= 13:
                        pass
                        #break
                    if hash_val in outputs:
                        break
                    else:
                        outputs.add(hash_val)
                else:
                    print i, j, k, outputs

if __name__ == '__main__':
    find_hash()

答案 3 :(得分:3)

只是一些准分析性的谣言:

在你的数字集合中,总共11个,三个是奇数,八个是偶数。 查看最简单的散列形式 - %13 - 将为您提供以下散列值:  10 - 3, 100 - 9,  32 - 6,  45 - 6,  58 - 6, 126 - 9,   3 - 3,  29 - 3, 200 - 5, 400 - 10,   0 - 0

当然,由于碰撞的数量,这是无法使用的。需要更精细的东西。

为什么说明显? 考虑到数字很少,任何精心设计 - 或者更确切地说,“不那么简单” - 算法可能比switch语句或(我更喜欢)简单地搜索大小十一个位置的无符号短/长向量并使用比赛的索引。

为什么要使用矢量搜索?

  1. 您可以通过将最常出现的值放在向量的开头来对其进行微调。
  2. 我假设目的是将哈希索引插入到具有良好的顺序编号的交换机中。在这种情况下,首先使用开关找到索引然后将其插入另一个开关似乎很浪费。也许您应该考虑不使用散列并直接进入最终开关?
  3. 散列的切换版本无法微调,并且由于值差别很大,将导致编译器生成二进制搜索树,这将导致大量的比较和条件/其他跳转(特别是代价高昂)花时间(我假设你已经转向哈希速度)并且需要空间。
  4. 如果你想加速矢量搜索,并且正在使用x86系统,你可以基于汇编程序指令实现矢量搜索repne scasw(short)/ repne scasd(long),这将更快。在一些指令的设置时间之后,您将在一条指令中找到第一个条目,在十一条指令中找到最后一条,然后是几条指令清理。这意味着5-10指令最佳情况和15-20最差情况。除了可能只有一两种情况,这应该超过基于交换机的散列。

答案 4 :(得分:2)

鲍勃·詹金斯也有一个这样的计划:http://burtleburtle.net/bob/hash/perfect.html

除非你很幸运,否则给定数据集没有“漂亮”的完美哈希函数。完美的散列算法通常在键上使用简单的散列函数(使用足够的位以使其无冲突)然后使用表来完成它。

答案 5 :(得分:0)

我做了一个快速检查并使用SHA256哈希函数然后在我在Mathematica中尝试时使用13进行模块化除法。对于c ++,此函数应该在openssl库中。见post

如果您正在进行大量的散列和查找,那么模块化分区是一项非常昂贵的操作,可以重复执行。还有另一种将n位散列函数映射到i位索引的方法。请参阅Michael Mitzenmacher的post,了解如何使用C中的位移操作。希望有所帮助。

答案 6 :(得分:0)

尝试以下操作,将n值映射到0到12之间的唯一索引 (1369%(N + 1))%13