Perl打了多个范围的计数器,其他语言的选项?

时间:2013-08-07 21:37:22

标签: perl

回来寻求你的建议。我编写了一个perl脚本,它将特定数字的命中数计入用户定义的bin中。例如,这是我的数据文件:

12
14
15
20
21

我想知道我在以下范围内有多少次点击:

1-19
20-29
30-39

结果就像

1-19    3
20-29   2
30-39   0

我已经通过将数据保存到散列(datahash),然后将我的范围保存到另一个散列(rangehash),然后基本上遍历datahash中的所有数据点并检查值是否在rangehash的范围。

问题是对于datahash中的每个数据点,我遍历所有的rangehash值并在找到datapoint所在的范围后退出。这对于少数数据点很有用,但现在我的文件至少有200万个数据点和50,000个范围,所以循环遍历所有这些只需要永远。

我想知道是否有人会有更好的解决方案,而不仅仅是循环整个事情。对其他语言的建议很受欢迎!!!

最佳,

Sakti

3 个答案:

答案 0 :(得分:5)

以下将是超级快,虽然它假设不会发生:

my @buckets = (0) x 4;
++$buckets[ $_ / 10 ] while <>:
print " 1-19: ".( $buckets[0] + $buckets[1] )."\n";
print "20-29: $buckets[2]\n";
print "30-39: $buckets[3]\n";

以下更通用的解决方案实际上可能更快:

use List::Util qw( sum );
++$counts[$_] while <>:
print " 1-19: ".( sum 0, @counts[ 1..19] )."\n";
print "20-29: ".( sum 0, @counts[20..29] )."\n";
print "30-39: ".( sum 0, @counts[30..39] )."\n";

答案 1 :(得分:3)

最简单的方法可能是使用散列添加数字,然后总结相应范围的散列切片。您也可以使用数组而不是散列,因为您可以使用数字作为索引。这可能会产生非常大的空哈希,这很浪费,但它简化了密钥生成,因为坏索引会发出警告。

use strict;
use warnings;
use List::Util 'sum';

my %nums;
while (<DATA>) {
    s/\D+//g;     # remove junk
    $nums{$_}++;  # count number
}
my $low = 1;
for my $high (qw(19 29 39)) {
    my $sum =  sum(0,                      # to avoid undef return value
                   grep defined,           # avoid uninitialized warnings
                   @nums{$low .. $high});  # hash slice for our range
    print "$low - $high : $sum\n";
    $low = $high + 1;                      # set new low range
}

__DATA__
12
14
15
20
21

<强>输出:

1 - 19 : 3
20 - 29 : 2
30 - 39 : 0

答案 2 :(得分:0)

这仅适用于用户定义的分档,即无法轻易计算为int($x / 100)*100等的分档。

最近在这里或perlmonks上有一个类似的问题(我很难找到),最好的答案IMO是“排序箱的上限,然后使用二分搜索”。

对于50K箱,每个数据点约为if s,这可能没问题(当然不是“永远”)。

根据数据,可以应用一些缓存以实现进一步加速。例如。一个人可以将数据舍入到预期间隔的1/1000(最后一个bin - 第一个bin),并且只检查覆盖此部分的bin。 (我刚刚做了这个,但它可能有用。或者不是。)