为啥这个特定的函数是一个糟糕的散列函数?
Posted
技术标签:
【中文标题】为啥这个特定的函数是一个糟糕的散列函数?【英文标题】:Why is this certain function a bad hashing function?为什么这个特定的函数是一个糟糕的散列函数? 【发布时间】:2021-03-22 03:30:18 【问题描述】:我想知道为什么下面的代码 sn-p 是一个不好的哈希函数。
def computeHash(self, s):
h = 0
for ch in s:
h = h + ord(ch) # ord gives the ASCII value of character ch
return h % HASH_TABLE_SIZE
如果我大幅增加哈希表的大小,这会弥补哈希函数的不足吗?
【问题讨论】:
【参考方案1】:这是一个糟糕的散列函数,因为字符串是顺序敏感的,但散列不是; "ab"
和 "ba"
的哈希值相同,对于更长的字符串,冲突只会变得更糟;所有"abc"
、"acb"
、"bac"
、"bca"
、"cab"
、"cba"
将共享相同的哈希。
对于顺序不敏感的数据结构(例如frozenset
),这样的策略并没有那么糟糕,但是通过简单地减少一个序数并增加一个序数来产生冲突仍然太容易了一个接一个,或者简单地在其中放一个NUL
字符; frozenset('\0', 'a')
的哈希值与 frozenset('a')
相同;通常,这可以通过以某种方式将集合的长度合并到散列中来解决。
安全哈希(例如Python uses SipHash)是最好的解决方案;随机种子与一种算法相结合,该算法隐藏种子,同时将其合并到生成的哈希中,这不仅使得意外更难创建冲突(像散列索引和序数这样的简单改进将有助于,以使哈希顺序和长度敏感),但也使恶意数据源几乎不可能故意引起冲突(简单的解决方案根本无法处理)。
这个散列的另一个问题是它没有均匀地分配位;短字符串意味着在哈希中仅设置低位。这意味着当字符串都相对较短时,增加表大小是完全没有意义的;如果所有字符串都是 ASCII 且不超过 100 个字符,则最大可能的原始哈希值为 12700;如果您需要存储一百万个这样的字符串,那么在前 12,700 个存储桶中,每个存储桶平均会发生近 79 次碰撞(实际上,比普通存储桶的碰撞次数要多得多;在中间值中会有更多碰撞的驼峰,并且开始附近的碰撞更少,最后几乎没有,因为像'\x7f' * 100
这样的东西是达到所述最大值的唯一方法),无论你有多少桶,它们永远不会被使用。从技术上讲,基于开放寻址的哈希表可能会使用它们,但它在很大程度上等同于每个桶的单独链接,因为超过 12700 的所有索引只能通过开放寻址“反弹”算法找到;如果那设计不好,例如线性扫描,即使 no 条目实际上与您的特定哈希冲突(您的存储桶已通过链接填充,它必须线性扫描直到找到一个空槽或匹配元素)。
【讨论】:
【参考方案2】:哈希函数错误:
1.AC 和 BB 将给出相同的结果,对于大字符串,可能存在许多 ascii 值之和相同的排列。
2.即使不同长度的字符串也会给出相同的哈希结果。 'A'(A +空格)='a'。
3.即使是字符串中的重排字符也会给出相同的哈希值。
【讨论】:
【参考方案3】:这是一个糟糕的散列函数。一个大问题:重新排列任何或所有字符会返回完全相同的哈希值。
增加 TABLE_SIZE 对此没有任何调整。
【讨论】:
以上是关于为啥这个特定的函数是一个糟糕的散列函数?的主要内容,如果未能解决你的问题,请参考以下文章