Algorithm 优化O(n^2)算法所需的建议

Algorithm 优化O(n^2)算法所需的建议,algorithm,optimization,hadoop,Algorithm,Optimization,Hadoop,我期待着优化一个相当简单的算法,目前正在进行 O(n2)。我有一个档案,每个档案都需要 可以与同一文件中的其他文件进行比较。如果这两个是相同的 “相同”(比较器函数相当复杂),匹配 记录被输出。请注意,可能有多条记录匹配 彼此之间,没有秩序感——只有在匹配是真是假的情况下 伪代码: For (outRec in sourceFile) { Get new filePointer for targetFile //starting from the top of the file for i

我期待着优化一个相当简单的算法,目前正在进行 O(n2)。我有一个档案,每个档案都需要 可以与同一文件中的其他文件进行比较。如果这两个是相同的 “相同”(比较器函数相当复杂),匹配 记录被输出。请注意,可能有多条记录匹配 彼此之间,没有秩序感——只有在匹配是真是假的情况下

伪代码:


For (outRec in sourceFile) {
  Get new filePointer for targetFile //starting from the top of the file for inner loop
  For (inRec in targetFile) {
    if (compare(outRec, inRec) == TRUE ) {
      write outRec
      write inRec
    }
    increment some counters
  }
  increment some other counters
}
数据没有以任何方式排序,也没有预处理 可以对数据进行排序

有没有关于这件事怎么会变得不那么重要的想法 O(n2)?我正在考虑应用MapReduce范例 在代码上,分解外部和内部循环,可能使用 链式映射函数。我很确定我已经把密码弄明白了 Hadoop,但我想在花时间编写代码之前检查替代方案 它

感谢您的建议

添加:记录类型。基本上,我需要匹配名称/字符串。这个 下面的示例中显示了匹配的类型


1,Joe Smith,Daniel Foster
2,Nate Johnson,Drew Logan
3,Nate Johnson, Jack Crank
4,Joey Smyth,Daniel Jack Foster
5,Joe Morgan Smith,Daniel Foster

Expected output: Records 1,4,5 form a match set End of output

1、乔·史密斯、丹尼尔·福斯特
内特·约翰逊,德鲁·洛根
3,内特·约翰逊,杰克·克兰克
乔伊·史密斯,丹尼尔·杰克·福斯特
5、乔·摩根·史密斯、丹尼尔·福斯特

预期产出: 记录1、4、5构成一个匹配集 输出结束
补充:这些文件将相当大。最大的文件是
预计将有大约2亿条记录。

假设文件的大小不会太大,我会遍历整个文件,计算行的散列,并跟踪散列/行(或文件指针位置)的组合。然后对哈希列表进行排序,并识别那些多次出现的哈希

只需浏览文件的每条记录并将它们插入到哈希表中即可。在每个步骤中,检查记录是否已经在哈希表中。如果是,则输出它。这可以在O(n)中完成。

我们需要了解有关比较函数的更多信息。你的比较是传递性的吗?(也就是说,A==B和B==C是否意味着A==C?)?(A==B是否意味着B==A?)

如果您的比较函数是传递的和自反的,并且许多记录相等是常见的,那么您可以通过将记录与组中的一个“代表性样本”进行比较,将其分组。在最好的情况下,这可能接近O(N)


请注意,对记录进行哈希运算时,假定哈希(A)=哈希(B)比较(A,B)=真,但如果比较(A,B),则即使在字节(A)!=字节(B)设计适当的散列算法可能很棘手。

我不确定比较器和数据集的属性,但假设比较器在行上定义了等价关系,这里什么也没有:

  • 为输入文件创建一个映射,并使用comparator函数作为映射的关键比较器。映射值是行的序列/列表,即“相同”的所有行依次添加到同一映射条目)。需要O(n*logn)个时间
  • 浏览另一个文件的行,检查每一行是否与映射中的键匹配。在这种情况下,由于比较器隐含的等价关系,您知道此行与该映射条目值中的所有行“相同”。取O(n*logn+C),具体取决于必须输出的匹配数

  • 请注意,在最坏的情况下,根据您的问题描述,您无法获得比O(n^2)更好的结果,这仅仅是因为您必须输出的匹配记录可能有O(n^2)个结果

    FYI MapReduce将而不是改善解决方案的算法复杂性。它增加了一些开销,但随后将其并行化,以便您可以在更少的挂钟时间内使用必要的资源

    为了提高你的挂钟时间,首先要做的是找到避免进行比较的方法。任何这样做的方式都将是一场胜利。即使您的比较逻辑很复杂,您仍然可以使用排序来提供帮助

    例如,假设您有一些维度,这些数据分布在其中。在该维度中差异过大的数据保证不会比较相等,尽管在该维度中接近并不能保证相等。然后,您可以按该维度对数据进行排序,然后只在该维度中相近的元素之间运行比较。瞧!大多数
    O(n*n)
    比较现在都不存在了

    让我们把它弄得更复杂些。假设您可以识别两个相互独立的维度。按照第一个维度对数据进行排序。将第一个维度中的数据划分为条带。(使条带重叠的最大值在该尺寸上有所不同,并且比较时仍然相等。)现在取下每个条带,并按第二个尺寸对其进行排序。然后在该维度中可以接受的相近元素对之间进行比较,如果该元素对比较相等,则将其包含在答案中,这是该元素对可能出现的第一个条带。(这种重复数据消除逻辑是必需的,因为重叠可能意味着比较相等的一对可以出现在多个条带中。)这可能比第一种方法更好,因为您已经设法缩小了范围,以便只将行与少量“附近”行进行比较


    如果你想使用更少的资源,你需要专注于避免实际进行个别比较的方法。你在这条道路上的任何想法都会有所帮助。

    正如你已经提到的,你不会有比O(n^2)更好的运气,但你可以将其平行化

    我有一个可以使用HDFS的工作解决方案,您可以使用分布式缓存来扩展它

    public class MatchImporter extends Mapper<LongWritable, Text, Text, Text> {
    
    FileSystem fs;
    private BufferedReader stream;
    
    @Override
    protected void setup(Context context) throws IOException,
            InterruptedException {
        fs = FileSystem.get(context.getConfiguration());
    }
    
    private void resetFile() throws IOException {
        if (stream != null)
            stream.close();
        stream = new BufferedReader(new InputStreamReader(fs.open(new Path(
                "files/imp/in/target.txt"))));
    }
    
    private boolean compare(Text in, String target) {
        return target.contains(in.toString());
    }
    
    enum Counter {
        PROGRESS
    }
    
    @Override
    protected void map(LongWritable key, Text value, Context context)
            throws IOException, InterruptedException {
    
        resetFile();
        String line = null;
        while ((line = stream.readLine()) != null) {
            // increment a counter to don't let the task die
            context.getCounter(Counter.PROGRESS).increment(1);
            context.progress();
            if (compare(value, line)) {
                context.write(new Text(line), value);
            }
        }
    }
    
    public static void main(String[] args) throws Exception {
        Configuration conf = new Configuration();
        Job job = new Job(conf);
    
        job.setMapperClass(MatchImporter.class);
        job.setReducerClass(Reducer.class);
        job.setJarByClass(MatchImporter.class);
    
        Path in = new Path("files/imp/in/source.txt");
        Path out = new Path("files/imp/out/");
    
        FileInputFormat.addInputPath(job, in);
        FileSystem fs = FileSystem.get(conf);
        if (fs.exists(out))
            fs.delete(out, true);
    
        SequenceFileOutputFormat.setOutputPath(job, out);
        job.setInputFormatClass(TextInputFormat.class);
        job.setOutputFormatClass(TextOutputFormat.class);
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(Text.class);
    
        job.waitForCompletion(true);
    }
    
    }
    
    和target.txt

    john meat
    jay hardly
    
    将导致减速器输出:

    john meat   john
    
    诀窍在于,您可以拆分source.txt并并行地进行比较。这将给你加速,但不会让你更好地在大O

    这里有一个重要提示: 您必须使用cou报告进度
    john meat   john