Hashtable / Dictionary碰撞

时间:2009-04-09 18:11:33

标签: c# string math dictionary hashtable

仅使用标准英文字母和下划线,最多可以使用多少个字符,而不会在哈希表/字典中造成潜在的冲突。

如此字符串:

blur
Blur
b
Blur_The_Shades_Slightly_With_A_Tint_Of_Blue

...

5 个答案:

答案 0 :(得分:15)

无法保证您不会在单个字母之间发生碰撞。

可能不会,但string.GetHashCode中使用的算法未指定,可能会更改。 (特别是它在.NET 1.1和.NET 2.0之间发生了变化,这使人们认为它不会改变。)

请注意,哈希代码冲突不会阻止精心设计的哈希表工作 - 您应该仍然能够获得正确的值,如果有的话,它可能需要使用相等性来检查多个密钥相同的哈希码。

任何依赖哈希码唯一的字典都缺少有关哈希码的重要信息,IMO :)(除非它在非常特定条件下运行,它绝对知道他们将是唯一的,即它使用perfect hash function。)

答案 1 :(得分:3)

鉴于perfect hashing function(你通常不会像其他人提到过的那样),你可以找到最大可能的字符数,以确保不会产生两个字符串碰撞,如下:


没有。独特的哈希码avilable = 2 ^ 32 = 4294967296(假设一个32位整数用于哈希码) 字符集的大小= 2 * 26 + 1 = 53(拉丁字母表中的大写字母为26,加上下划线)

然后,您必须考虑长度为l(或更少)的字符串总共有54 ^ l个表示形式。请注意,基数是54而不是53,因为字符串可以在任何字符之后终止,为每个字符添加额外的可能性 - 而不是它会对结果产生很大影响。

采取不。作为最大字符串表示形式的唯一哈希码,您可以得到以下简单的等式:

54 ^ l = 2 ^ 32

解决它:

log2 (54 ^ l) = 32
l * log2 54 = 32
l = 32 / log2 54 = 5.56

(其中log2是基数2的对数函数。)

由于字符串长度显然不能是分数,因此您可以使用不可分割的部分来提供 5 的最大长度。确实很短,但是观察到这种限制可以防止在给定完美散列函数的情况下发生碰撞的最远的机会。


然而,正如我所提到的,这在很大程度上是理论上的,而且我不确定它在任何设计考虑中可能有多大用处。这样说,希望它可以帮助你从理论的角度理解这个问题,在此基础上你可以添加实际的思考(例如非完美的哈希函数,分布的不均匀性)。

答案 2 :(得分:3)

Universal Hashing

计算与S个字符串冲突的概率,其中每个字符的L位为W位,长度为H位,假设最佳universal hash }( 1 )您可以根据大小(桶数)'N`的哈希表计算冲突概率。

首先,我们可以假设一个理想的哈希表实现( 2 ),将哈希中的H位完美地分成可用的桶N 3 )。这意味着H变得毫无意义,除了作为N的限制。 W和'L'只是S上限的基础。对于更简单的数学,假设字符串长度< L只是用一个特殊的空字符填充到L。如果我们感兴趣我们感兴趣的是最坏的情况,这是54 ^ L(26 * 2 +'_'+ null),显然这是一个荒谬的数字,实际的条目数比字符更有用设置和长度,所以我们将简单地工作,好像S本身就是一个变量。

我们只是试图将S个项目放入N个存储桶中。 这就成了一个众所周知的问题,birthday paradox

解决这个问题的各种概率和桶的数量是instructive,但假设我们有10亿个桶(因此在32位系统中大约有4GB内存),那么在我们达到50%之前我们只需要37K个条目他们至少有一次碰撞的可能性。鉴于试图避免哈希表中的任何冲突变得明显荒谬。

所有这些并不意味着我们不应该关心关于哈希函数的行为。显然,这些数字假设是理想的实现,它们是我们能够获得多少好的上限。糟糕的哈希函数可以在某些区域产生更糟糕的冲突,通过永远或很少使用它来浪费一些可能的“空间”,所有这些都可能导致哈希不是最优的,甚至会降低到看起来像列表的性能但是更差常数因素。

.NET框架的字符串散列函数的实现并不是很好(因为它可能更好),但对于绝大多数用户来说可能是可以接受的,并且计算起来相当有效。

另类方法:完美哈希

如果您希望生成所谓的perfect hashes,则需要事先完全了解输入值,但这并不常见。与上述数学相似,我们可以证明即使是完美的哈希也有其限制:

召回长度为L的54 ^ L个字符串的限制。然而,我们只有H位(我们假定为32),这是大约40亿个不同的数字。因此,如果您可以拥有真正的任何字符串及其中的任意数量,那么您必须满足:

54 ^ L <= 2 ^ 32

解决它:

log2 (54 ^ L) <= 32
L * log2 54 <= 32
L <= 32 / log2 54 <= 5.56

由于字符串长度显然不能是小数,因此最多只剩下5个。确实很短。

如果你知道你将只拥有一组远低于40亿的字符串,那么完美的散列会让你处理L的任何值,但是在实践中限制这组值可能非常困难并且您必须提前知道它们或降级到数据库的字符串 - &gt;哈希并在遇到新字符串时添加它。


  1. 对于本练习,通用散列是最佳的,因为我们希望降低任何碰撞的概率,即对于任何输入,它从一组可能性R中输出x的概率为1 / R

  2. 请注意,在散列(和内部分段)上做一个最佳的工作是相当困难的,但是你应该期望内置类型是合理的,如果不总是理想的话。

  3. 在这个例子中,我避免了封闭和开放寻址的问题。这确实与所涉及的概率有关,但并不显着

答案 3 :(得分:1)

哈希算法不应该保证唯一性。鉴于有更多潜在的字符串(长度为26 ^ n,甚至忽略特殊字符,空格,大写字母,非英语字符等),而不是哈希表中的地方,这样的保证无法实现。它只能保证良好的分配。

答案 4 :(得分:0)

如果您的密钥是字符串(例如,字典),那么将使用它的GetHashCode()。这是一个32位整数。 Hashtable默认为1键来计算负载因子,并增加桶的数量以维持该负载因子。因此,如果你看到碰撞,它们应该倾向于在重新分配边界周围发生(并在重新分配后不久减少)。