Hash 整数散列序列

Hash 整数散列序列,hash,Hash,我必须处理数字序列,其中序列具有以下属性: 元素是整数 序列的长度是变化的,不是固定的 整数有一个上界 允许元素多次出现 元素的顺序并不重要 给定一个序列,我想知道这个序列是否已经发生,也就是说我想散列序列。比如说, [2, 3, 6, 2, 13] 及 应该具有相同的哈希值 正在使用的编程语言是C 我知道我可以先对序列进行排序,然后将它们存储在trie中,这绝对是一种选择。然而,对于这个目的,什么样的散列函数才是合适的呢?将所有的数字和序列的长度相乘,对一些相当大的数字进行模化,怎么样?

我必须处理数字序列,其中序列具有以下属性:

  • 元素是整数
  • 序列的长度是变化的,不是固定的
  • 整数有一个上界
  • 允许元素多次出现
  • 元素的顺序并不重要
给定一个序列,我想知道这个序列是否已经发生,也就是说我想散列序列。比如说,

[2, 3, 6, 2, 13]

应该具有相同的哈希值

正在使用的编程语言是C


我知道我可以先对序列进行排序,然后将它们存储在trie中,这绝对是一种选择。然而,对于这个目的,什么样的散列函数才是合适的呢?

将所有的数字和序列的长度相乘,对一些相当大的数字进行模化,怎么样?下面是一些显示计算的Scala代码:

val l = List(6, 3, 2, 13, 2)
(l.reduce(_ * _) * l.length) % 10000
结果是:4680

显然,这并不能保证如果散列匹配,序列是唯一的。(这甚至可能不是一个很好的近似值!)但是,如果散列不匹配,则可以保证序列不相同

  • 元素的顺序并不重要
让我立刻想到了这样的事情。也就是说,您将有一个函数
f
将整数映射到随机位字符串,并且您的哈希将只是与序列中的数字对应的位字符串的异或

当然,上面描述的基本Zobrist散列不能满足您的其他要求,即

  • 允许元素多次出现
因为异或运算是其自身的逆运算(即,对于任何
a
,异或a=0)。但是,简单地将XOR替换为没有此属性的其他操作(在正常的Zobrist散列中,这实际上被认为是可取的),例如n位加法,应该会产生您想要的散列:

unsigned int hash_multiset(int*seq,int n){
无符号整数h=0;
而(n--)h+=f(*seq++);
返回h;
}
(关于此函数需要注意的一个小细节是,如果要截断其输出,最好使用高位而不是低位。这是因为,如果序列的散列中的k个最低位
[A]
[b]
发生冲突,那么
[A,A]
[b,b]的k个最低位也会发生冲突
[a,b]
等等。对于k个最高位,这是不正确的,因为较低的位可以带入较高的位,产生更多的“随机”输出。)

有多种方法可以实现函数
f
。对于有限范围的输入整数,只需使用随机位字符串的固定查找表即可。或者,如果您事先不知道输入的范围,可以使用另一个(普通)哈希表将整数映射到随机位字符串,然后“动态”构建它


最后,它还可以实现
f
,而无需查找表,只需使用“看起来足够随机”的固定函数即可。对于这样一个函数,一个好的选择是使用简单而快速的函数,例如or(在有硬件支持的系统上),将输出截断为您首选的散列长度。

所以它们实际上更多的是集合而不是序列,对吗?因为顺序无关紧要?一个简单的旧XOR将是一个合理的起点。“整数可能有一个事先未知的上界”。。。如果有有限多个集合,并且每个集合的大小都是有限的,那么您可以更具体地说“整数肯定有上界…”。虽然我不完全确定这个事实是否真的与这个问题有关。一个想法是将每个集合放入某种规范形式(例如,对其进行排序),并生成一个良好的抗冲突哈希(MD5,SHA-*,取决于您期望的数量),并将其存储。
hash=a[0]xor a[1]xor a[2]xor a[3]xor…
需要注意的重要一点是,无论顺序如何,您都需要相同的哈希。这意味着您要么需要不区分顺序的散列,要么需要在散列之前按规范顺序放置元素(即,对它们进行排序)。顺序不敏感的散列将是加法(带溢出)、XOR和一些不同的位攻击技术。“罐装”散列算法通常(根据设计)不区分顺序,这意味着您必须在应用它们之前进行排序。这个想法的integer mod组件几乎可以保证,如果我有10000个或更多序列,我将发生错误冲突。谢谢,这似乎是一个选项。这看起来非常有趣,谢谢。n=序列的长度吗?是。C数组不存储自己的长度,因此您需要以某种方式将其传递给函数。
val l = List(6, 3, 2, 13, 2)
(l.reduce(_ * _) * l.length) % 10000