请标记为重复,但到目前为止我发现的大多数问题都太具体或比我正在寻找的更复杂。例如。在"What is a good hash function"中,接受的答案似乎是针对散列字符串。
我最近开始使用.NET进行编程,我发现很遗憾内置类缺乏检查等式和查找哈希等基本功能的能力。我相信他们有自己的设计理由;无需保护.NET。我只是想知道当我需要使用集合作为字典的关键时如何避免重大的副轨道。例如,我希望两个不同的List对象包含所有相等的值,以映射到字典中的相同条目。开箱即用,它们不会:List的默认行为是List除了它本身不等于任何东西,因此具有相同值的列表的另一个实例是不同的键。
实施Equals非常简单。这是我不确定的哈希函数。
我是否只提供了一些可以在我的GetHashCode实现中调用的内容?
如果我必须从头开始编写,那么什么是非常简单但足够好的哈希算法?我可以使用SHA1,但我认为这将是矫枉过正的。我可以只考虑项目的所有哈希值,但我认为这会有一些讨厌的碰撞属性。我不关心计算哈希是否非常快,但我不希望我的哈希表在具有某些特定分布的数据集上减慢到线性。我想要的是这么简单,我可以记住它。如果你可以解释(或链接)它的工作原理,可以获得奖励。
答案 0 :(得分:3)
在这里要非常小心。如果您为GetHashCode
(或类似的集合)创建List<T>
方法,那么可能会执行以下操作:
public override int GetHashCode()
{
int hash = 13;
foreach (var t in this)
{
// X is an operation (undefined here) that somehow combines
// the previous hash value and the item's hash value
hash = hash X t.GetHashCode();
}
return hash;
}
(我建议用Jenkins hash来计算哈希码。同时查看Wang hash(或位混音器)。)
除非您第一次计算该值并对其进行缓存,否则每次调用GetHashCode
时都会迭代所有项目。
因此,您为集合创建了GetHashCode
和Equals
,并将实例放入Dictionary
。现在你必须非常小心,不要更改集合(即不添加或删除任何项目)或集合中的任何项目。否则GetHashCode
的值将会改变,字典将不再起作用。
我强烈建议如果你想使用一个集合作为字典的关键,你就要确保集合是不可变的。
另外要考虑的事情。列表相等的概念并不像您指出的那么简单。例如,列表[1, 2, 3, 4, 5]
和[5, 1, 3, 4, 2]
是否相等?这取决于你对平等的定义。当然A.Union(B) == A.Intersect(B)
,这意味着如果你的平等定义是“包含相同的项目”,它们就是平等的。但是如果订单很重要,那么列表就不相同了。
如果您的定义是“包含相同的项目”,那么我上面显示的哈希码计算将不起作用,因为哈希码计算是依赖于顺序的。因此,如果您想计算这些列表的哈希码,则必须先对它们进行排序。
如果列表不能包含重复项,那么计算相等性就是创建一个列表的哈希集并查找该哈希集中其他列表中的每个项目。如果列表可以包含重复项,那么您必须对它们进行排序以确定相等性,或者使用某种带有计数的字典。并且这两个暗示列表中包含的对象将实现某种形式的相等比较器等。
一些平等的定义根本不考虑重复。也就是说,[1, 2, 3]
将等于[3, 3, 3, 2, 1, 1]
。
考虑到平等的不同差异以及为定义List<T>
的行为而允许的那些以及更多的努力,我可以理解为什么设计集合类的人没有实现值相等。特别是考虑到使用List<T>
或类似集合作为字典或哈希表中的键非常罕见。
答案 1 :(得分:2)
根据我的经验,如果你有一些东西并且想要计算它们的哈希值,最好分别计算每个单独对象的哈希值;将所有这些哈希值收集到一个数组中。最后,计算哈希值数组的哈希值。
所有更简单的技术都会相对较快地崩溃。 (比如将值一起进行异或或乘以幻数和求和 - 这些都有各种各样的病态失败案例。)你最后计算的一个额外的数组哈希是一个很小的代价并且总体上有回报。
答案 2 :(得分:0)
一个好的哈希函数对于任何位的字符串同样有效 - 而不仅仅是字符。但是,由于集合可能:
...在我看来,这里的关键问题可能是“将一组单独的哈希值组合起来为集合生成哈希值的最佳方法是什么?”。
在我看来,对集合中各个元素的哈希值进行异或运算是一种合理的方法。我可以立即看到的唯一问题是,它会导致两个具有相同元素的集合,但包含在不同的顺序中,散列到相同的值。避免此问题的算法可能如下所示: