超大字符串子集的比较
Posted
技术标签:
【中文标题】超大字符串子集的比较【英文标题】:Comparison of extra-large subsets of strings 【发布时间】:2014-05-13 12:00:33 【问题描述】:每天有一个文件,从 2000000 到 4000000 个字符串,其中逐行包含唯一的 15 个符号数字,如下所示:
850025000010145
401115000010152
400025000010166
770025555010152
512498004158752
从今年年初开始,您相应地拥有一定数量的此类文件。因此,我必须将今天文件的每一行与年初以来的所有文件进行比较,并仅返回所有检查文件中从未遇到过的数字。
我应该使用哪种语言和算法?如何实现?
【问题讨论】:
非常重要的是:您关心什么?你只想“让它工作”吗?或者你想“快速”地完成它(如果是的话 - 那么多快)。内存使用量重要吗? 这是操作任务。我有 16Gb 允许的内存。数据库管理员说它不适用于我们的数据库,他们问我“编写脚本”:) 运行时并不重要(当然如果不超过半天) 1) 文件中是否只包含个数字,还是在识别数字后有额外的内容? 2) 你唯一的目标是获得一组独特的这些数字吗? 有只有一行一行的数字,没有多余的内容 是的,我每天都需要获得一组以前从未见过的唯一数字 【参考方案1】:您应该能够做到这一点,而无需编写简单脚本以外的任何代码(即 bash、Windows 批处理、Powershell 等)。有一些标准工具可以快速处理这类事情。
首先,您有一些包含 200 万到 400 万个数字的文件。处理所有这些文件很困难,因此您要做的第一件事是创建一个已排序的组合文件。简单的方法是将所有文件连接到一个文件中,对其进行排序并删除重复项。例如,使用 GNU/Linux cat
和 sort
命令:
cat file1 file2 file3 file4 > combined
sort -u combined > combined_sort
(-u
删除重复项)
这种方法的问题是您最终会排序一个非常大的文件。图 400 万行,每行 15 个字符,外加换行符,以及近 100 天的文件,您正在使用 7 GB。一整年的数据价值将是 25 GB。这需要很长时间。
因此,请对每个单独的文件进行排序,然后将它们合并:
sort -u file1 >file1_sort
sort -u file2 >file2_sort
...
sort -m -u file1 file2 file3 > combined_sorted
-m
开关合并已排序的文件。
现在您所拥有的是一个排序列表,其中包含您迄今为止看到的所有标识符。您想将今天的文件与该文件进行比较。首先,对今天的文件进行排序:
sort -u today >today_sort
现在,您可以比较文件并仅输出今天文件独有的文件:
comm -2 -3 today_sort combined_sort
-2
表示禁止仅出现在第二个文件中的行,-3
表示禁止两个文件共有的行。因此,您将得到的只是 today_sort
中不存在于 combined_sort
中的行。
现在,如果您打算每天都这样做,那么您需要获取 comm
命令的输出并将其与 combined_sort
合并,以便您明天可以使用该组合文件。这使您不必每天都重建combined_sort
文件。所以:
comm -2 -3 today_sort combined_sort > new_values
然后:
sort -m combined_sort new_values > combined_sort_new
你可能想用日期命名文件,所以你会有combined_sort_20140401
和combined_sort_20140402
等。
因此,如果您从年初开始并希望每天都这样做,您的脚本将如下所示:
sort -u $todays_file > todays_sorted_file
comm -2 -3 todays_sorted_file $old_combined_sort > todays_uniques
sort -m $old_combined_sort todays_sorted_file > $new_combined_sort
$todays_file
、$old_combined_sort
和 $new_combined_sort
是您在命令行上传递的参数。所以,如果脚本被称为“每日”:
daily todays_file.txt all_values_20140101 all_values_20140102
【讨论】:
这种方式可以和inotify结合,让整个过程自动化。【参考方案2】:一种解决方案可能是基于之前的n-1
文件构建prefix tree(假设今天创建了n
-th 文件)。最耗时的构建过程只需执行一次。构建前缀树后,您可以将其保存为文件(本主题为google)。
运行程序检查新文件:
try(BufferedReader br = new BufferedReader(new FileReader("new_file.txt")))
String line = br.readLine();
while (line != null)
if(!tree.contains(line))
counter++;
else
tree.insert(line);
line = br.readLine();
因此,您每天都运行此“伪”代码,获取唯一查询并更新树。
contains
需要 O(m) 时间,其中 m 是字符数
insert
也需要 O(m) 时间
我建议使用 Java。
【讨论】:
为什么要使用前缀树而不是某种类型的哈希表? 前缀树使用的内存比哈希表少。 前缀树可以使用更少的内存。这在很大程度上取决于您放入其中的字符串的分布。当您考虑到在这种情况下,每个节点很可能需要 10 个指向子节点的指针时,内存节省(如果有的话)将比您构建英文单词前缀树所获得的要小得多。此外,由于这些是 15 位数字,因此可以将它们转换为 64 位整数,每个整数只需要 8 个字节。你不会得到 8 个字节的前缀树节点。【参考方案3】:如果你必须手动解决问题: - 将字符串转换为 64 位整数。这样可以节省空间(2 倍到 4 倍)并加快计算速度。 - 对当前整数文件进行排序 - 将当前文件与旧数据文件(已排序)合并,选择新数字
合并步骤可能看起来像 MergeSort 的合并步骤。 您可以将数字范围存储在单独的文件中,以避免文件过大。
附:我想建议使用位图,但它的大小约为 125 TB
【讨论】:
感谢您的回答,哪种语言最适合这项任务? 任何本地语言 - C、C++、Delphi 和快速 VM/CLR 语言,如 Java、C#。我预计 Python、Ruby、Basic、php 运行速度较慢,但限制因素 - 文件操作,因此这些语言可能也适合。你熟悉什么语言?以上是关于超大字符串子集的比较的主要内容,如果未能解决你的问题,请参考以下文章