我需要处理数字序列,其中一个序列具有以下特性:
- 元素是整数
- 序列长度变化且不固定
- 整数具有上限
- 允许元素有多个重复出现
- 元素的顺序无关紧要
给定一个序列,我想知道这个序列是否已经出现过,也就是我想对序列进行哈希。例如:
[2, 3, 6, 2, 13]
并且。
[6, 3, 2, 13, 2]
这些序列应该具有相同的哈希值。
所使用的编程语言是C。
我知道我可以先对序列进行排序,然后将它们存储在一棵trie中,这绝对是一个选项。然而,为此目的选择什么样的哈希函数会比较合适呢?
要求元素的顺序不重要,这让我立刻想到了Zobrist hashing。也就是说,你需要一个将整数映射为随机二进制串的函数f
,然后你的哈希值就是序列中每个数字对应的二进制串的异或和。
当然,上面描述的基本Zobrist哈希并不满足你另一个要求:
- 允许元素多次出现
因为异或操作是自反的(即对于任何a
,都有a XOR a = 0
)。然而,只需用一些其他环操作替换XOR,这些操作不具有此属性(在普通的Zobrist哈希中,实际上被认为是理想的),例如n位加法,就可以产生你所需的哈希值:
unsigned int hash_multiset (int *seq, int n) {
unsigned int h = 0;
while (n--) h += f( *seq++ );
return h;
}
f
,只需使用一个固定的函数,它“看起来足够随机”。对于这样的函数,一个很好的选择是使用简单快速的块密码,例如TEA或(在具有硬件支持的系统上)AES,并将输出截断到您所选的哈希长度。val l = List(6, 3, 2, 13, 2)
(l.reduce(_ * _) * l.length) % 10000
不考虑性能,如果你想要一些易于审查和可靠的东西,我会选择:
var sample = new [] { 11, 55, 12, 3 };
String.Join(" ", sample.OrderBy(i => i)).GetHashCode()
如果你考虑的是一个集合而不是一个序列,你可能想要添加一个调用 .Distinct() 的操作。
hash = a[0] xor a[1] xor a[2] xor a[3] xor ...
- Hot Licks