超大字符串子集的比较

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 catsort 命令:

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_20140401combined_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 运行速度较慢,但​​限制因素 - 文件操作,因此这些语言可能也适合。你熟悉什么语言?

以上是关于超大字符串子集的比较的主要内容,如果未能解决你的问题,请参考以下文章

如何仅针对键的子集有效地比较 C++ 中的两个字符串映射

XACML 政策。应用函数字符串子集给出意外结果

String高效编程2,3事(Java)

超大字符串型整数相加的简单实现

mysql+mybatis存储超大json

C++实现两个超大的字符数字相加的算法的代码