我知道根据鸽巢原理,如果物品的数量大于容器的数量,那么至少会有一个容器包含多个物品。这个容器是哪个并不重要吗?这如何应用于MD5、SHA1、SHA2哈希?
我认为你使用哈希函数的应用是一个重要的区别。例如,在哈希容器中频繁发生冲突可能会降低性能。在密码学中,频繁碰撞将产生更为严重的后果(参见:Wikipedia上的密码哈希函数)。
即使使用“不错”的哈希算法,碰撞也相对容易发生。例如,在Java中,
String s = new String(new char[size]);
始终哈希为0。也就是说,在Java中,所有仅包含 \0
的字符串都会哈希为0。
至于“容器是什么并不重要吗?”,这取决于应用程序。您可以设计哈希函数,将“相似”的对象哈希到附近的值。例如,当您想搜索相似的对象时,只需将它们全部哈希并查看它们所在的位置即可。在这种情况下,碰撞或接近碰撞是可取的,因为它将相似的对象分组。
在其他应用中,您希望即使对象发生最微小的变化,也会导致完全不同的哈希值。例如,在加密中,您希望尽可能确定某些内容未被修改。在这种情况下,更难找到哈希到相同值的不同对象。
根据您的应用程序,像MDA、SHA1/2等加密哈希可能不是理想的选择,因为它们看起来完全随机,因此会出现由生日悖论预测的冲突。传统上,使用基于余数运算的简单哈希的一个原因是,期望键是序列号或类似的东西,因此余数运算将比随机预期的冲突少。例如,如果键是1..1000的整数,则如果您的哈希函数是key mod 1009,则在大小为1009的容器中可能根本没有冲突。人们有时会通过精心选择容器大小和哈希函数来手动调整系统,以实现均匀分割。
当然,如果您必须担心人们恶意选择会给您带来困难的键,或者上游系统发送给您非常偏向某些键(例如,它有自己的哈希表并决定一次处理所有哈希到X的键),则您可能希望使用基于密钥的加密哈希函数来防御这种情况。