对于整数列表的良好散列函数,其中顺序不会改变值

时间:2015-02-04 16:52:51

标签: c#

给定一组整数,一个"功能组",是否有更好的方法来获取整数的GetHashCode,其中数字的位置不会影响哈希?

void Main()
{
    int[] ints = { 10001, 10002, 10003, 10004, 10005 };

    int hash = GetHashCode(ints);

    Console.WriteLine("hash={0}", hash);
}

int GetHashCode(IEnumerable<int> integers)
{
    IEnumerator<int> intEnum = integers.GetEnumerator();

    if(intEnum.MoveNext()==false) return 0;

    int hash = 0;
    unchecked {
        hash = intEnum.Current.GetHashCode();
        for(;intEnum.MoveNext()==true;)
            hash = 31 * hash + intEnum.Current.GetHashCode();
    }

    return hash;
}

输出为:hash = 954101523 如果我交换10003和10002我得到:hash = 954130353

除了在获取哈希值之前对列表进行排序之外,如果列表位置中的项目发生更改,是否有更好的替代方法?

整数列表基本上代表一组记录ID,它们是一个&#34;功能组&#34;,所以&#34;功能组&#34;真的是关键,而不是真正依赖于订单

3 个答案:

答案 0 :(得分:2)

具有良好的一值哈希函数的加法器

由于Hash Function Prospector,一个好的单值哈希函数在C语言中实现了公共领域的实现:

// exact bias: 0.020888578919738908
uint32_t
triple32(uint32_t x)
{
    x ^= x >> 17;
    x *= UINT32_C(0xed5ad4bb);
    x ^= x >> 11;
    x *= UINT32_C(0xac4c1b51);
    x ^= x >> 15;
    x *= UINT32_C(0x31848bab);
    x ^= x >> 14;
    return x;
}

您可以将其转换为C#,将其应用于每个值,然后将所有散列结果求和。加法完全满足您的“顺序无关紧要”标准,因为顺序与加法无关紧要,您仍然会得到相同的结果。上面的一值哈希函数可以满足您对体面哈希函数的需求。

实施

以下实现了上述想法(通过测试重新排列以显示它给出相同的哈希值):

using System;
using System.Collections.Generic;

public class Test
{
    static void Main()
    {
        int[] ints = { 10001, 10002, 10003, 10004, 10005 };
        int hash = GetHashCode(ints);
        int[] reorderedInts = { 10004, 10002, 10005, 10001, 10003 };
        int reorderedHash = GetHashCode(reorderedInts);

        Console.WriteLine("hash          == {0}", hash);
        Console.WriteLine("hashReordered == {0}", reorderedHash);
    }

    static int GetHashCode(IEnumerable<int> integers)
    {
        int hash = 0;

        foreach(int integer in integers)
        {
            int x = integer;

            x ^= x >> 17;
            x *= 830770091;   // 0xed5ad4bb
            x ^= x >> 11;
            x *= -1404298415; // 0xac4c1b51
            x ^= x >> 15;
            x *= 830770091;   // 0x31848bab
            x ^= x >> 14;

            hash += x;
        }

        return hash;
    }
}

produces the output

hash          == -2145263134
hashReordered == -2145263134

答案 1 :(得分:1)

我建议您首先通过查找规范排列(例如,首先对列表进行排序),然后使用您想要的任何哈希值对其进行哈希处理,而不是找到排列不变的哈希值。

请注意,由于这是我们所说的整数,您可以使用基数排序在线性时间内完成。

答案 2 :(得分:0)

循环的每次迭代都包括乘法和加法运算。这些不是相互交换的。如果你是将加法更改为乘法,然后所有操作都是相互交换的,列表的排序无关紧要。

虽然任何包含哈希值为零的列表都会有一个零的哈希码,所以你可能会特别注意这个值。