【问题标题】:A way to store keys in a dictionary without storing the key?一种将密钥存储在字典中而不存储密钥的方法?
【发布时间】:2015-05-04 15:09:01
【问题描述】:

例如,如果编写一个字典类,冲突是很少见的,但它们确实存在。因此,您需要存储密钥以确保当您在哈希表中找到您的密钥时,它是正确的,而不是冲突。

有时键很长并且通常是字符串,因此每个键可以超过 40 个字节,而如果它只是一个哈希码。如果存储的密钥是​​散列的对象,但使用稍微不同的散列算法,具有不同的素数怎么办?那么发生碰撞的可能性是(1/(2^32)) * (1/(2^32))

您甚至可以使用另一种散列算法并存储该散列,因此发生冲突的可能性为(1/(2^32)) * (1/(2^32)) * (1/(2^32))。显然,仍然可能发生冲突,但可能性很小,而且您只需为密钥存储 4 个字节而不是超过 32 个字节,从而节省了大量内存。

我想这仍然是不可接受的,对,因为仍有机会,但也有可能某人的 RAM 可能会意外翻转一点并蓝屏,这似乎不太可能不实施。有没有其他选择,还是小机会仍然不值得?

【问题讨论】:

  • dictionary 中的关键点是您有一个已知的唯一索引器,并且您希望找到存储在该索引处的值。如果你有一些只能从对象本身获取的现成哈希,你可能不需要字典,只需要一个列表。
  • 如果你的哈希是 32 位,那么冲突的概率会比 1/2^32 高很多。每个这样的散列函数需要 4 个字节,因此其中 3 个需要 12 个字节。这么小的散列不太可能发生,所以如果你必须绝对避免冲突,我建议你留出 40 个字节。
  • 你还假设了一个完美的散列函数,每个值的概率都具有完美的分布,这在现实中基本上是不可能的。
  • 即使您存储了 6 个散列,每个散列使用不同的素数计算,它仍然比字符串键散列使用更少的内存,并且冲突的概率接近 10^50 分之一。我知道还有机会,而且散列并不完美,但看起来很诱人..
  • 不,它不会是 10^50 中的 1。你从哪里得到这些数字?这比实际使用的可能性要大得多,具体取决于用于哈希的实际算法。

标签: c# algorithm dictionary hash hashcode


【解决方案1】:

如果您想 100% 确定没有任何冲突,则无法在插入之前检查密钥。话虽如此,我们在这里很幸运,因为您需要一个实施良好的字典才能快速找到密钥。

话虽如此,您可能想看看here 描述的函数。碰撞几率会很低

编辑:删除了我写的关于 GUID 的一些废话......

【讨论】:

  • 同意:GUID 比手动组合谁知道哪种哈希函数要好得多。
  • 你怎么知道指向 GUID 的键呢?没有关联。
  • 你说得对,我的这部分回答很愚蠢,无法修复。我认为您的密钥 事实上 已存储并且易于检查。让我编辑一下……
【解决方案2】:

视情况而定。

绝对需要保证冲突解决吗?如果是这样:您必须存储密钥或与之等效的东西。在某些情况下(例如小键空间、冗余数据等),您可以使用压缩或自定义散列函数来可逆地将键映射到更小的东西。

如果不是:是的,你的方法会奏效。注意,由于birthday paradox,碰撞的概率为:

  • 取决于集合中已有元素的数量;和
  • 比你想象的要高。

有一个权衡:现在您必须计算(并比较)几个哈希值才能找到项目。

沿着这条路走得更远:为什么有固定数量的哈希?您可以计算一个哈希值,只有在发生冲突时才计算下一个哈希值;这导致基于 trie 的实现。 (当然,您需要一个可靠分布的 系列 散列函数...)

这对于除高性能和/或内存受限的应用程序之外的所有应用程序来说都是多余的 - 但偶尔这样做很有用:)

【讨论】:

  • 尝试很有趣。我可能有一个基于此的想法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-05
相关资源
最近更新 更多