9

我有一个Dictionary<string,int>可能包含超过 10+ 百万个唯一键的潜力。我试图减少这需要的内存量,同时仍然保持字典的功能。

我有将字符串的哈希存储为 long 的想法,这会将应用程序的内存使用量降低到可接受的量(~1.5 gig 到 ~.5 gig),但我对我的方法感觉不太好这。

long longKey=
BitConverter.ToInt64(cryptoTransformSHA1.ComputeHash(enc.GetBytes(strKey)), 0);

基本上,这会切断 SHA1 哈希的末尾,并将其第一块放入一个 long 中,然后我将其用作密钥。虽然这可行,但至少对于我正在测试的数据而言,我不觉得这是一个非常可靠的解决方案,因为键冲突的可能性增加了。

有没有其他方法可以减少字典的内存占用,或者我上面的方法没有我想象的那么可怕?

[编辑]为了澄清,我需要保持使用字符串查找字典中包含的值的能力。将实际字符串存储在字典中会占用大量内存。我想做的是使用Dictionary<long,int>long 是字符串上散列函数的结果。

4

6 回答 6

11

所以我最近做了一些类似的事情,出于某些对我的应用程序来说相当独特的原因,我没有使用数据库。事实上,我试图停止使用数据库。我发现 GetHashCode 在 3.5 中得到了显着改进。一个重要的注意事项,永远不要持久存储来自 GetHashCode 的结果。永远不能。不保证它们在框架版本之间保持一致。

因此,您确实需要对您的数据进行分析,因为不同的哈希函数可能对您的数据效果更好或更差。您还需要考虑速度。作为一般规则,即使散列的数量达到数十亿,加密散列函数也不应该有很多冲突。对于我需要独特的东西,我通常使用 SHA1 Managed。一般来说,CryptoAPI 的性能很差,即使底层的哈希函数性能很好。

对于 64 位散列,我目前一起使用 Lookup3 和 FNV1,它们都是 32 位散列。要发生碰撞,两者都需要发生碰撞,这在数学上是不可能的,而且我还没有看到超过 1 亿个哈希值发生过。您可以在网络上找到公开可用的代码。

仍然进行自己的分析。对我有用的东西可能对你不起作用。实际上,在我的办公室内部,具有不同要求的不同应用程序实际上使用不同的散列函数或散列函数的组合。

我会避免任何未经证实的哈希函数。散列函数的数量与认为应该编写它们的人一样多。做你的研究和测试测试。

于 2008-12-18T22:20:26.580 回答
7

有 1000 万多条记录,您是否考虑过使用具有非聚集索引的数据库?对于这类事情,数据库有更多的技巧。

根据定义,在任何算法下,散列都有可能发生冲突——尤其是在大容量的情况下。根据情况,我会对此非常谨慎。

使用字符串可能会占用空间,但它是可靠的......如果你在 x64 上,这不需要太大(尽管它绝对算作“大”;-p)

于 2008-12-18T21:21:31.223 回答
5

顺便说一句,加密散列/散列函数对字典非常不利。它们又大又慢。通过解决一个问题(大小),您只引入了另一个更严重的问题:该函数将不再均匀分布输入,从而破坏了用于接近无冲突寻址的良好哈希的一个最重要的属性(如你似乎已经注意到了自己)。

/编辑:正如安德鲁所指出的,这GetHashCode是解决此问题的方法,因为这是它的预期用途就像在真正的字典中一样,您将不得不解决碰撞问题。最好的方案之一是双散列。不幸的是,唯一 100% 可靠的方法是实际存储原始值。否则,您将创建无限压缩,我们知道这是不存在的。

于 2008-12-18T20:44:18.833 回答
3

为什么不直接使用GetHashCode()来获取字符串的哈希值?

于 2008-12-18T20:41:36.093 回答
2

使用我过去使用过的哈希表实现,哈希会将您带到一个存储桶,该存储桶通常是具有相同哈希的其他对象的链接列表。哈希不是唯一的,但它们足以将您的数据拆分为非常易于管理的列表(有时只有 2 或 3 长),然后您可以搜索这些列表以找到您的实际项目。

一个好的散列的关键不是它的唯一性,而是它的速度和分布能力……你希望它尽可能均匀地分布。

于 2008-12-18T20:44:07.303 回答
2

去获取 SQLite。您不太可能击败它,即使您做到了,也可能不值得花时间/精力/复杂性。

SQLite。

于 2008-12-20T02:16:12.153 回答