【问题标题】:What hashing algorithms to consider for variable length data可变长度数据要考虑哪些散列算法
【发布时间】:2012-12-31 15:38:41
【问题描述】:

为了避免任何混淆,我正在根据我对哈希算法的研究重新构建我的问题

问题陈述 我有多个包含可变长度数据记录的文本文件。我需要查找输入中是否有重复记录。每个文本文件都可能有数百万的数据记录。

由于我无法一次将所有数据加载到内存中,因此我计划在处理记录时创建记录中关键字段的哈希。处理记录意味着验证、过滤和转换它。在处理完所有文本文件中的所有记录后,将它们合并以创建整个输入的一个视图(文本文件或数据库表)。

如果为所有记录生成哈希值,查找重复项会快得多。如果哈希值发生冲突,则只能检查那些记录是否相等(假设哈希算法是确定性的)

问题 - 对于此类输入(即可变长度数据),我应该考虑哪些哈希算法?

【问题讨论】:

  • 在 unix 中你会做 cat $files | sort | uniq -c 会给你跨多个文件的每一行的计数。您可以对其进行解析以获取重复项。
  • 我不认为完美的散列是解决方案。构建一个完美的哈希本身需要访问所有字符串,并且可能比检测重复项需要更多的工作。

标签: java algorithm hash text-files


【解决方案1】:

简答

不要这样做。使用 Java 映射。您可以在此处找到详细信息: http://docs.oracle.com/javase/6/docs/api/java/util/Map.html

长答案

您可以通过将字符串视为以 N 为底的数字来创建完美的散列函数,其中 N 是任何字符可以采用的所有可能值。这里的问题是内存。散列函数旨在与数组一起使用,这意味着您需要一个足够大的数组来处理散列结果,这是不切实际的。

例如,以 10 个字符的键为例。让我们更加谦虚,假设它们保证只包含小写字母。这为每个字符和 10 个字符提供了 26 种可能性。这意味着可能的组合是:

26 ^ 10 = 141,167,095,653,376

如果您查找散列算法,它们首先包含的内容之一就是碰撞检测,因为它们承认碰撞是生活中的事实。

现在你说你没有在内存中加载键,但你为什么要使用散列呢?哈希的目的是为您提供到数组索引的映射。也许你最好使用另一种机制。

可能的解决方案

如果您担心内存问题,请获取有关文件中重复项的一些统计信息。如果您只存储一个标志来指示哈希中特定键的出现,并且您有很多重复项,那么您可以只使用 Java 的映射。 Java 的映射处理冲突,因此不会阻止您检测唯一键。你可以放心,如果找到了 A[x],这意味着 x 在 A 中,即使 x 的哈希与之前的哈希冲突。

接下来,您可以尝试一些实用程序来提取重复项。由于它们是专门为此目的而编写的,因此它们应该能够处理大量数据。

最后,您可以尝试将您的条目放入数据库并使用它来处理重复项。这可能看起来有点矫枉过正,但数据库已针对处理大量记录进行了优化。

【讨论】:

  • 嗯..好点。您对验证记录在文本文件中是否唯一的不同方法有任何建议吗?
  • 是的,使用Java地图,内置:docs.oracle.com/javase/6/docs/api/java/util/Map.html
  • 使用更大的哈希值。 32 位散列很可能发生冲突,64 位散列几乎不可能,而 128 位散列几乎是不可能的。
  • 这意味着将所有记录加载到内存中。这些可能是数百万。很多!
  • @AndyDufresne:是的,但问题是如果您实现自己的散列,您仍然在同一条船上,您将需要一个数组。您可以尝试使用数据库并依靠它有效处理大量记录的能力...您还可以查看从文件中删除重复项的实用程序
【解决方案2】:

这是对地图理念的扩展。在诉诸于此之前,我会检查它不能通过简单地构建一个代表所有字符串的 HashSet 来完成。请记住,您可以使用 64 位 JVM 并设置较大的堆大小。

定义一个 StringLocation 类,其中包含对磁盘上的字符串进行随机访问所需的数据 - 例如,对 RandomAcessFile 的引用和文件内的偏移量。如果您无法同时打开所有文件,请根据需要打开和关闭,为最常用的文件缓存 RandomAcessFile。

创建一个HashMap<Integer,List<StringLocation>>

开始阅读字符串。对于每一个字符串,转换为小写,得到它的hashCode(),hash,整数形式。如果 Map 中有一个以哈希为键的条目,则将新字符串与现有值中表示的每个字符串进行比较,执行随机文件访问以获取已处理的字符串。使用字符串 equalsIgnoreCase。如果有匹配项,则您有重复项。如果不匹配,则将表示当前字符串的新 StringLocation 附加到 List。

这需要一次最多两个字符串在内存中,一个是您当前正在处理的字符串,另一个是之前处理的字符串,它具有与您比较的相同 hashCode() 结果。

您可以通过使用 MessageDigest 为小写字符串生成具有低冲突风险的宽校验和并将其保存在 StringLocation 中,从而进一步减少必须重新读取字符串以进行相等检查的次数目的。在比较过程中,如果校验和不匹配,则返回 false,无需重新读取字符串。

【讨论】:

  • 我已经根据我的研究更新了问题描述。如果您有任何建议,我们将不胜感激。谢谢。
  • 我已阅读您更新的问题。你会允许碰撞吗?如果没有,则需要研究“完美哈希”。我认为不可能一次只读取几个字符串来计算完美的哈希值。我的主要建议是使用 HashMap,并接受冲突。
  • 另外,我认为重写您的问题以专注于您要解决的问题是个好主意。您似乎已将您的想法缩小到需要对大量长字符串进行最小完美哈希的设计。那不是很实用。你真正的问题可能更容易解决。
  • 我重新设计了这个问题以避免混淆。回答您关于碰撞的问题 - 是的。散列算法可能有冲突。假设散列函数是确定性的,软件只能验证那些散列值冲突的数据记录。让我知道你的意见。谢谢!
  • 我看到了这种方法的以下问题 - 1. 根据 javadocs,hashCode() 从一次执行到另一次执行不一致。此外,如果多个文件通过多个 jvm 加载,然后在稍后阶段合并,这可能是一个问题。 2. 在内存中为大文件保存一个 hashmap 可能不会节省内存,因为 jvm 还会执行其他操作。
猜你喜欢
  • 2015-08-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-06-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多