给定一组整数,一个"功能组",是否有更好的方法来获取整数的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;真的是关键,而不是真正依赖于订单
答案 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;
}
}
hash == -2145263134
hashReordered == -2145263134
答案 1 :(得分:1)
我建议您首先通过查找规范排列(例如,首先对列表进行排序),然后使用您想要的任何哈希值对其进行哈希处理,而不是找到排列不变的哈希值。
请注意,由于这是我们所说的整数,您可以使用基数排序在线性时间内完成。
答案 2 :(得分:0)
循环的每次迭代都包括乘法和加法运算。这些不是相互交换的。如果你是将加法更改为乘法,然后所有操作都是相互交换的,列表的排序无关紧要。
虽然任何包含哈希值为零的列表都会有一个零的哈希码,所以你可能会特别注意这个值。