我希望优化目前相当简单的算法 为O(n 2 )。我有一个记录文件,每个人都需要 在同一个文件中相互比较。如果两个是 '相同'(比较器功能相当复杂),匹配 记录输出。请注意,可能有几个匹配的记录 彼此, 并且没有秩序感 - 只有当匹配为真或假时。
伪代码:
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(n 2 )?我正在考虑应用MapReduce范例 在代码上,分解外部和内部循环,可能使用a 链式地图功能。我很确定我已经找到了代码 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
补充:这些文件非常大。最大的文件是 预计将有大约2亿条记录。
答案 0 :(得分:4)
我不确定比较器和数据集的属性,但假设你的比较器在你的行上定义了等价关系,这里什么都没有:
请注意,在最坏的情况下,根据您的问题描述,您不能比O(n ^ 2)更好,只是因为可能存在O(n ^ 2)匹配记录的结果,您必须输出!
答案 1 :(得分:2)
假设文件不是很大,我会完整地浏览文件,计算行的哈希值,并跟踪哈希/行#(或文件指针位置)组合。然后对哈希列表进行排序,并识别出现多次的哈希值。
答案 2 :(得分:2)
我们需要了解您的比较功能。你的比较是否具有传递性? (也就是说,A == B和B == C是否意味着A == C?)它是反身的吗? (A == B是否意味着B == A?)
如果您的比较函数具有传递性和反身性,并且许多记录相同,那么您可以将记录与组中的一个“代表性样本”进行比较,从而将记录分组。在最好的情况下,这可能接近O(N)。
请注意,散列记录会假设散列(A)==散列(B)< => compare(A,B)== true,但是如果比较(A,B)即使字节(A)!=字节(B)也可以为真,那么设计合适的散列算法可能会很棘手。
答案 3 :(得分:2)
FYI MapReduce将不改善解决方案的算法复杂性。它增加了一些开销,但随后将其并行化,以便您可以在更少的挂钟时间内使用必要的资源。
为了改善您的挂钟时间,首先要做的是找到避免运行比较的方法。任何这样做都将是一场胜利。即使您的比较逻辑很复杂,您仍然可以使用排序来提供帮助。
例如,假设您有一些数据分散的维度。在该维度中变化太大的数据保证不会比较相等,尽管在该维度中接近并不保证相等。然后,您可以做的是按该维度对数据进行排序,然后仅在该维度中接近的元素之间进行比较。瞧!大多数O(n*n)
比较现已消失。
让它变得更复杂。假设您可以识别两个彼此独立的维度。按照第一个这样的维度对数据进行排序。将第一维中的数据划分为条带。 (使条带重叠的最大值可以在该维度上变化,并且仍然比较相等。)现在取出每个条带并按第二个维度对其进行排序。然后在该维度中可接受地接近的元素对之间进行比较,并且如果它比较相等则在答案中包括该对,并且这是它可能出现的第一个条带。(需要该重复数据删除逻辑,因为重叠可能意味着比较相等的对可以出现在多个条带中。)这可能比第一种方法更好,因为你已经设法缩小了范围,因此你只是将行与少量的“附近”行进行比较。 / p>
如果您想减少使用资源,则需要关注避免实际进行个别比较的方法。你在这条道路上提出的任何建议都会有所帮助。
答案 4 :(得分:1)
只需浏览文件的每条记录并将其插入哈希表即可。在每个步骤中,检查记录是否已经在哈希表中。如果是,则输出它。这可以在O(n)中完成。
答案 5 :(得分:1)
正如你已经提到过的,你将不会有幸比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);
}
}
使用source.txt中的输入:
thomas
phil
jen
james
christian
stefan
stephan
john
和target.txt
john meat
jay hardly
将导致减速器输出:
john meat john
诀窍在于您可以拆分source.txt并并行比较。这会给你加速,但在大O中不会让你更好。
这里有一个重要的注意事项: 您必须使用计数器报告进度,因为与整个文件的比较可能需要永久。这将防止您的任务在分布式环境中失败。
小提示:
尝试将source.txt拆分为64m块,并将target.txt设为sequencefile
。这将获得大量的加速,然后你必须重写阅读内容。
祝你好运!
答案 6 :(得分:0)
您尚未提及预期匹配的输入百分比,或者您与不精确匹配的匹配频率。如果你可以做一些预处理来减少问题的大小,那可能会有很大的帮助。
如果您只是对输入进行排序并在相邻条目上运行比较函数,则可能会剔除足够多的重复项以使n ^ 2秒通过可以忍受。
答案 7 :(得分:0)
如果真的不能做到比不透明的等价关系更好,那么你最坏的情况将永远是O(n ^ 2) - 例如,对于没有匹配的情况,你需要比较每一对,以确保这一点。 (正如人们所提到的,你可以将其平行化,但对于数以亿计的记录而言,这仍然不会特别容易处理;可能不值得这样做所需的资源成本。)
通常,有一些更好的方法可以做到。
如果你确实有一个等价关系(也就是说,如果你有一些逻辑保证,如果匹配(a,b)=匹配(b,c)=真,那么匹配(a,c)也是如此) ,可能有一些规范形式,您可以将您的记录转换为,可以进行散列和/或排序。
在您的示例中,您似乎匹配“Joe Smith”的变体。如果是这种情况,您可以扩充比较标准,以选择等价类的一个特定成员来表示整体。例如,选择“JOSEPH”代表相当于“Joe”,“SMITH”的所有名称,代表相当于“Smythe”的所有名称等。
进行转换后,您可以使用哈希表将操作减少到O(n),而不是O(n ^ 2)。
答案 8 :(得分:0)
感谢所有好的建议 在完成选项之后,考虑到我的时间线,似乎最好的方法是使用MapReduce框架来并行化问题并为其投入更多硬件。我意识到这并没有降低O(n 2 )的复杂性 我能想到的唯一可能的解决方案是在数据上运行某种类型的minHash,将数据条带化为重叠的部分,并在条带和重叠内进行比较。这应该减少比较次数,但我不确定散列运行的成本是多少。
答案 9 :(得分:0)
正如btilly所说,你实际上并不需要传递性来对记录进行分类。对于英文人名,您可以用两个首字母表示每个名字,每个记录用一个排序的首字母列表表示。然后,您只需要在同一个类中的记录之间运行完整的O(N ^ 2)比较。还有一个问题是同一对记录可以出现在多个类中,但通过维护一个单独的匹配记录对集合(由记录索引标识)很容易检测到。
在示例中,您将记录1放在“DF,JS”类中,记录2放在“DL,NJ”类中,记录3放在“JC,NJ”类中,记录4放在“DJ,JS”类中,“JF,JS”和“DF,JS”,以及“DF,JM”,“DF,JS”和“DF,MS”类中的记录5。你总共有7个班级:“DF,JM”,“DF,MS”,“DF,JS”,“DJ,JS”,“DL,NJ”,“JC,NJ”,“JF,JS”,其中只有“DF,JS”类包含多条记录,即记录1,4和5.因此在本例中,您只需要运行两次完整的比较功能。
另一方面,存在人们有奇怪名字的问题。 This blog entry on the subject is worth a look if you haven't seen it before.无论你做什么,你都会错过一些比赛。