【问题标题】:Fastest way to check duplicate columns in a line in perl在perl中检查一行中重复列的最快方法
【发布时间】:2020-12-12 05:02:31
【问题描述】:

我有一个这样的文件有 100 万行

aaa,111
bbb,222
...
...
a3z,222 (line# 500,000)
...
...
bz1,444 (last line# 1 million)

我需要检查的是逗号后的第二个值是否唯一。如果没有,则打印出行号。在上面的例子中它应该打印出来

Duplicate: line: 500000 value: a3z,222

为此,我使用 perl 并将第二列的值存储在数组中。如果我在数组中找不到值,我将其添加到其中。如果该值已经存在,那么我将其作为副本打印出来。

问题是我使用的逻辑非常慢。完成需要2-3个小时。有没有办法可以加快速度?如果不需要,我不想创建数组。我只想检查文件第 2 列中的重复值。

如果有更快的方法在批处理文件中执行此操作,我愿意接受。

这是我的工作代码。

# header
use warnings;
use DateTime;
use strict;
use POSIX qw(strftime);
use File::Find;
use File::Slurp;
use File::Spec;
use List::MoreUtils qw(uniq);

print "Perl Starting ... \n\n"; 

# Open the file for read access:
open my $filehandle, '<', 'my_huge_input_file.txt';

my $counter = 0;
my @uniqueArray;
# Loop through each line:
while (defined(my $recordLine = <$filehandle>))
{

  # Keep track of line numbers
  $counter++;


  # Strip the linebreak character at the end.
  chomp $recordLine;

  my @fields = split(/,/, $recordLine);
  my $val1=$fields[0]; 
  my $val2=$fields[1]; 

  if ( !($val2 ~~ @uniqueArray) && ($val2 ne "") )
  {
        push(@uniqueArray, $val2);
  }
  else
  {
    print ("DUP line: $counter - val1: $val1 - val2: $val2 \n");
  }

}

print "\nPerl End ... \n\n"; 

【问题讨论】:

  • If not then print out the line number. 的第二行和具有该值的任何后续行?还是第一个?还是第一个?

标签: arrays perl


【解决方案1】:

这是哈希的用途之一

use feature qw(say);

...

my %second_field_value;

while (defined(my $recordLine = <$filehandle>))
{
    chomp $recordLine; 
    my @fields = split /,/, $recordLine;

    if (exists $second_field_value{$fields[1]}) {
        say "DUP line: $. -- @fields[0,1]";
    }
    ++$second_field_value{$fields[1]};
}

这将累积该字段的所有可能值,这是必须的。我们还可以根据需要报告的关于他们的内容来存储有关被发现的受骗者的适当信息。

行号(最后读取的文件句柄)在$. variable 中可用;不需要$counter

请注意,检查和标志/计数器设置可以在一个表达式中完成,对于

if ($second_field_values{$fields[1]}++) { say ... }  # already seen before

这是检查重复项时的惯用语。感谢 ikegami 提出来。这通过在条件中包含 post-increment 来工作(因此检查是使用旧值完成的,并且块中的计数是最新的)。

我还必须评论智能匹配运算符 (~~)。人们普遍认为,它目前的形式存在很大的问题,而且几乎可以肯定它将遭受重大变化,甚至更糟。因此,简单地说,我会说:不要使用它。带有它的代码很有可能在未来某个未指定的时间点中断,可能没有警告,也可能是悄悄地。


关于在 cmets 中提出的性能和“计算复杂性”的说明。

在每一行上搜索数组具有 O(nm) 复杂度(n 行,m 值),什么是真正的 O(n2) 因为数组在每一行上都有一个新值(所以 m = n-1 );此外,整个数组被搜索(实际上)每一行,因为通常没有欺骗。使用散列,复杂度为 O(n),因为我们在每一行都有一个恒定时间查找。

关键在于输入的大小。对于几百行的文件,我们无法区分。对于 100 万行,报告的运行时间是“2-3 小时”(使用数组)和“5 秒以下”(使用哈希)。

“复杂性”评估涉及输入大小这一事实具有实际意义。

一方面,使用粗心构建的“运行良好”的算法的代码可能会因意外的大输入而惨遭破坏 - 或者更确切地说,一旦涉及生产运行,对于实际数据。

另一方面,当我们了解它的用例时,使用更干净和更简单的代码运行通常是相当令人满意的,即使它具有更差的“复杂性”。

一般来说,复杂性告诉我们运行时如何取决于大小,而不是它到底是什么。所以 O(n2) 算法可能比 O(n em> logn) 一个用于足够小的输入。这具有很大的实际意义,被广泛用于选择算法。

【讨论】:

  • 不可能!您的代码在 5 秒内处理了整个文件。太棒了,谢谢!
  • @SamB 好吧,很好——这是对“计算复杂性”概念价值的认可:)。在每一行上搜索一个数组......它是O(nm)n 行,m 数组值)实际上是O(n^2),因为m-array 在每一行都有一个不同的值,所以 @ 987654334@是n;哈希是O(n)。关键是 - 它开始与 n 足够大,你已经得到了一百万行...
  • @SamB ...所以在第 10 行,搜索的是一个 9 长的数组,在第 11 行,它是一个 10 长的数组...但在第 1_000 行,该数组有那么多元素...并计算它在 100_000 及以上的情况。另一个令人讨厌的地方是它每次都搜索 whole 数组,因为那里没有很多骗子(大概)。如果你准备好了,你可以打印出计时信息(每 1_000 或 10_000 行),看看它是如何发展的
  • 检查和标志设置可以结合使用:if (!$second_field_value{$fields[1]}++)这是查找重复的常用成语
【解决方案2】:

使用哈希。数组适合存储顺序数据,散列适合存储随机访问数据。您对@uniqueArray 的搜索在每次搜索时为 O(n),每行执行一次,使您的算法为 O(n^2)。哈希解决方案在每次搜索时为 O(1)(或多或少),每行执行一次,因此总体为 O(n)。

另外,使用 $. 作为行号 - perl 会为您跟踪它。

my %seen;
while(<$filehandle>)
{
  chomp;
  my ($val1, $val2) = split /,/;

  # track all values and their line numbers.
  push @{$seen{$val2}}, [$., $val1];
}

# now go through the full list, looking for anything that was seen
# more than once.
for my $val2 (grep { @{$seen{$_}} > 1 } keys %seen)
{
  print "DUP line: $val2 was seen on lines ", join ", ", map { "$_[0] ($_[1]) " } @{$seen{$val2}};
  print "\n";
}

这都是 O(n)。更快。

【讨论】:

    【解决方案3】:

    您接受的哈希答案将是这里的标准方法。但我想知道使用数组是否会快一点。 (我也改用$_,因为我认为它使代码更简洁。)

    use feature qw(say);
    
    ...
    
    my @second_field_value;
    
    while (<$filehandle>))
    {
        chomp; 
        my @fields = split /,/;
    
        if ($second_field_value[$fields[1]]) {
            say "DIP line: $. -- @fields";
        }
        ++$second_field_value[$fields[1]];
    }
    

    这将是一个非常稀疏的数组,但它可能仍然比散列版本更快。 (恐怕我没有时间对其进行基准测试。)

    更新:我进行了一些基本测试。数组版本更快。但还不够值得担心。

    【讨论】:

      猜你喜欢
      • 2017-05-11
      • 2012-05-20
      • 2017-12-18
      • 2018-11-25
      • 2019-02-11
      • 1970-01-01
      • 2020-07-01
      • 2015-10-07
      • 1970-01-01
      相关资源
      最近更新 更多