排列的好哈希函数?

时间:2009-10-08 08:22:44

标签: performance hash permutation

我有特定范围内的数字(通常从0到1000左右)。算法从该范围中选择一些数字(大约3到10个数字)。这种选择经常进行,我需要检查是否已经选择了所选数字的排列。

例如,一步选择[1, 10, 3, 18],另一步选择[10, 18, 3, 1],然后可以丢弃第二个选择,因为它是一个排列。

我需要非常快速地进行检查。现在我把所有数组都放在一个hashmap中,并使用一个自定义哈希函数:只需要总结所有元素,所以1 + 10 + 3 + 18 = 32,还有10 + 18 + 3 + 1 = 32。对于equals,我使用bitset快速检查元素是否在两个集合中(我在使用bitset时不需要排序,但只有在数字范围已知且不太大时才有效。)

这可以正常工作,但可以产生大量冲突,因此经常调用equals()方法。我想知道是否有更快的方法来检查排列?

排列有没有好的哈希函数?

更新

我做了一点基准测试:生成0到6范围内的所有数字组合,以及数组长度1到9.有3003种可能的排列,并且应该在这么多不同的哈希值附近生成一个好的哈希值(我使用散列的32位数字:

  • 仅添加41个不同的哈希值(因此存在大量冲突)
  • 将XOR'ing值组合在一起的8个不同的哈希值
  • 用于乘法的286个不同的哈希
  • (R + 2e)3003个不同的哈希值,并且如abc所示相乘(使用1779033703表示R)

所以abc的哈希值可以非常快速地计算出来并且比其他所有哈希值都要好得多。谢谢!

PS:我不想在不需要时对值进行排序,因为这样会太慢。

7 个答案:

答案 0 :(得分:6)

一个潜在的候选人可能是这个。 修复一个奇数整数R. 对于要散列的每个元素,计算因子(R + 2 * e)。 然后计算所有这些因素的乘积。 最后将产品除以2得到哈希值。

(R + 2e)中的因子2保证所有因子都是奇数,因此避免 产品将永远变为0。最后除以2是因为 产品总是奇数,因此除法只是删除一个恒定位。

E.g。我选择R = 1779033703。这是一个随意的选择,做一些实验应该显示给定的R是好还是坏。假设你的值是[1,10,3,18]。 产品(使用32位整数计算)

(R + 2) * (R + 20) * (R + 6) * (R + 36) = 3376724311

因此哈希将是

  

3376724311/2 = 1688362155。

答案 1 :(得分:5)

总结元素已经是你可以做的最简单的事情之一。但我不认为这是一个特别好的哈希函数w.r.t.伪随机性。

如果在存储数组或计算哈希值之前排序数组,那么每个好的哈希函数都可以。

如果是关于速度:你有没有测量瓶颈的位置?如果你的哈希函数给你带来了很多冲突,并且你必须花费大部分时间来逐位比较数组,那么哈希函数显然不擅长它应该做的事情。排序+更好的哈希可能是解决方案。

答案 2 :(得分:3)

如果我正确理解您的问题,您希望测试未订购商品的集合之间的相等性。这正是Bloom过滤器将为您做的事情。以少量误报为代价(在这种情况下你需要调用暴力集合比较),你将能够通过检查它们的Bloom过滤器散列是否相等来比较这些集合。 / p>

这个代数的代数原因是OR运算是可交换的。这也适用于其他半环境。

答案 3 :(得分:0)

取决于你是否有很多冲突(所以相同的哈希而不是排列),你可以在对数组进行哈希处理时预先排序。在这种情况下,你可以做一个更积极的散列,你不仅要添加数字,还要添加一些bitmagick,以获得完全不同的哈希值。

这只有在你遇到大量不必要的冲突时才有用,因为你现在正在做的哈希太差了。如果你几乎没有碰撞,你使用的方法似乎很好

答案 4 :(得分:0)

我喜欢使用字符串的默认哈希码(Java,C#不确定其他语言),它会生成非常独特的哈希码。 因此,如果您首先对数组进行排序,然后使用某个分隔符生成唯一的字符串。

所以你可以做以下(Java):

    int[] arr = selectRandomNumbers();
    Arrays.sort(arr);
    int hash = (arr[0] + "," + arr[1] + "," + arr[2] + "," + arr[3]).hashCode();

如果性能是个问题,您可以将建议的低效字符串连接更改为使用StringBuilder或String.format

   String.format("{0},{1},{2},{3}", arr[0],arr[1],arr[2],arr[3]);

字符串哈希码当然不能保证两个不同的字符串有不同的哈希值,但考虑到这种建议的格式,冲突应该是非常罕见的

答案 5 :(得分:0)

我会建议: 1.检查排列的长度是否相同(如果不相同 - 它们不相等)

  1. 仅排序1个数组。而不是排序另一个数组迭代第一个数组的元素并在第二个数组中搜索每个数组的存在(仅在第二个数组中的元素较小时进行比较 - 不要遍历整个数组)。
  2. 注意:如果您的permutaions中可以包含相同的数字(例如[1,2,2,10]),那么当它与第一个数组中的成员匹配时,您将需要从第二个数组中删除元素。

    伪码:

    if length(arr1) <> length(arr2) return false;
    sort(arr2);
    for i=1 to length(arr1) {
    elem=arr1[i];
    j=1;
    while (j<=length(arr2) and elem<arr2[j]) j=j+1;
    if elem <> arr2[j] return false;
    }
    return true;
    

    这个想法是,我们可以尝试匹配排序数组中的所有元素,而不是排序另一个数组。

答案 6 :(得分:0)

您可以通过使用产品以及术语的总和来减少大量碰撞。

1 * 10 * 3 * 18 = 540且10 * 18 * 3 * 1 = 540

所以sum-product哈希值为[32,540]

当碰撞发生时,你仍然需要对碰撞做些什么。