回来寻求你的建议。我编写了一个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
答案 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。 (我刚刚做了这个,但它可能有用。或者不是。)