什么是计算二进制文件中设置位数的最快方法

时间:2015-01-20 13:42:34

标签: perl

我想知道在Perl中计算二进制文件中设置位数(1' s)的最快方法是什么 我需要快速,因为我正在阅读10个文件,每个文件大约有5千万位。

我现在这样做的方式太慢了,运行10-15个文件需要几个小时。

这就是我现在的工作方式(我知道它的效率很慢,但过去文件要小得多,而且这种方法还不错):

#count number of 1's in binary vector
sub get_DET_fault_count {
    my $bin_vec = shift;

    my $tmp_vec = generate_tmp_path("bin_vec");
    io($tmp_vec)->println( unpack( "B*", $bin_vec ) );
    my $fault_count = `grep -o -E '1' $tmp_vec | wc -l`;

    chomp $fault_count;
    `rm $tmp_vec`;
    return $fault_count;
}

2 个答案:

答案 0 :(得分:8)

我可以想到两种方式: 1)使用unpack,因为你已经在做,但不要浪费周期做任何IO。 2)使用具有预先计算值的查找表,以查找给定字节中的位数

1)这里的技巧是解包的'%'指令告诉unpack在结果中执行校验和,在二进制数据的情况下对所有的0和1进行求和

use strict;
use warnings;

my $filename = $ARGV[0];

open(my $fh, '<', $filename) or die "$!";
binmode $fh;


my $count = 0;
my $word  = 0;

while ( read $fh, $word, 4 ) {
   $count += unpack '%32B*', $word;
}

print "filename contains $count set bits\n";
__END__
7733485

2)0 - 255中的值只有一定数量的设置位,永远不会更改,因此您可以预先计算一个数组以保存所有这些位。你浪费了一点内存 - 大约4k或8k,具体取决于构建 - 以防止除查找之外的任何计算。

use strict;
use warnings;

my $filename = $ARGV[0];

open(my $fh, '<', $filename) or die "$!";
binmode $fh;

my @bitcounts = (
   0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3, 2, 3, 
   3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 
   3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 1, 2, 
   2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 
   3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 
   5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 1, 2, 2, 3, 
   2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 
   4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 
   3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 2, 3, 3, 4, 3, 4, 
   4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 
   5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 
   5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8
);

my $count = 0;
my $byte  = 0;
while ( read $fh, $byte, 1 ) {
   $count += $bitcounts[ord($byte)];
}

print "filename contains $count set bits\n";
__END__
7733485

对于我处理的JPEG I样本,这两种方法都给了我7733485。

答案 1 :(得分:2)

你可以做一个循环,不断改变字节(使用>>运算符)并检查是否设置了最低位。

这样的事情:

do {
   $counter++ if $bin_vec & 1;
   $bin_vec >> 1;
} while ($bin_vec > 0);