我的文件中有 1e8 个数字,大小从 1 到 999 不等。我需要通读每个文件并保存一份报告,说明在每个文件中找到了每个数字的数量。我认为设置一个全为零的常量数组,然后使用我刚刚读取的数字作为索引递增会给我答案。执行此操作的 Perl 语法不是我所期望的。并非所有数字都一定会出现。也许散列是一种方法,但数组中可能只有几个洞。有什么帮助吗?谢谢。
3 回答
执行此操作的 Perl 语法不是我所期望的。
展示你的作品
并非所有数字都一定会出现。
跳过零(简单的 if 块,请参阅http://perldoc.perl.org/perlintro.html)
也许散列是一种方法,但数组中可能只有几个洞。
是的,哈希是很自然的,请参阅 perlfaq4
,搜索“count”、“uniq”和“duplicate”
Tom Duff(Duff's Device 的创造者):“如果你的代码太慢,你必须让它更快。如果没有更好的算法可用,你必须修剪周期。 ”
我不同意这是最适合散列的情况。当然,哈希是惯用的,它是 perlfaq4 中提到的方法,它不会浪费计数器容器中的元素。但他说的是 1 到 999 之间的 100_000_000 个整数。计数器容器中使用的元素数量是微不足道的。获得计数所需的迭代次数非常重要。100,000,000 次迭代需要很多时间。
如果我们改用数组,我们会丢弃索引为零的元素。如果所有的整数都是相同的值,我们会再扔掉 998 个元素。有这么大的事吗?另一方面,即使索引到数组和索引到散列聚合都需要 O(1) 操作,但 Big-O 表示法只说明了部分情况。其中“n”是整数总数(100,000,000),数组方法和哈希方法都是 O(n) 操作。因此,归结为哪种方法计算效率更高。尽管数组查找和哈希查找都是 O(1),但事实证明哈希查找需要更多的周期来执行。
迭代超过 100,000,000 个整数和递增计数器需要时间。但事实证明,在散列中这样做比在数组中花费更多的时间。我知道从“常用成语”的角度来看这是亵渎神明。但这可能是一种非常特殊的情况,其中计算效率比惯用代码更重要,并且比使用数组作为计数器的内存占用量稍大一点更重要。
这是一些代码,演示了我在说什么:
use strict;
use warnings;
use Benchmark qw/ cmpthese /;
use List::Util qw/ max min /;
use Test::More;
use Readonly;
Readonly my $datasize => 100_000_000;
Readonly my $test_size => 100_000;
my @array_results = keys count_in_array( $test_size );
my @hash_results = keys count_in_hash( $test_size );
is( max( @array_results ), 999, "max( keys count_in_array() ) in range." );
is( min( @array_results ), 1, "min( keys count_in_array() ) in range." );
is( max( @hash_results ), 999, "max( keys count_in_hash() ) in range." );
is( min( @hash_results ), 1, "min( keys count_in_hash() ) in range." );
done_testing();
cmpthese( 5, {
array => sub{ my $results = count_in_array() },
hash => sub{ my $results = count_in_hash() },
} );
sub count_in_array {
my @container;
for( 1 .. $datasize ) {
$container[ int( rand( 999 ) ) + 1 ]++;
}
return {
map{
$container[$_] > 0
? ( $_ => $container[$_] )
: ()
} 1 .. $#container
};
}
sub count_in_hash {
my %container;
for( 1 .. $datasize ) {
$container{ int( rand ( 999 ) ) + 1 }++;
}
return \%container;
}
以下是该基准的结果:
ok 1 - max( keys count_in_array() ) in range.
ok 2 - min( keys count_in_array() ) in range.
ok 3 - max( keys count_in_hash() ) in range.
ok 4 - min( keys count_in_hash() ) in range.
1..4
s/iter hash array
hash 24.9 -- -42%
array 14.5 72% --
这是数组方法的一大胜利(它快了 72%)。如果这仍然太慢,请使用 Inline::C 修剪循环,以使用整数数组重写数组方法。这将更快一个数量级。
这可能是需要优化的关键 3%。
那么,我们应该做些什么来减轻脱离公认习语的影响呢?我们确保记录我们正在做的事情,以便未来的读者(包括未来的我们自己)了解正在做的事情,以及为什么它以我们不熟悉的方式完成。
只是我的 .02。
根据系统排序实用程序的质量,在命令行:
sort giant-file.txt | uniq -c > giant_file_counts.txt