我有一个Perl脚本,用于比较加载到两个数组中的两组数据,并且我试图使比较更有效。目前代码如下:
foreach(@{FILE_DATA}) {
if((++$file_current_linenum % 200) == 0) {
$progress = int($file_current_linenum / $file_total_lines * 10000) / 100;
logg("Processed $file_current_linenum file rows, $progress%, $mismatches mismatches.");
}
$file_current_line = $_;
$match_found = 0;
foreach(@{DB_DATA}) {
$db_current_line = $_;
if($file_current_line->{"channel"} == $db_current_line->{"channel"} ) {
if ($file_current_line->{"checksum"} == $db_current_line->{"checksum"} &&
$file_current_line->{"time"} > ($db_current_line->{"date_part"} - $TIME_MATCH_TOLERANCE) &&
$file_current_line->{"time"} < ($db_current_line->{"date_part"} + $TIME_MATCH_TOLERANCE) ){
$match_found = 1;
last; # break;
}
}
}
if($match_found != 1) {
push(@results, $file_current_line);
$mismatches++;
}
}
我的第一个想法是从两个数组中删除匹配以减小池大小,这会影响迭代器的位置吗?
这两组数据最多可包含数万个元素,比较可能需要几个小时才能完成。
答案 0 :(得分:3)
您的解决方案是O(DB * FILE)。
以下是O(DB + FILE)当且仅当从来没有多行具有相同的通道和校验和时:
my %DB_DATA;
for my $db_line (@DB_DATA) {
push @{ $DB_DATA{ $db_line->{channel} }{ $db_line->{checksum} } }, $db_line;
}
for my $file_line_idx (0..$#FILE_DATA) {
my $file_line = $FILE_DATA[$file_line_idx];
my $found = 0;
if (my $r1 = $DB_DATA{ $file_line->{channel} } ) {
if (my $r2 = $r1->{ $file_line->{checksum} } ) {
my $file_time = $file_line->{time};
for my $db_line (@$r2) {
my $db_time = $db_line->{date_part};
if (abs($file_time - $db_time) < $TIME_MATCH_TOLERANCE) {
$found = 1;
last;
}
}
}
}
push @mismatches, $file_line if !$found;
if ((($file_line_idx+1) % 200) == 0) {
logg(sprintf("Processed %d file rows, %d%, %d mismatches.",
$file_line_idx+1,
int(($file_line_idx+1)/@FILE_DATA) * 100,
0+@mismatches,
));
}
}
以下是O(DB + FILE),即使有很多行具有相同的通道和校验和,但如果$TIME_MATCH_TOLERANCE
很大则使用大量内存:
my %DB_DATA;
for my $db_line (@DB_DATA) {
for my $db_time (
$db_line->{date_part} - $TIME_MATCH_TOLERANCE + 1
..
$db_line->{date_part} + $TIME_MATCH_TOLERANCE - 1
) {
++$DB_DATA{ $db_line->{channel} }{ $db_line->{checksum} }{$db_time};
}
}
for my $file_line_idx (0..$#FILE_DATA) {
my $file_line = $FILE_DATA[$file_line_idx];
my $found = 0;
if (my $r1 = $DB_DATA{ $file_line->{channel} } ) {
if (my $r2 = $r1->{ $file_line->{checksum} } ) {
if ($r2->{ $file_line->{time} } {
$found = 1;
last;
}
}
}
push @mismatches, $file_line if !$found;
if ((($file_line_idx+1) % 200) == 0) {
logg(sprintf("Processed %d file rows, %d%, %d mismatches.",
$file_line_idx+1,
int(($file_line_idx+1)/@FILE_DATA) * 100,
0+@mismatches,
));
}
}
注意:假设时间戳是整数。如果它们不是,请在将它们用作键之前将它们转换为整数。
以下是O((DB + FILE)log DB)[非常接近O(DB + FILE)],即使有很多行具有相同的通道和校验和,并且使用最小的内存:
sub binsearch(&\@) {
my ($compare, $array) = @_;
my $i = 0;
my $j = $#$array;
return 0 if $j == -1;
while (1) {
my $k = int(($i+$j)/2);
for ($array->[$k]) {
my $cmp = $compare->()
or return 1;
if ($cmp < 0) {
$j = $k-1;
return 0 if $i > $j;
} else {
$i = $k+1;
return 0 if $i > $j;
}
}
}
}
my %DB_DATA;
for my $db_line (@DB_DATA) {
push @{ $DB_DATA{ $db_line->{channel} }{ $db_line->{checksum} } }, $db_line;
}
for my $r1 (values(%DB_DATA)) {
for my $r2 (values(%$r1)) {
@$r2 = sort { $a->{date_part} <=> $b->{date_part} } @$r2;
}
}
for my $file_line_idx (0..$#FILE_DATA) {
my $file_line = $FILE_DATA[$file_line_idx];
my $found = 0;
if (my $r1 = $DB_DATA{ $file_line->{channel} } ) {
if (my $r2 = $r1->{ $file_line->{checksum} } ) {
my $file_time = $file_line->{time};
my $min_db_time = $file_time - $TIME_MATCH_TOLERANCE;
my $max_db_time = $file_time + $TIME_MATCH_TOLERANCE;
if ( binsearch {
$_->{date_part} >= $max_db_time ? -1
: $_->{date_part} <= $min_db_time ? +1
: 0
} @$r2 ) {
$found = 1;
last;
}
}
}
push @mismatches, $file_line if !$found;
if ((($file_line_idx+1) % 200) == 0) {
logg(sprintf("Processed %d file rows, %d%, %d mismatches.",
$file_line_idx+1,
int(($file_line_idx+1)/@FILE_DATA) * 100,
0+@mismatches,
));
}
}
答案 1 :(得分:1)
你可以通过使用&#34; channel&#34;的串联从DB_DATA预先构建哈希来显着减少时间。和&#34;校验和&#34;值作为键,每个值是具有该通道和校验和的所有DB_DATA条目的列表。这样,对于每个FILE_DATA条目,您只需要检查该列表。
如果有很多条目具有给定的通道和校验和,您可以尝试通过按date_part对它们进行排序,然后尝试二进制搜索以查找有效条目来进一步提高。
如果具有给定通道和校验和的条目非常少,则应将运行时间减少一百万左右,因为它将运行时间从O($#FILE_DATA * $#DB_DATA)减少到O ($#FILE_DATA + $#DB_DATA)。